2020-05-05 13:29:47 -04:00
package db
import (
"context"
2020-11-05 16:54:03 -05:00
stderrors "errors"
2020-05-15 08:38:36 -04:00
"fmt"
2020-06-28 16:55:57 -04:00
"io"
2020-08-19 16:47:18 -04:00
"strconv"
2020-05-31 19:55:56 -04:00
"strings"
2020-05-05 13:29:47 -04:00
"testing"
2020-05-31 19:55:56 -04:00
"time"
2020-05-05 13:29:47 -04:00
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/db/db_test"
"github.com/hashicorp/boundary/internal/db/timestamp"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/oplog/store"
2020-06-09 21:22:15 -04:00
wrapping "github.com/hashicorp/go-kms-wrapping"
2021-07-14 17:31:58 -07:00
"github.com/hashicorp/go-secure-stdlib/base62"
2020-05-05 13:29:47 -04:00
"github.com/hashicorp/go-uuid"
2020-05-31 19:55:56 -04:00
"github.com/jinzhu/gorm"
2020-05-05 13:29:47 -04:00
"github.com/stretchr/testify/assert"
2020-06-09 21:22:15 -04:00
"github.com/stretchr/testify/require"
2020-06-29 08:18:57 -04:00
"google.golang.org/protobuf/proto"
2021-05-14 12:23:12 -05:00
"google.golang.org/protobuf/types/known/timestamppb"
2020-05-05 13:29:47 -04:00
)
2020-08-06 12:50:20 -07:00
func TestDb_UpdateUnsetField ( t * testing . T ) {
db , _ := TestSetup ( t , "postgres" )
rw := & Db {
underlying : db ,
}
tu := & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
PublicId : testId ( t ),
Name : "default" ,
2021-01-07 10:02:32 -05:00
},
}
2020-08-06 12:50:20 -07:00
require . NoError ( t , rw . Create ( context . Background (), tu ))
updatedTu := tu . Clone ().( * db_test . TestUser )
updatedTu . Name = "updated"
updatedTu . Email = "ignore"
cnt , err := rw . Update ( context . Background (), updatedTu , [] string { "Name" }, nil )
require . NoError ( t , err )
assert . Equal ( t , 1 , cnt )
assert . Equal ( t , "ignore" , updatedTu . Email )
assert . Equal ( t , "updated" , updatedTu . Name )
}
2020-05-11 16:54:05 -04:00
func TestDb_Update ( t * testing . T ) {
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2021-05-14 12:23:12 -05:00
now := & timestamp . Timestamp { Timestamp : timestamppb . Now ()}
2020-05-31 19:55:56 -04:00
publicId , err := NewPublicId ( "testuser" )
2020-06-29 08:18:57 -04:00
require . NoError ( t , err )
2020-05-31 19:55:56 -04:00
id := testId ( t )
2020-07-15 13:12:15 -04:00
badVersion := uint32 ( 22 )
versionOne := uint32 ( 1 )
versionZero := uint32 ( 0 )
2020-05-31 19:55:56 -04:00
type args struct {
i * db_test . TestUser
fieldMaskPaths [] string
setToNullPaths [] string
opt [] Option
}
tests := [] struct {
name string
args args
want int
wantErr bool
wantErrMsg string
wantName string
wantEmail string
wantPhoneNumber string
2020-06-24 12:33:49 -04:00
wantVersion int
2020-05-31 19:55:56 -04:00
}{
{
name : "simple" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "simple-updated" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
},
want : 1 ,
wantErr : false ,
wantErrMsg : "" ,
wantName : "simple-updated" + id ,
wantEmail : "" ,
wantPhoneNumber : "updated" + id ,
},
2020-06-24 12:33:49 -04:00
{
name : "simple-with-bad-version" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "simple-with-bad-version" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
2020-07-15 13:12:15 -04:00
opt : [] Option { WithVersion ( & badVersion )},
2020-06-24 12:33:49 -04:00
},
want : 0 ,
wantErr : false ,
wantErrMsg : "" ,
},
2020-07-15 13:12:15 -04:00
{
name : "simple-with-zero-version" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "simple-with-bad-version" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
opt : [] Option { WithVersion ( & versionZero )},
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrMsg : "db.Update: with version option is zero: parameter violation: error #100" ,
2020-07-15 13:12:15 -04:00
},
2020-06-24 12:33:49 -04:00
{
name : "simple-with-version" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "simple-with-version" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
2020-07-15 13:12:15 -04:00
opt : [] Option { WithVersion ( & versionOne )},
2020-06-24 12:33:49 -04:00
},
want : 1 ,
wantErr : false ,
wantErrMsg : "" ,
wantName : "simple-with-version" + id ,
wantEmail : "" ,
wantPhoneNumber : "updated" + id ,
wantVersion : 2 ,
},
2020-08-10 15:17:15 -04:00
{
name : "simple-with-where" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "simple-with-where" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
opt : [] Option { WithWhere ( "email = ? and phone_number = ?" , id , id )},
},
want : 1 ,
wantErr : false ,
wantErrMsg : "" ,
wantName : "simple-with-where" + id ,
wantEmail : "" ,
wantPhoneNumber : "updated" + id ,
wantVersion : 2 ,
},
{
name : "simple-with-where-and-version" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "simple-with-where-and-version" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
opt : [] Option { WithWhere ( "email = ? and phone_number = ?" , id , id ), WithVersion ( & versionOne )},
},
want : 1 ,
wantErr : false ,
wantErrMsg : "" ,
wantName : "simple-with-where-and-version" + id ,
wantEmail : "" ,
wantPhoneNumber : "updated" + id ,
wantVersion : 2 ,
},
{
name : "bad-with-where" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "bad-with-where" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
opt : [] Option { WithWhere ( "foo = ? and phone_number = ?" , id , id )},
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrMsg : `db.Update: column "foo" does not exist: integrity violation: error #1102` ,
2020-08-10 15:17:15 -04:00
},
2020-05-31 19:55:56 -04:00
{
name : "multiple-null" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "multiple-null-updated" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
},
},
fieldMaskPaths : [] string { "Name" },
setToNullPaths : [] string { "Email" , "PhoneNumber" },
},
want : 1 ,
wantErr : false ,
wantErrMsg : "" ,
wantName : "multiple-null-updated" + id ,
wantEmail : "" ,
wantPhoneNumber : "" ,
},
{
name : "non-updatable" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "non-updatable" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
PublicId : publicId ,
CreateTime : now ,
UpdateTime : now ,
},
},
fieldMaskPaths : [] string { "Name" , "PhoneNumber" , "CreateTime" , "UpdateTime" , "PublicId" },
setToNullPaths : [] string { "Email" },
},
want : 1 ,
wantErr : false ,
wantErrMsg : "" ,
wantName : "non-updatable" + id ,
wantEmail : "" ,
wantPhoneNumber : "updated" + id ,
},
2020-06-03 09:51:34 -04:00
{
name : "primary-key" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "primary-key" + id ,
Email : "updated" + id ,
PhoneNumber : "updated" + id ,
PublicId : publicId ,
CreateTime : now ,
UpdateTime : now ,
},
},
fieldMaskPaths : [] string { "Id" },
setToNullPaths : [] string { "Email" },
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrMsg : "db.Update: not allowed on primary key field Id: parameter violation: error #103" ,
2020-06-03 09:51:34 -04:00
},
2020-05-31 19:55:56 -04:00
{
name : "both are missing" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "both are missing-updated" + id ,
Email : id ,
PhoneNumber : id ,
},
},
fieldMaskPaths : nil ,
setToNullPaths : [] string {},
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrMsg : "db.Update: both fieldMaskPaths and setToNullPaths are missing: parameter violation: error #100" ,
2020-05-31 19:55:56 -04:00
},
{
name : "i is nil" ,
args : args {
i : nil ,
fieldMaskPaths : [] string { "Name" , "PhoneNumber" },
setToNullPaths : [] string { "Email" },
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrMsg : "db.Update: missing interface: parameter violation: error #100" ,
2020-05-31 19:55:56 -04:00
},
{
name : "only read-only" ,
args : args {
i : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
Name : "only read-only" + id ,
Email : id ,
PhoneNumber : id ,
},
},
fieldMaskPaths : [] string { "CreateTime" },
setToNullPaths : [] string { "UpdateTime" },
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrMsg : "db.Update: after filtering non-updated fields, there are no fields left in fieldMaskPaths or setToNullPaths: parameter violation: error #100" ,
2020-05-31 19:55:56 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-31 19:55:56 -04:00
rw := & Db {
underlying : db ,
}
u := testUser ( t , db , tt . name + id , id , id )
if tt . args . i != nil {
tt . args . i . Id = u . Id
tt . args . i . PublicId = u . PublicId
}
rowsUpdated , err := rw . Update ( context . Background (), tt . args . i , tt . args . fieldMaskPaths , tt . args . setToNullPaths , tt . args . opt ... )
if tt . wantErr {
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-31 19:55:56 -04:00
assert . Equal ( tt . want , rowsUpdated )
2020-11-05 16:54:03 -05:00
assert . Contains ( err . Error (), tt . wantErrMsg )
2020-05-31 19:55:56 -04:00
return
}
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
assert . Equal ( tt . want , rowsUpdated )
2020-06-24 12:33:49 -04:00
if tt . want == 0 {
return
}
2020-05-31 19:55:56 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
foundUser . PublicId = tt . args . i . PublicId
where := "public_id = ?"
for _ , f := range tt . args . setToNullPaths {
switch {
case strings . EqualFold ( f , "phonenumber" ):
f = "phone_number"
}
where = fmt . Sprintf ( "%s and %s is null" , where , f )
}
err = rw . LookupWhere ( context . Background (), foundUser , where , tt . args . i . PublicId )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
assert . Equal ( tt . args . i . Id , foundUser . Id )
assert . Equal ( tt . wantName , foundUser . Name )
assert . Equal ( tt . wantEmail , foundUser . Email )
assert . Equal ( tt . wantPhoneNumber , foundUser . PhoneNumber )
assert . NotEqual ( now , foundUser . CreateTime )
assert . NotEqual ( now , foundUser . UpdateTime )
assert . NotEqual ( publicId , foundUser . PublicId )
2020-06-24 12:33:49 -04:00
assert . Equal ( u . Version + 1 , foundUser . Version )
2020-05-31 19:55:56 -04:00
})
}
2020-06-24 12:33:49 -04:00
t . Run ( "no-version-field" , func ( t * testing . T ) {
assert := assert . New ( t )
w := Db { underlying : db }
id , err := uuid . GenerateUUID ()
assert . NoError ( err )
car := testCar ( t , db , "foo-" + id , id , int32 ( 100 ))
car . Name = "friendly-" + id
2020-07-15 13:12:15 -04:00
versionOne := uint32 ( 1 )
rowsUpdated , err := w . Update ( context . Background (), car , [] string { "Name" }, nil , WithVersion ( & versionOne ))
2020-06-24 12:33:49 -04:00
assert . Error ( err )
assert . Equal ( 0 , rowsUpdated )
})
2020-05-31 19:55:56 -04:00
t . Run ( "valid-WithOplog" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
user := testUser ( t , db , "foo-" + id , id , id )
2020-05-05 13:29:47 -04:00
2020-05-05 16:35:24 -04:00
user . Name = "friendly-" + id
2020-05-31 19:55:56 -04:00
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil ,
// write oplogs for this update
WithOplog (
TestWrapper ( t ),
oplog . Metadata {
"key-only" : nil ,
"deployment" : [] string { "amex" },
"project" : [] string { "central-info-systems" , "local-info-systems" },
"resource-public-id" : [] string { user . PublicId },
"op-type" : [] string { oplog . OpType_OP_TYPE_UPDATE . String ()},
}),
)
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-12 11:57:28 -04:00
assert . Equal ( 1 , rowsUpdated )
2020-05-05 13:29:47 -04:00
2020-05-21 09:24:37 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-21 09:24:37 -04:00
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-21 09:24:37 -04:00
assert . Equal ( foundUser . Name , user . Name )
2020-05-31 19:55:56 -04:00
err = TestVerifyOplog ( t , & w , user . PublicId , WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), WithCreateNotBefore ( 10 * time . Second ))
2020-06-29 08:18:57 -04:00
require . NoError ( err )
})
t . Run ( "both-WithOplog-NewOplogMsg" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := Db { underlying : db }
id , err := uuid . GenerateUUID ()
require . NoError ( err )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
)
require . NoError ( err )
updateMsg := oplog . Message {}
user . Name = "friendly-" + id
rowsUpdated , err := w . Update (
context . Background (),
user ,
[] string { "Name" }, nil ,
NewOplogMsg ( & updateMsg ),
WithOplog ( TestWrapper ( t ), oplog . Metadata { "alice" : [] string { "bob" }}),
)
require . Error ( err )
assert . Equal ( 0 , rowsUpdated )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . InvalidParameter ), err ))
2020-06-29 08:18:57 -04:00
})
t . Run ( "valid-NewOplogMsg" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := Db { underlying : db }
ticket , err := w . GetTicket ( & db_test . TestUser {})
require . NoError ( err )
id , err := uuid . GenerateUUID ()
require . NoError ( err )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
)
require . NoError ( err )
updateMsg := oplog . Message {}
user . Name = "friendly-" + id
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil , NewOplogMsg ( & updateMsg ))
require . NoError ( err )
assert . Equal ( 1 , rowsUpdated )
foundUser , err := db_test . NewTestUser ()
require . NoError ( err )
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
require . NoError ( err )
assert . Equal ( foundUser . Name , user . Name )
metadata := oplog . Metadata {
"resource-public-id" : [] string { user . PublicId },
// "op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()},
}
err = w . WriteOplogEntryWith ( context . Background (), TestWrapper ( t ), ticket , metadata , [] * oplog . Message { & createMsg , & updateMsg })
require . NoError ( err )
err = TestVerifyOplog ( t , & w , user . PublicId , WithOperation ( oplog . OpType_OP_TYPE_UNSPECIFIED ), WithCreateNotBefore ( 10 * time . Second ))
2020-05-31 19:55:56 -04:00
assert . NoError ( err )
2020-05-21 09:24:37 -04:00
})
2020-05-31 19:55:56 -04:00
t . Run ( "vet-for-write" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-05-18 12:57:45 -04:00
assert . NoError ( err )
2020-05-31 19:55:56 -04:00
user := & testUserWithVet {
PublicId : id ,
Name : id ,
PhoneNumber : id ,
Email : id ,
}
err = db . Create ( user ). Error
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
2020-05-05 16:35:24 -04:00
user . Name = "friendly-" + id
2020-05-31 19:55:56 -04:00
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-12 11:57:28 -04:00
assert . Equal ( 1 , rowsUpdated )
2020-05-05 13:29:47 -04:00
2020-05-31 19:55:56 -04:00
foundUser := & testUserWithVet { PublicId : user . PublicId }
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
foundUser . PublicId = user . PublicId
2020-05-05 13:29:47 -04:00
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 16:35:24 -04:00
assert . Equal ( foundUser . Name , user . Name )
2020-05-05 13:29:47 -04:00
2020-05-31 19:55:56 -04:00
user . PublicId = "not-allowed-by-vet-for-write"
rowsUpdated , err = w . Update ( context . Background (), user , [] string { "PublicId" }, nil )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-31 19:55:56 -04:00
assert . Equal ( 0 , rowsUpdated )
2020-05-05 13:29:47 -04:00
})
t . Run ( "nil-tx" , func ( t * testing . T ) {
2020-06-10 06:56:46 -04:00
assert := assert . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : nil }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-05-18 12:57:45 -04:00
assert . NoError ( err )
2020-05-31 19:55:56 -04:00
user := testUser ( t , db , "foo-" + id , id , id )
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil )
2020-05-18 12:57:45 -04:00
assert . Error ( err )
2020-05-12 11:57:28 -04:00
assert . Equal ( 0 , rowsUpdated )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.Update: missing underlying db: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
t . Run ( "no-wrapper-WithOplog" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
user := testUser ( t , db , "foo-" + id , id , id )
2020-05-05 13:29:47 -04:00
2020-05-05 16:35:24 -04:00
user . Name = "friendly-" + id
2020-05-12 11:57:28 -04:00
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" },
2020-05-31 19:55:56 -04:00
nil ,
2020-05-05 13:29:47 -04:00
WithOplog (
nil ,
oplog . Metadata {
"key-only" : nil ,
"deployment" : [] string { "amex" },
"project" : [] string { "central-info-systems" , "local-info-systems" },
}),
)
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-12 11:57:28 -04:00
assert . Equal ( 0 , rowsUpdated )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.Update: oplog validation failed: db.validateOplogArgs: missing wrapper: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
t . Run ( "no-metadata-WithOplog" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
user := testUser ( t , db , "foo-" + id , id , id )
2020-05-05 16:35:24 -04:00
user . Name = "friendly-" + id
2020-05-31 19:55:56 -04:00
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil ,
2020-05-05 13:29:47 -04:00
WithOplog (
TestWrapper ( t ),
nil ,
),
)
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-12 11:57:28 -04:00
assert . Equal ( 0 , rowsUpdated )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.Update: oplog validation failed: db.validateOplogArgs: missing metadata: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
}
2020-05-31 19:55:56 -04:00
// testUserWithVet gives us a model that implements VetForWrite() without any
// cyclic dependencies.
type testUserWithVet struct {
Id uint32 `gorm:"primary_key"`
PublicId string
Name string `gorm:"default:null"`
PhoneNumber string `gorm:"default:null"`
Email string `gorm:"default:null"`
}
func ( u * testUserWithVet ) GetPublicId () string {
return u . PublicId
}
2021-01-07 10:02:32 -05:00
2020-05-31 19:55:56 -04:00
func ( u * testUserWithVet ) TableName () string {
return "db_test_user"
}
2021-01-07 10:02:32 -05:00
2020-05-31 19:55:56 -04:00
func ( u * testUserWithVet ) VetForWrite ( ctx context . Context , r Reader , opType OpType , opt ... Option ) error {
if u . PublicId == "" {
2020-11-05 16:54:03 -05:00
return stderrors . New ( "public id is empty string for user write" )
2020-05-31 19:55:56 -04:00
}
if opType == UpdateOp {
dbOptions := GetOpts ( opt ... )
for _ , path := range dbOptions . WithFieldMaskPaths {
switch path {
case "PublicId" :
2020-11-05 16:54:03 -05:00
return stderrors . New ( "you cannot change the public id" )
2020-05-31 19:55:56 -04:00
}
}
}
if opType == CreateOp {
if u . Id != 0 {
2020-11-05 16:54:03 -05:00
return stderrors . New ( "id is a db sequence" )
2020-05-31 19:55:56 -04:00
}
}
return nil
}
2020-05-11 16:54:05 -04:00
func TestDb_Create ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
// intentionally not run with t.Parallel so we don't need to use DoTx for the Create tests
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-05-05 13:29:47 -04:00
t . Run ( "simple" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2021-05-14 12:23:12 -05:00
ts := & timestamp . Timestamp { Timestamp : timestamppb . Now ()}
2020-05-21 09:24:37 -04:00
user . CreateTime = ts
user . UpdateTime = ts
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create ( context . Background (), user )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-18 12:57:45 -04:00
assert . NotEmpty ( user . Id )
2020-05-21 09:24:37 -04:00
// make sure the database controlled the timestamp values
assert . NotEqual ( ts , user . GetCreateTime ())
assert . NotEqual ( ts , user . GetUpdateTime ())
2020-05-05 13:29:47 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( foundUser . Id , user . Id )
})
t . Run ( "valid-WithOplog" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create (
context . Background (),
user ,
WithOplog (
TestWrapper ( t ),
oplog . Metadata {
"key-only" : nil ,
"deployment" : [] string { "amex" },
"project" : [] string { "central-info-systems" , "local-info-systems" },
},
),
)
2020-06-29 08:18:57 -04:00
require . NoError ( err )
require . NotEmpty ( user . Id )
2020-05-05 13:29:47 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( foundUser . Id , user . Id )
})
2020-06-29 08:18:57 -04:00
t . Run ( "both-Oplog-NewOplogMsg" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := Db { underlying : db }
id , err := uuid . GenerateUUID ()
require . NoError ( err )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
WithOplog ( TestWrapper ( t ), oplog . Metadata { "alice" : [] string { "bob" }}),
)
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . InvalidParameter ), err ))
2020-06-29 08:18:57 -04:00
})
t . Run ( "valid-NewOplogMsg" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := Db { underlying : db }
ticket , err := w . GetTicket ( & db_test . TestUser {})
require . NoError ( err )
id , err := uuid . GenerateUUID ()
require . NoError ( err )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
)
require . NoError ( err )
updateMsg := oplog . Message {}
user . Name = "friendly-" + id
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil , NewOplogMsg ( & updateMsg ))
require . NoError ( err )
assert . Equal ( 1 , rowsUpdated )
foundUser , err := db_test . NewTestUser ()
require . NoError ( err )
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
require . NoError ( err )
assert . Equal ( foundUser . Name , user . Name )
metadata := oplog . Metadata {
"resource-public-id" : [] string { user . PublicId },
// "op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()},
}
err = w . WriteOplogEntryWith ( context . Background (), TestWrapper ( t ), ticket , metadata , [] * oplog . Message { & createMsg , & updateMsg })
require . NoError ( err )
err = TestVerifyOplog ( t , & w , user . PublicId , WithOperation ( oplog . OpType_OP_TYPE_UNSPECIFIED ), WithCreateNotBefore ( 10 * time . Second ))
require . NoError ( err )
})
2020-05-05 13:29:47 -04:00
t . Run ( "no-wrapper-WithOplog" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create (
context . Background (),
user ,
WithOplog (
nil ,
oplog . Metadata {
"key-only" : nil ,
"deployment" : [] string { "amex" },
"project" : [] string { "central-info-systems" , "local-info-systems" },
},
),
)
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.Create: oplog validation failed: db.validateOplogArgs: missing wrapper: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
t . Run ( "no-metadata-WithOplog" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create (
context . Background (),
user ,
WithOplog (
TestWrapper ( t ),
nil ,
),
)
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.Create: oplog validation failed: db.validateOplogArgs: missing metadata: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
t . Run ( "nil-tx" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : nil }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create ( context . Background (), user )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.Create: missing underlying db: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
}
2020-05-11 16:54:05 -04:00
func TestDb_LookupByPublicId ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
t . Parallel ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-05-05 13:29:47 -04:00
t . Run ( "simple" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create ( context . Background (), user )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-18 12:57:45 -04:00
assert . NotEmpty ( user . PublicId )
2020-05-05 13:29:47 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( foundUser . Id , user . Id )
})
t . Run ( "tx-nil," , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 16:54:05 -04:00
w := Db {}
2020-05-05 13:29:47 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.LookupById: missing underlying db: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
t . Run ( "no-public-id-set" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
foundUser , err := db_test . NewTestUser ()
foundUser . PublicId = ""
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . Contains ( err . Error (), "db.LookupById: db.primaryKeyWhere: missing primary key: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
t . Run ( "not-found" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
foundUser , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
foundUser . PublicId = id
err = w . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
2020-05-05 13:29:47 -04:00
})
}
2020-05-11 16:54:05 -04:00
func TestDb_LookupWhere ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
t . Parallel ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-05-05 13:29:47 -04:00
t . Run ( "simple" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
user . Name = "foo-" + id
err = w . Create ( context . Background (), user )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-18 12:57:45 -04:00
assert . NotEmpty ( user . PublicId )
2020-05-05 13:29:47 -04:00
var foundUser db_test . TestUser
err = w . LookupWhere ( context . Background (), & foundUser , "public_id = ?" , user . PublicId )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( foundUser . Id , user . Id )
})
t . Run ( "tx-nil," , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 16:54:05 -04:00
w := Db {}
2020-05-05 13:29:47 -04:00
var foundUser db_test . TestUser
err := w . LookupWhere ( context . Background (), & foundUser , "public_id = ?" , 1 )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . Equal ( "db.LookupWhere: missing underlying db: parameter violation: error #100" , err . Error ())
2020-05-05 13:29:47 -04:00
})
t . Run ( "not-found" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
var foundUser db_test . TestUser
err = w . LookupWhere ( context . Background (), & foundUser , "public_id = ?" , id )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
2020-05-05 13:29:47 -04:00
})
t . Run ( "bad-where" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
require := require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
var foundUser db_test . TestUser
err = w . LookupWhere ( context . Background (), & foundUser , "? = ?" , id )
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-05 13:29:47 -04:00
})
}
2020-05-11 16:54:05 -04:00
func TestDb_SearchWhere ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
t . Parallel ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-08-19 16:47:18 -04:00
knownUser := testUser ( t , db , "zedUser" , "" , "" )
2020-06-01 17:32:48 -04:00
2020-06-16 18:59:30 -04:00
type args struct {
where string
arg [] interface {}
opt [] Option
}
tests := [] struct {
2020-08-19 16:47:18 -04:00
name string
db Db
createCnt int
args args
wantCnt int
wantErr bool
wantNameOrder bool
2020-06-16 18:59:30 -04:00
}{
{
name : "no-limit" ,
db : Db { underlying : db },
createCnt : 10 ,
args : args {
where : "1=1" ,
2020-08-19 16:47:18 -04:00
opt : [] Option { WithLimit ( - 1 ), WithOrder ( "name asc" )},
2020-06-16 18:59:30 -04:00
},
2020-08-19 16:47:18 -04:00
wantCnt : 11 , // there's an additional knownUser
wantErr : false ,
wantNameOrder : true ,
2020-06-16 18:59:30 -04:00
},
2021-06-20 16:16:51 -07:00
{
name : "no-where" ,
db : Db { underlying : db },
createCnt : 10 ,
args : args {
opt : [] Option { WithLimit ( 10 )},
},
wantCnt : 10 ,
wantErr : false ,
},
2020-06-16 18:59:30 -04:00
{
name : "custom-limit" ,
db : Db { underlying : db },
createCnt : 10 ,
args : args {
where : "1=1" ,
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 3 ,
wantErr : false ,
},
{
name : "simple" ,
db : Db { underlying : db },
createCnt : 1 ,
args : args {
where : "public_id = ?" ,
arg : [] interface {}{ knownUser . PublicId },
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 1 ,
wantErr : false ,
},
2021-06-20 16:16:51 -07:00
{
name : "no args" ,
db : Db { underlying : db },
createCnt : 1 ,
args : args {
where : fmt . Sprintf ( "public_id = '%v'" , knownUser . PublicId ),
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 1 ,
wantErr : false ,
},
{
name : "no where, but with args" ,
db : Db { underlying : db },
createCnt : 1 ,
args : args {
arg : [] interface {}{ knownUser . PublicId },
opt : [] Option { WithLimit ( 3 )},
},
wantErr : true ,
},
2020-06-16 18:59:30 -04:00
{
name : "not-found" ,
db : Db { underlying : db },
createCnt : 1 ,
args : args {
where : "public_id = ?" ,
arg : [] interface {}{ "bad-id" },
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 0 ,
wantErr : false ,
},
{
name : "bad-where" ,
db : Db { underlying : db },
createCnt : 1 ,
args : args {
where : "bad_column_name = ?" ,
arg : [] interface {}{ knownUser . PublicId },
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 0 ,
wantErr : true ,
},
{
name : "nil-underlying" ,
db : Db {},
createCnt : 1 ,
args : args {
where : "public_id = ?" ,
arg : [] interface {}{ knownUser . PublicId },
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 0 ,
wantErr : true ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
testUsers := [] * db_test . TestUser {}
for i := 0 ; i < tt . createCnt ; i ++ {
2020-08-19 16:47:18 -04:00
testUsers = append ( testUsers , testUser ( t , db , tt . name + strconv . Itoa ( i ), "" , "" ))
2020-06-16 18:59:30 -04:00
}
assert . Equal ( tt . createCnt , len ( testUsers ))
2020-05-05 13:29:47 -04:00
2020-06-16 18:59:30 -04:00
var foundUsers [] db_test . TestUser
err := tt . db . SearchWhere ( context . Background (), & foundUsers , tt . args . where , tt . args . arg , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tt . wantCnt , len ( foundUsers ))
2020-08-19 16:47:18 -04:00
if tt . wantNameOrder {
assert . Equal ( tt . name + strconv . Itoa ( 0 ), foundUsers [ 0 ]. Name )
for i , u := range foundUsers {
if u . Name != "zedUser" {
assert . Equal ( tt . name + strconv . Itoa ( i ), u . Name )
}
}
}
2020-06-16 18:59:30 -04:00
})
}
2020-05-05 13:29:47 -04:00
}
2020-09-16 12:02:02 -04:00
func TestDb_Exec ( t * testing . T ) {
t . Parallel ()
t . Run ( "update" , func ( t * testing . T ) {
db , _ := TestSetup ( t , "postgres" )
require := require . New ( t )
w := Db { underlying : db }
id := testId ( t )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
err = w . Create ( context . Background (), user )
require . NoError ( err )
require . NotEmpty ( user . Id )
2020-09-30 18:11:59 -04:00
rowsAffected , err := w . Exec ( context . Background (), "update db_test_user set name = ? where public_id = ?" , [] interface {}{ "updated-" + id , user . PublicId })
2020-09-16 12:02:02 -04:00
require . NoError ( err )
require . Equal ( 1 , rowsAffected )
})
}
2021-01-07 10:02:32 -05:00
2020-05-11 16:54:05 -04:00
func TestDb_DoTx ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
t . Parallel ()
2021-07-29 18:52:56 -06:00
ctx := context . TODO ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-05-05 13:29:47 -04:00
t . Run ( "valid-with-10-retries" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-05-05 13:29:47 -04:00
attempts := 0
got , err := w . DoTx ( context . Background (), 10 , ExpBackoff {},
2020-06-22 13:27:26 -07:00
func ( Reader , Writer ) error {
2020-05-05 13:29:47 -04:00
attempts += 1
if attempts < 9 {
2021-07-29 18:52:56 -06:00
return errors . E ( ctx , errors . WithCode ( errors . TicketAlreadyRedeemed ))
2020-05-05 13:29:47 -04:00
}
return nil
})
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( 8 , got . Retries )
assert . Equal ( 9 , attempts ) // attempted 1 + 8 retries
})
t . Run ( "valid-with-1-retries" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-05-05 13:29:47 -04:00
attempts := 0
got , err := w . DoTx ( context . Background (), 1 , ExpBackoff {},
2020-06-22 13:27:26 -07:00
func ( Reader , Writer ) error {
2020-05-05 13:29:47 -04:00
attempts += 1
if attempts < 2 {
2021-07-29 18:52:56 -06:00
return errors . E ( ctx , errors . WithCode ( errors . TicketAlreadyRedeemed ))
2020-05-05 13:29:47 -04:00
}
return nil
})
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( 1 , got . Retries )
assert . Equal ( 2 , attempts ) // attempted 1 + 8 retries
})
t . Run ( "valid-with-2-retries" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-05-05 13:29:47 -04:00
attempts := 0
got , err := w . DoTx ( context . Background (), 3 , ExpBackoff {},
2020-06-22 13:27:26 -07:00
func ( Reader , Writer ) error {
2020-05-05 13:29:47 -04:00
attempts += 1
if attempts < 3 {
2021-07-29 18:52:56 -06:00
return errors . E ( ctx , errors . WithCode ( errors . TicketAlreadyRedeemed ))
2020-05-05 13:29:47 -04:00
}
return nil
})
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( 2 , got . Retries )
assert . Equal ( 3 , attempts ) // attempted 1 + 8 retries
})
t . Run ( "valid-with-4-retries" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-05-05 13:29:47 -04:00
attempts := 0
got , err := w . DoTx ( context . Background (), 4 , ExpBackoff {},
2020-06-22 13:27:26 -07:00
func ( Reader , Writer ) error {
2020-05-05 13:29:47 -04:00
attempts += 1
if attempts < 4 {
2021-07-29 18:52:56 -06:00
return errors . E ( ctx , errors . WithCode ( errors . TicketAlreadyRedeemed ))
2020-05-05 13:29:47 -04:00
}
return nil
})
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( 3 , got . Retries )
assert . Equal ( 4 , attempts ) // attempted 1 + 8 retries
})
t . Run ( "zero-retries" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-05-05 13:29:47 -04:00
attempts := 0
2020-06-22 13:27:26 -07:00
got , err := w . DoTx ( context . Background (), 0 , ExpBackoff {}, func ( Reader , Writer ) error { attempts += 1 ; return nil })
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( RetryInfo {}, got )
assert . Equal ( 1 , attempts )
})
t . Run ( "nil-tx" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 16:54:05 -04:00
w := & Db { nil }
2020-05-05 13:29:47 -04:00
attempts := 0
2020-06-22 13:27:26 -07:00
got , err := w . DoTx ( context . Background (), 1 , ExpBackoff {}, func ( Reader , Writer ) error { attempts += 1 ; return nil })
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( RetryInfo {}, got )
2020-12-21 10:07:34 -08:00
assert . Equal ( "db.DoTx: missing underlying db: parameter violation: error #100" , err . Error ())
2020-05-05 13:29:47 -04:00
})
t . Run ( "not-a-retry-err" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-11-05 16:54:03 -05:00
got , err := w . DoTx ( context . Background (), 1 , ExpBackoff {}, func ( Reader , Writer ) error { return stderrors . New ( "not a retry error" ) })
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( RetryInfo {}, got )
2021-01-13 15:50:07 -08:00
assert . False ( errors . Match ( errors . T ( errors . TicketAlreadyRedeemed ), err ))
2020-05-05 13:29:47 -04:00
})
t . Run ( "too-many-retries" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := & Db { underlying : db }
2020-05-05 13:29:47 -04:00
attempts := 0
2021-01-13 15:50:07 -08:00
got , err := w . DoTx ( context . Background (), 2 , ExpBackoff {}, func ( Reader , Writer ) error {
attempts += 1
2021-07-29 18:52:56 -06:00
return errors . E ( ctx , errors . WithCode ( errors . TicketAlreadyRedeemed ))
2021-01-13 15:50:07 -08:00
})
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( 3 , got . Retries )
2020-12-21 10:07:34 -08:00
assert . Equal ( "db.DoTx: Too many retries: 3 of 3: db transaction issue: error #1103" , err . Error ())
2020-05-05 13:29:47 -04:00
})
2020-05-15 08:38:36 -04:00
t . Run ( "updating-good-bad-good" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-06-22 13:27:26 -07:00
rw := Db { underlying : db }
2020-05-15 08:38:36 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-15 08:38:36 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-15 08:38:36 -04:00
user . Name = "foo-" + id
2020-06-22 13:27:26 -07:00
err = rw . Create ( context . Background (), user )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
require . NotZero ( user . Id )
2020-05-15 08:38:36 -04:00
2020-06-22 13:27:26 -07:00
_ , err = rw . DoTx ( context . Background (), 10 , ExpBackoff {}, func ( r Reader , w Writer ) error {
2020-05-15 08:38:36 -04:00
user . Name = "friendly-" + id
2020-05-31 19:55:56 -04:00
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil )
2020-05-15 08:38:36 -04:00
if err != nil {
return err
}
if rowsUpdated != 1 {
return fmt . Errorf ( "error in number of rows updated %d" , rowsUpdated )
}
return nil
})
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-15 08:38:36 -04:00
foundUser , err := db_test . NewTestUser ()
assert . NoError ( err )
foundUser . PublicId = user . PublicId
2020-06-22 13:27:26 -07:00
err = rw . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-15 08:38:36 -04:00
assert . Equal ( foundUser . Name , user . Name )
user2 , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-06-22 13:27:26 -07:00
_ , err = rw . DoTx ( context . Background (), 10 , ExpBackoff {}, func ( _ Reader , w Writer ) error {
2020-05-15 08:38:36 -04:00
user2 . Name = "friendly2-" + id
2020-05-31 19:55:56 -04:00
rowsUpdated , err := w . Update ( context . Background (), user2 , [] string { "Name" }, nil )
2020-05-15 08:38:36 -04:00
if err != nil {
return err
}
if rowsUpdated != 1 {
return fmt . Errorf ( "error in number of rows updated %d" , rowsUpdated )
}
return nil
})
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-06-22 13:27:26 -07:00
err = rw . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-15 08:38:36 -04:00
assert . NotEqual ( foundUser . Name , user2 . Name )
2020-06-22 13:27:26 -07:00
_ , err = rw . DoTx ( context . Background (), 10 , ExpBackoff {}, func ( r Reader , w Writer ) error {
2020-05-15 08:38:36 -04:00
user . Name = "friendly2-" + id
2020-05-31 19:55:56 -04:00
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil )
2020-05-15 08:38:36 -04:00
if err != nil {
return err
}
if rowsUpdated != 1 {
return fmt . Errorf ( "error in number of rows updated %d" , rowsUpdated )
}
return nil
})
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-06-22 13:27:26 -07:00
err = rw . LookupByPublicId ( context . Background (), foundUser )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-15 08:38:36 -04:00
assert . Equal ( foundUser . Name , user . Name )
})
2020-05-05 13:29:47 -04:00
}
2020-05-11 16:54:05 -04:00
func TestDb_Delete ( t * testing . T ) {
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-06-09 21:22:15 -04:00
newUser := func () * db_test . TestUser {
w := & Db {
underlying : db ,
}
u , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( t , err )
2020-06-09 21:22:15 -04:00
err = w . Create ( context . Background (), u )
2020-06-29 08:18:57 -04:00
require . NoError ( t , err )
2020-06-09 21:22:15 -04:00
return u
}
notFoundUser := func () * db_test . TestUser {
u , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( t , err )
2020-06-09 21:22:15 -04:00
u . Id = 1111111
return u
}
2020-05-05 13:29:47 -04:00
2020-06-09 21:22:15 -04:00
newMetadata := func ( publicId string ) oplog . Metadata {
return oplog . Metadata {
"op-type" : [] string { oplog . OpType_OP_TYPE_DELETE . String ()},
"resource-public-id" : [] string { publicId },
}
}
type args struct {
i * db_test . TestUser
opt [] Option
metadata func ( publicId string ) oplog . Metadata
}
tests := [] struct {
name string
underlying * gorm . DB
wrapper wrapping . Wrapper
args args
want int
wantOplog bool
wantErr bool
2020-12-21 10:07:34 -08:00
wantErrIs errors . Code
2020-06-09 21:22:15 -04:00
}{
{
name : "simple-no-oplog" ,
underlying : db ,
wrapper : TestWrapper ( t ),
args : args {
i : newUser (),
},
want : 1 ,
wantErr : false ,
},
{
name : "valid-with-oplog" ,
underlying : db ,
wrapper : TestWrapper ( t ),
args : args {
i : newUser (),
metadata : newMetadata ,
},
wantOplog : true ,
want : 1 ,
wantErr : false ,
},
{
name : "nil-wrapper" ,
underlying : db ,
wrapper : nil ,
args : args {
i : newUser (),
metadata : newMetadata ,
},
wantOplog : true ,
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-09 21:22:15 -04:00
},
{
name : "nil-metadata" ,
underlying : db ,
wrapper : nil ,
args : args {
i : newUser (),
metadata : func ( string ) oplog . Metadata { return nil },
},
wantOplog : true ,
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-09 21:22:15 -04:00
},
{
name : "nil-underlying" ,
underlying : nil ,
wrapper : TestWrapper ( t ),
args : args {
i : newUser (),
},
want : 0 ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-09 21:22:15 -04:00
},
{
name : "not-found" ,
underlying : db ,
wrapper : TestWrapper ( t ),
args : args {
i : notFoundUser (),
},
want : 0 ,
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
require := require . New ( t )
assert := assert . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
if tt . wantOplog {
metadata := tt . args . metadata ( tt . args . i . PublicId )
opLog := WithOplog ( tt . wrapper , metadata )
tt . args . opt = append ( tt . args . opt , opLog )
}
got , err := rw . Delete ( context . Background (), tt . args . i , tt . args . opt ... )
assert . Equal ( tt . want , got )
if tt . wantErr {
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrIs ), err ), "received unexpected error: %v" , err )
2020-06-09 21:22:15 -04:00
err := TestVerifyOplog ( t , rw , tt . args . i . GetPublicId (), WithOperation ( oplog . OpType_OP_TYPE_DELETE ), WithCreateNotBefore ( 5 * time . Second ))
assert . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tt . want , got )
2020-05-05 13:29:47 -04:00
2020-06-29 08:18:57 -04:00
foundUser := tt . args . i . Clone ().( * db_test . TestUser )
2020-06-09 21:22:15 -04:00
foundUser . PublicId = tt . args . i . PublicId
err = rw . LookupByPublicId ( context . Background (), foundUser )
assert . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
2020-05-05 13:29:47 -04:00
2020-06-09 21:22:15 -04:00
err = TestVerifyOplog ( t , rw , tt . args . i . GetPublicId (), WithOperation ( oplog . OpType_OP_TYPE_DELETE ), WithCreateNotBefore ( 5 * time . Second ))
switch {
case tt . wantOplog :
assert . NoError ( err )
default :
assert . Error ( err )
}
})
2020-06-29 08:18:57 -04:00
t . Run ( "both-WithOplog-NewOplogMsg" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := Db { underlying : db }
id , err := uuid . GenerateUUID ()
require . NoError ( err )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
)
require . NoError ( err )
deleteMsg := oplog . Message {}
rowsDeleted , err := w . Delete ( context . Background (), user , NewOplogMsg ( & deleteMsg ), WithOplog ( TestWrapper ( t ), oplog . Metadata { "alice" : [] string { "bob" }}))
require . Error ( err )
assert . Equal ( 0 , rowsDeleted )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . InvalidParameter ), err ))
2020-06-29 08:18:57 -04:00
})
t . Run ( "valid-NewOplogMsg" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := Db { underlying : db }
ticket , err := w . GetTicket ( & db_test . TestUser {})
require . NoError ( err )
id , err := uuid . GenerateUUID ()
require . NoError ( err )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
)
require . NoError ( err )
deleteMsg := oplog . Message {}
rowsDeleted , err := w . Delete ( context . Background (), user , NewOplogMsg ( & deleteMsg ))
require . NoError ( err )
assert . Equal ( 1 , rowsDeleted )
foundUser , err := db_test . NewTestUser ()
require . NoError ( err )
foundUser . PublicId = user . PublicId
err = w . LookupByPublicId ( context . Background (), foundUser )
require . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
2020-06-29 08:18:57 -04:00
metadata := oplog . Metadata {
"resource-public-id" : [] string { user . PublicId },
}
err = w . WriteOplogEntryWith ( context . Background (), TestWrapper ( t ), ticket , metadata , [] * oplog . Message { & createMsg , & deleteMsg })
require . NoError ( err )
err = TestVerifyOplog ( t , & w , user . PublicId , WithOperation ( oplog . OpType_OP_TYPE_UNSPECIFIED ), WithCreateNotBefore ( 10 * time . Second ))
require . NoError ( err )
})
2020-06-09 21:22:15 -04:00
}
2020-05-05 13:29:47 -04:00
}
2020-05-11 16:54:05 -04:00
func TestDb_ScanRows ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
t . Parallel ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-05-05 13:29:47 -04:00
t . Run ( "valid" , func ( t * testing . T ) {
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-05-11 21:05:11 -04:00
w := Db { underlying : db }
2020-05-05 13:29:47 -04:00
user , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
err = w . Create ( context . Background (), user )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-18 12:57:45 -04:00
assert . NotEmpty ( user . Id )
2020-05-05 16:35:24 -04:00
where := "select * from db_test_user where name in ($1, $2)"
2020-09-30 18:11:59 -04:00
rows , err := w . Query ( context . Background (), where , [] interface {}{ "alice" , "bob" })
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-18 12:57:45 -04:00
defer func () { err := rows . Close (); assert . NoError ( err ) }()
2020-05-05 13:29:47 -04:00
for rows . Next () {
u , err := db_test . NewTestUser ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
// scan the row into your Gorm struct
err = w . ScanRows ( rows , & u )
2020-09-25 13:35:32 -04:00
require . NoError ( err )
assert . Equal ( user . PublicId , u . PublicId )
}
})
}
func TestDb_Query ( t * testing . T ) {
t . Parallel ()
db , _ := TestSetup ( t , "postgres" )
t . Run ( "valid" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := Db { underlying : db }
user , err := db_test . NewTestUser ()
user . Name = "alice"
require . NoError ( err )
err = rw . Create ( context . Background (), user )
require . NoError ( err )
assert . NotEmpty ( user . Id )
assert . Equal ( "alice" , user . Name )
where := "select * from db_test_user where name in ($1, $2)"
2020-09-30 18:11:59 -04:00
rows , err := rw . Query ( context . Background (), where , [] interface {}{ "alice" , "bob" })
2020-09-25 13:35:32 -04:00
require . NoError ( err )
defer func () { err := rows . Close (); assert . NoError ( err ) }()
for rows . Next () {
u , err := db_test . NewTestUser ()
require . NoError ( err )
// scan the row into your Gorm struct
err = rw . ScanRows ( rows , & u )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-05 13:29:47 -04:00
assert . Equal ( user . PublicId , u . PublicId )
}
})
}
2020-05-31 19:55:56 -04:00
2020-06-29 06:31:23 -04:00
func TestDb_CreateItems ( t * testing . T ) {
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-06-29 06:31:23 -04:00
testOplogResourceId := testId ( t )
createFn := func () [] interface {} {
results := [] interface {}{}
for i := 0 ; i < 10 ; i ++ {
u , err := db_test . NewTestUser ()
require . NoError ( t , err )
results = append ( results , u )
}
return results
}
createMixedFn := func () [] interface {} {
u , err := db_test . NewTestUser ()
require . NoError ( t , err )
c , err := db_test . NewTestCar ()
require . NoError ( t , err )
return [] interface {}{
u ,
c ,
}
}
2020-07-01 05:40:02 -04:00
returnedMsgs := [] * oplog . Message {}
2020-06-29 06:31:23 -04:00
type args struct {
createItems [] interface {}
opt [] Option
}
tests := [] struct {
2020-07-01 05:40:02 -04:00
name string
underlying * gorm . DB
args args
wantOplogId string
wantOplogMsgs bool
wantErr bool
2020-12-21 10:07:34 -08:00
wantErrIs errors . Code
2020-06-29 06:31:23 -04:00
}{
{
name : "simple" ,
underlying : db ,
args : args {
createItems : createFn (),
},
wantErr : false ,
},
{
name : "withOplog" ,
underlying : db ,
args : args {
createItems : createFn (),
opt : [] Option {
WithOplog (
TestWrapper ( t ),
oplog . Metadata {
"resource-public-id" : [] string { testOplogResourceId },
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
},
),
},
},
wantOplogId : testOplogResourceId ,
wantErr : false ,
},
2020-07-01 05:40:02 -04:00
{
name : "NewOplogMsgs" ,
underlying : db ,
args : args {
createItems : createFn (),
opt : [] Option {
NewOplogMsgs ( & returnedMsgs ),
},
},
wantOplogMsgs : true ,
wantErr : false ,
},
{
name : "withOplog and NewOplogMsgs" ,
underlying : db ,
args : args {
createItems : createFn (),
opt : [] Option {
NewOplogMsgs ( & [] * oplog . Message {}),
WithOplog (
TestWrapper ( t ),
oplog . Metadata {
"resource-public-id" : [] string { testOplogResourceId },
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
},
),
},
},
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-07-01 05:40:02 -04:00
wantErr : true ,
},
2020-06-29 06:31:23 -04:00
{
name : "mixed items" ,
underlying : db ,
args : args {
createItems : createMixedFn (),
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "bad oplog opt: nil metadata" ,
underlying : nil ,
args : args {
createItems : createFn (),
opt : [] Option {
WithOplog (
TestWrapper ( t ),
nil ,
),
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "bad oplog opt: nil wrapper" ,
underlying : nil ,
args : args {
createItems : createFn (),
opt : [] Option {
WithOplog (
nil ,
oplog . Metadata {
"resource-public-id" : [] string { "doesn't matter since wrapper is nil" },
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
},
),
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "bad opt: WithLookup" ,
underlying : nil ,
args : args {
createItems : createFn (),
opt : [] Option { WithLookup ( true )},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "nil underlying" ,
underlying : nil ,
args : args {
createItems : createFn (),
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "empty items" ,
underlying : db ,
args : args {
createItems : [] interface {}{},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "nil items" ,
underlying : db ,
args : args {
createItems : nil ,
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
err := rw . CreateItems ( context . Background (), tt . args . createItems , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrIs ), err ), "unexpected error: %s" , err . Error ())
2020-06-29 06:31:23 -04:00
return
}
require . NoError ( err )
for _ , item := range tt . args . createItems {
u := db_test . AllocTestUser ()
u . PublicId = item .( * db_test . TestUser ). PublicId
err := rw . LookupByPublicId ( context . Background (), & u )
assert . NoError ( err )
2020-06-29 19:28:15 -04:00
if _ , ok := item .( * db_test . TestUser ); ok {
assert . Truef ( proto . Equal ( item .( * db_test . TestUser ). StoreTestUser , u . StoreTestUser ), "%s and %s should be equal" , item , u )
}
2020-06-29 06:31:23 -04:00
}
if tt . wantOplogId != "" {
err = TestVerifyOplog ( t , rw , tt . wantOplogId , WithOperation ( oplog . OpType_OP_TYPE_CREATE ), WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
}
2020-07-01 05:40:02 -04:00
if tt . wantOplogMsgs {
assert . Equal ( len ( tt . args . createItems ), len ( returnedMsgs ))
for _ , m := range returnedMsgs {
assert . Equal ( m . OpType , oplog . OpType_OP_TYPE_CREATE )
}
}
2020-06-29 06:31:23 -04:00
})
}
}
func TestDb_DeleteItems ( t * testing . T ) {
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-06-29 06:31:23 -04:00
testOplogResourceId := testId ( t )
createFn := func () [] interface {} {
results := [] interface {}{}
for i := 0 ; i < 10 ; i ++ {
u := testUser ( t , db , "" , "" , "" )
results = append ( results , u )
}
return results
}
2020-07-01 05:40:02 -04:00
returnedMsgs := [] * oplog . Message {}
2020-06-29 06:31:23 -04:00
type args struct {
deleteItems [] interface {}
opt [] Option
}
tests := [] struct {
name string
underlying * gorm . DB
args args
wantRowsDeleted int
wantOplogId string
2020-07-01 05:40:02 -04:00
wantOplogMsgs bool
2020-06-29 06:31:23 -04:00
wantErr bool
2020-12-21 10:07:34 -08:00
wantErrIs errors . Code
2020-06-29 06:31:23 -04:00
}{
{
name : "simple" ,
underlying : db ,
args : args {
deleteItems : createFn (),
},
wantRowsDeleted : 10 ,
wantErr : false ,
},
2020-07-01 05:40:02 -04:00
{
name : "NewOplogMsgs" ,
underlying : db ,
args : args {
deleteItems : createFn (),
opt : [] Option {
NewOplogMsgs ( & returnedMsgs ),
},
},
wantRowsDeleted : 10 ,
wantErr : false ,
},
{
name : "withOplog and NewOplogMsgs" ,
underlying : db ,
args : args {
deleteItems : createFn (),
opt : [] Option {
NewOplogMsgs ( & [] * oplog . Message {}),
WithOplog (
TestWrapper ( t ),
oplog . Metadata {
"resource-public-id" : [] string { testOplogResourceId },
"op-type" : [] string { oplog . OpType_OP_TYPE_DELETE . String ()},
},
),
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-07-01 05:40:02 -04:00
},
2020-06-29 06:31:23 -04:00
{
name : "withOplog" ,
underlying : db ,
args : args {
deleteItems : createFn (),
opt : [] Option {
WithOplog (
TestWrapper ( t ),
oplog . Metadata {
"resource-public-id" : [] string { testOplogResourceId },
"op-type" : [] string { oplog . OpType_OP_TYPE_DELETE . String ()},
},
),
},
},
wantRowsDeleted : 10 ,
wantOplogId : testOplogResourceId ,
wantErr : false ,
},
{
name : "bad oplog opt: nil metadata" ,
underlying : nil ,
args : args {
deleteItems : createFn (),
opt : [] Option {
WithOplog (
TestWrapper ( t ),
nil ,
),
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "bad oplog opt: nil wrapper" ,
underlying : nil ,
args : args {
deleteItems : createFn (),
opt : [] Option {
WithOplog (
nil ,
oplog . Metadata {
"resource-public-id" : [] string { "doesn't matter since wrapper is nil" },
"op-type" : [] string { oplog . OpType_OP_TYPE_CREATE . String ()},
},
),
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "bad opt: WithLookup" ,
underlying : nil ,
args : args {
deleteItems : createFn (),
opt : [] Option { WithLookup ( true )},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "nil underlying" ,
underlying : nil ,
args : args {
deleteItems : createFn (),
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "empty items" ,
underlying : db ,
args : args {
deleteItems : [] interface {}{},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
{
name : "nil items" ,
underlying : db ,
args : args {
deleteItems : nil ,
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 06:31:23 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
rowsDeleted , err := rw . DeleteItems ( context . Background (), tt . args . deleteItems , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrIs ), err ), "unexpected error: %s" , err . Error ())
2020-06-29 06:31:23 -04:00
return
}
require . NoError ( err )
assert . Equal ( tt . wantRowsDeleted , rowsDeleted )
for _ , item := range tt . args . deleteItems {
u := db_test . AllocTestUser ()
u . PublicId = item .( * db_test . TestUser ). PublicId
err := rw . LookupByPublicId ( context . Background (), & u )
require . Error ( err )
2020-12-21 10:07:34 -08:00
require . Truef ( errors . Match ( errors . T ( errors . RecordNotFound ), err ), "found item %s that should be deleted" , u . PublicId )
2020-06-29 06:31:23 -04:00
}
if tt . wantOplogId != "" {
err = TestVerifyOplog ( t , rw , tt . wantOplogId , WithOperation ( oplog . OpType_OP_TYPE_DELETE ), WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
}
2020-07-01 05:40:02 -04:00
if tt . wantOplogMsgs {
assert . Equal ( len ( tt . args . deleteItems ), len ( returnedMsgs ))
for _ , m := range returnedMsgs {
assert . Equal ( m . OpType , oplog . OpType_OP_TYPE_DELETE )
}
}
2020-06-29 06:31:23 -04:00
})
}
}
2020-05-31 19:55:56 -04:00
func testUser ( t * testing . T , conn * gorm . DB , name , email , phoneNumber string ) * db_test . TestUser {
t . Helper ()
2020-06-29 08:18:57 -04:00
require := require . New ( t )
2020-05-31 19:55:56 -04:00
publicId , err := base62 . Random ( 20 )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
u := & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
PublicId : publicId ,
Name : name ,
Email : email ,
PhoneNumber : phoneNumber ,
},
}
if conn != nil {
err = conn . Create ( u ). Error
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
}
return u
}
2021-01-07 10:02:32 -05:00
2020-06-24 12:33:49 -04:00
func testCar ( t * testing . T , conn * gorm . DB , name , model string , mpg int32 ) * db_test . TestCar {
t . Helper ()
require := require . New ( t )
publicId , err := base62 . Random ( 20 )
require . NoError ( err )
c := & db_test . TestCar {
StoreTestCar : & db_test . StoreTestCar {
PublicId : publicId ,
Name : name ,
Model : model ,
Mpg : mpg ,
},
}
if conn != nil {
err = conn . Create ( c ). Error
require . NoError ( err )
}
return c
}
2020-05-31 19:55:56 -04:00
func testId ( t * testing . T ) string {
t . Helper ()
2020-06-29 08:18:57 -04:00
require := require . New ( t )
2020-05-31 19:55:56 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2020-05-31 19:55:56 -04:00
return id
}
2020-06-28 16:55:57 -04:00
2020-06-29 08:18:57 -04:00
func testScooter ( t * testing . T , conn * gorm . DB , model string , mpg int32 ) * db_test . TestScooter {
t . Helper ()
require := require . New ( t )
privateId , err := base62 . Random ( 20 )
require . NoError ( err )
u := & db_test . TestScooter {
StoreTestScooter : & db_test . StoreTestScooter {
PrivateId : privateId ,
Model : model ,
Mpg : mpg ,
},
}
if conn != nil {
err = conn . Create ( u ). Error
require . NoError ( err )
}
return u
}
func TestDb_LookupById ( t * testing . T ) {
t . Parallel ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-06-29 08:18:57 -04:00
scooter := testScooter ( t , db , "" , 0 )
user := testUser ( t , db , "" , "" , "" )
type args struct {
resourceWithIder interface {}
opt [] Option
}
tests := [] struct {
name string
underlying * gorm . DB
args args
wantErr bool
want proto . Message
2020-12-21 10:07:34 -08:00
wantIsErr errors . Code
2020-06-29 08:18:57 -04:00
}{
{
name : "simple-private-id" ,
underlying : db ,
args : args {
resourceWithIder : scooter ,
},
wantErr : false ,
want : scooter ,
},
{
name : "simple-public-id" ,
underlying : db ,
args : args {
resourceWithIder : user ,
},
wantErr : false ,
want : user ,
},
{
name : "missing-public-id" ,
underlying : db ,
args : args {
resourceWithIder : & db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {},
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "missing-private-id" ,
underlying : db ,
args : args {
resourceWithIder : & db_test . TestScooter {
StoreTestScooter : & db_test . StoreTestScooter {},
},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "not-an-ider" ,
underlying : db ,
args : args {
resourceWithIder : & db_test . NotIder {},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "missing-underlying-db" ,
underlying : nil ,
args : args {
resourceWithIder : user ,
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
cloner , ok := tt . args . resourceWithIder .( db_test . Cloner )
require . True ( ok )
cp := cloner . Clone ()
err := rw . LookupById ( context . Background (), cp , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
2020-12-21 10:07:34 -08:00
require . True ( errors . Match ( errors . T ( tt . wantIsErr ), err ))
2020-06-29 08:18:57 -04:00
return
}
require . NoError ( err )
assert . True ( proto . Equal ( tt . want , cp .( proto . Message )))
})
}
t . Run ( "not-ptr" , func ( t * testing . T ) {
u := testUser ( t , db , "" , "" , "" )
rw := & Db {
underlying : db ,
}
err := rw . LookupById ( context . Background (), * u )
require . Error ( t , err )
2020-12-21 10:07:34 -08:00
assert . True ( t , errors . Match ( errors . T ( errors . InvalidParameter ), err ))
2020-06-29 08:18:57 -04:00
})
}
func TestDb_GetTicket ( t * testing . T ) {
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-06-29 08:18:57 -04:00
type notReplayable struct {}
tests := [] struct {
name string
underlying * gorm . DB
aggregateType interface {}
wantErr bool
2020-12-21 10:07:34 -08:00
wantErrIs errors . Code
2020-06-29 08:18:57 -04:00
}{
{
name : "simple" ,
underlying : db ,
aggregateType : & db_test . TestUser {},
wantErr : false ,
},
{
name : "not-replayable" ,
underlying : db ,
aggregateType : & notReplayable {},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "nil-aggregate-type" ,
underlying : db ,
aggregateType : nil ,
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "no-underlying" ,
underlying : nil ,
aggregateType : & db_test . TestUser {},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
got , err := rw . GetTicket ( tt . aggregateType )
if tt . wantErr {
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrIs ), err ), "unexpected error type: %s" , err . Error ())
2020-06-29 08:18:57 -04:00
return
}
require . NoError ( err )
assert . NotEmpty ( got . Name )
assert . NotEmpty ( got . Version )
assert . NotEmpty ( got . CreateTime )
assert . NotEmpty ( got . UpdateTime )
})
}
}
func TestDb_WriteOplogEntryWith ( t * testing . T ) {
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2020-06-29 08:18:57 -04:00
w := Db { underlying : db }
ticket , err := w . GetTicket ( & db_test . TestUser {})
require . NoError ( t , err )
id , err := uuid . GenerateUUID ()
assert . NoError ( t , err )
user , err := db_test . NewTestUser ()
assert . NoError ( t , err )
user . Name = "foo-" + id
createMsg := oplog . Message {}
err = w . Create (
context . Background (),
user ,
NewOplogMsg ( & createMsg ),
)
require . NoError ( t , err )
metadata := oplog . Metadata {
"resource-public-id" : [] string { user . PublicId },
}
type args struct {
wrapper wrapping . Wrapper
ticket * store . Ticket
metadata oplog . Metadata
msgs [] * oplog . Message
opt [] Option
}
tests := [] struct {
name string
underlying * gorm . DB
args args
wantErr bool
2020-12-21 10:07:34 -08:00
wantErrIs errors . Code
2020-06-29 08:18:57 -04:00
wantErrContains string
}{
{
name : "valid" ,
underlying : db ,
args : args {
wrapper : TestWrapper ( t ),
ticket : ticket ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg },
},
wantErr : false ,
},
{
name : "valid-multiple" ,
underlying : db ,
args : args {
wrapper : TestWrapper ( t ),
ticket : ticket ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg , & createMsg },
},
wantErr : false ,
},
{
name : "missing-ticket" ,
underlying : db ,
args : args {
wrapper : TestWrapper ( t ),
ticket : nil ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg },
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "missing-db" ,
underlying : nil ,
args : args {
wrapper : TestWrapper ( t ),
ticket : ticket ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg },
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "missing-wrapper" ,
underlying : db ,
args : args {
wrapper : nil ,
ticket : ticket ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg },
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "nil-metadata" ,
underlying : db ,
args : args {
wrapper : TestWrapper ( t ),
ticket : ticket ,
metadata : nil ,
msgs : [] * oplog . Message { & createMsg },
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
{
name : "empty-metadata" ,
underlying : db ,
args : args {
wrapper : TestWrapper ( t ),
ticket : ticket ,
metadata : oplog . Metadata {},
msgs : [] * oplog . Message { & createMsg },
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantErrIs : errors . InvalidParameter ,
2020-06-29 08:18:57 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
err := rw . WriteOplogEntryWith ( context . Background (), tt . args . wrapper , tt . args . ticket , tt . args . metadata , tt . args . msgs , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantErrIs ), err ), "unexpected error %s" , err . Error ())
2020-06-29 08:18:57 -04:00
if tt . wantErrContains != "" {
assert . Contains ( err . Error (), tt . wantErrContains )
}
return
}
require . NoError ( err )
})
}
}
2020-06-28 16:55:57 -04:00
func TestClear_InputTypes ( t * testing . T ) {
type Z struct {
F string
}
var nilZ * Z
s := "test-string"
type args struct {
v interface {}
f [] string
d int
}
2021-01-07 10:02:32 -05:00
tests := [] struct {
2020-06-28 16:55:57 -04:00
name string
args args
want interface {}
2021-01-12 08:50:19 -08:00
err errors . Code
2020-06-28 16:55:57 -04:00
}{
{
name : "nil" ,
args : args {
v : nil ,
f : [] string { "field" },
d : 1 ,
},
2021-01-12 08:50:19 -08:00
err : errors . InvalidParameter ,
2020-06-28 16:55:57 -04:00
},
{
name : "string" ,
args : args {
v : "blank" ,
f : [] string { "field" },
d : 1 ,
},
2021-01-12 08:50:19 -08:00
err : errors . InvalidParameter ,
2020-06-28 16:55:57 -04:00
},
{
name : "pointer-to-nil-struct" ,
args : args {
v : nilZ ,
f : [] string { "field" },
d : 1 ,
},
2021-01-12 08:50:19 -08:00
err : errors . InvalidParameter ,
2020-06-28 16:55:57 -04:00
},
{
name : "pointer-to-string" ,
args : args {
v : & s ,
f : [] string { "field" },
d : 1 ,
},
2021-01-12 08:50:19 -08:00
err : errors . InvalidParameter ,
2020-06-28 16:55:57 -04:00
},
{
name : "not-pointer" ,
args : args {
v : Z {
F : "foo" ,
},
f : [] string { "field" },
d : 1 ,
},
2021-01-12 08:50:19 -08:00
err : errors . InvalidParameter ,
2020-06-28 16:55:57 -04:00
},
{
name : "map" ,
args : args {
v : map [ string ] int {
"A" : 31 ,
"B" : 34 ,
},
f : [] string { "field" },
d : 1 ,
},
2021-01-12 08:50:19 -08:00
err : errors . InvalidParameter ,
2020-06-28 16:55:57 -04:00
},
{
name : "pointer-to-struct" ,
args : args {
v : & Z {
F : "foo" ,
},
f : [] string { "field" },
d : 1 ,
},
want : & Z {
F : "foo" ,
},
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
input := tt . args . v
err := Clear ( input , tt . args . f , tt . args . d )
2021-01-12 08:50:19 -08:00
if tt . err != 0 {
assert . True ( errors . Match ( errors . T ( tt . err ), err ))
2020-06-28 16:55:57 -04:00
return
}
require . NoError ( err )
assert . Equal ( tt . want , input )
})
}
}
func TestClear_Structs ( t * testing . T ) {
s := "test-string"
type A struct { F string }
type B struct { F * string }
type AB struct {
A A
B * B
F string
IN io . Reader
MP map [ int ] int
SL [] string
AR [ 1 ] int
CH chan int
FN func ()
}
type AFB struct {
F A
B * B
}
type ABF struct {
A A
F * B
}
type C struct {
F string
NF string
}
type EA struct {
A
M string
NF string
}
type EAP struct {
* A
M string
NF string
}
type DEA struct {
EA
F string
}
type DEAP struct {
* EAP
F string
}
type args struct {
v interface {}
f [] string
d int
}
2021-01-07 10:02:32 -05:00
tests := [] struct {
2020-06-28 16:55:57 -04:00
name string
args args
want interface {}
}{
{
name : "blank-A" ,
args : args {
v : & A {},
f : [] string { "F" },
d : 1 ,
},
want : & A {},
},
{
name : "clear-A" ,
args : args {
v : & A { "clear" },
f : [] string { "F" },
d : 1 ,
},
want : & A {},
},
{
name : "clear-B" ,
args : args {
v : & B { & s },
f : [] string { "F" },
d : 1 ,
},
want : & B {},
},
{
name : "clear-C" ,
args : args {
v : & C { "clear" , "notclear" },
f : [] string { "F" },
d : 1 ,
},
want : & C { "" , "notclear" },
},
{
name : "shallow-clear-AB" ,
args : args {
v : & AB {
A : A { "notclear" },
B : & B { & s },
F : "clear" ,
},
f : [] string { "F" },
d : 1 ,
},
want : & AB {
A : A { "notclear" },
B : & B { & s },
F : "" ,
},
},
{
name : "deep-clear-AB" ,
args : args {
v : & AB {
A : A { "clear" },
B : & B { & s },
F : "clear" ,
},
f : [] string { "F" },
d : 2 ,
},
want : & AB {
A : A { "" },
B : & B {},
F : "" ,
},
},
{
name : "clear-AFB" ,
args : args {
v : & AFB {
F : A { "clear" },
B : & B { & s },
},
f : [] string { "F" },
d : 2 ,
},
want : & AFB {
F : A { "" },
B : & B {},
},
},
{
name : "clear-ABF" ,
args : args {
v : & ABF {
A : A { "clear" },
F : & B { & s },
},
f : [] string { "F" },
d : 2 ,
},
want : & ABF {
A : A { "" },
F : nil ,
},
},
{
name : "embedded-struct" ,
args : args {
v : & EA {
A : A { "clear" },
M : "clear" ,
NF : "notclear" ,
},
f : [] string { "F" , "M" },
d : 2 ,
},
want : & EA {
A : A { "" },
M : "" ,
NF : "notclear" ,
},
},
{
name : "embedded-struct-pointer" ,
args : args {
v : & EAP {
A : & A { "clear" },
M : "clear" ,
NF : "notclear" ,
},
f : [] string { "F" , "M" },
d : 2 ,
},
want : & EAP {
A : & A { "" },
M : "" ,
NF : "notclear" ,
},
},
{
name : "embedded-struct-pointer-extra-depth" ,
args : args {
v : & EAP {
A : & A { "clear" },
M : "clear" ,
NF : "notclear" ,
},
f : [] string { "F" , "M" },
d : 12 ,
},
want : & EAP {
A : & A { "" },
M : "" ,
NF : "notclear" ,
},
},
{
name : "deep-embedded-struct" ,
args : args {
v : & DEA {
EA : EA {
A : A { "clear" },
M : "clear" ,
NF : "notclear" ,
},
F : "clear" ,
},
f : [] string { "F" , "M" },
d : 3 ,
},
want : & DEA {
EA : EA {
A : A { "" },
M : "" ,
NF : "notclear" ,
},
F : "" ,
},
},
{
name : "deep-embedded-struct-pointer" ,
args : args {
v : & DEAP {
EAP : & EAP {
A : & A { "clear" },
M : "clear" ,
NF : "notclear" ,
},
F : "clear" ,
},
f : [] string { "F" , "M" },
d : 3 ,
},
want : & DEAP {
EAP : & EAP {
A : & A { "" },
M : "" ,
NF : "notclear" ,
},
F : "" ,
},
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
input := tt . args . v
err := Clear ( input , tt . args . f , tt . args . d )
assert . NotEmpty ( s )
require . NoError ( err )
assert . Equal ( tt . want , input )
})
}
}
func TestClear_SetFieldsToNil ( t * testing . T ) {
type P struct { F * string }
type A struct { F string }
type EA struct {
A
M string
NF string
}
type EAP struct {
* A
M string
NF string
}
type DEA struct {
EA
F string
}
type DEAP struct {
* EAP
F string
}
type args struct {
v interface {}
f [] string
}
2021-01-07 10:02:32 -05:00
tests := [] struct {
2020-06-28 16:55:57 -04:00
name string
args args
want interface {}
}{
{
name : "dont-panic" ,
args : args {
v : P {},
f : [] string { "F" },
},
want : P {},
},
{
name : "deep-embedded-struct" ,
args : args {
v : & DEA {
EA : EA {
A : A { "notclear" },
M : "clear" ,
NF : "notclear" ,
},
F : "clear" ,
},
f : [] string { "F" , "M" },
},
want : & DEA {
EA : EA {
A : A { "notclear" },
M : "" ,
NF : "notclear" ,
},
F : "" ,
},
},
{
name : "deep-embedded-struct-pointer" ,
args : args {
v : & DEAP {
EAP : & EAP {
A : & A { "notclear" },
M : "clear" ,
NF : "notclear" ,
},
F : "clear" ,
},
f : [] string { "F" , "M" },
},
want : & DEAP {
EAP : & EAP {
A : & A { "notclear" },
M : "" ,
NF : "notclear" ,
},
F : "" ,
},
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
input := tt . args . v
require . NotPanics ( func () {
setFieldsToNil ( input , tt . args . f )
})
assert . Equal ( tt . want , input )
})
}
}
2020-07-01 05:40:02 -04:00
func TestDb_oplogMsgsForItems ( t * testing . T ) {
t . Parallel ()
// underlying isn't used at this point, so it can just be nil
rw := Db { underlying : nil }
var users [] interface {}
var wantUsrMsgs [] * oplog . Message
for i := 0 ; i < 5 ; i ++ {
publicId , err := base62 . Random ( 20 )
require . NoError ( t , err )
u := & db_test . TestUser { StoreTestUser : & db_test . StoreTestUser { PublicId : publicId }}
users = append ( users , u )
wantUsrMsgs = append (
wantUsrMsgs ,
& oplog . Message {
Message : users [ i ].( proto . Message ),
TypeName : u . TableName (),
OpType : oplog . OpType_OP_TYPE_CREATE ,
},
)
}
publicId , err := base62 . Random ( 20 )
require . NoError ( t , err )
mixed := [] interface {}{
& db_test . TestUser { StoreTestUser : & db_test . StoreTestUser { PublicId : publicId }},
& db_test . TestCar { StoreTestCar : & db_test . StoreTestCar { PublicId : publicId }},
}
type args struct {
opType OpType
opts Options
items [] interface {}
}
tests := [] struct {
name string
args args
want [] * oplog . Message
wantErr bool
2020-12-21 10:07:34 -08:00
wantIsErr errors . Code
2020-07-01 05:40:02 -04:00
}{
{
name : "valid" ,
args : args {
opType : CreateOp ,
items : users ,
},
wantErr : false ,
want : wantUsrMsgs ,
},
{
name : "nil items" ,
args : args {
opType : CreateOp ,
items : nil ,
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-07-01 05:40:02 -04:00
},
{
name : "zero items" ,
args : args {
opType : CreateOp ,
items : [] interface {}{},
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-07-01 05:40:02 -04:00
},
{
name : "mixed items" ,
args : args {
opType : CreateOp ,
items : mixed ,
},
wantErr : true ,
2020-12-21 10:07:34 -08:00
wantIsErr : errors . InvalidParameter ,
2020-07-01 05:40:02 -04:00
},
{
name : "bad op" ,
args : args {
opType : UnknownOp ,
items : users ,
},
wantErr : true ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
got , err := rw . oplogMsgsForItems ( context . Background (), tt . args . opType , tt . args . opts , tt . args . items )
if tt . wantErr {
require . Error ( err )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "unexpected error %s" , err . Error ())
2020-07-01 05:40:02 -04:00
return
}
require . NoError ( err )
assert . Equal ( tt . want , got )
})
}
}
2020-07-30 13:44:14 -04:00
func TestDb_lookupAfterWrite ( t * testing . T ) {
t . Parallel ()
db , _ := TestSetup ( t , "postgres" )
scooter := testScooter ( t , db , "" , 0 )
user := testUser ( t , db , "" , "" , "" )
type args struct {
resourceWithIder interface {}
opt [] Option
}
tests := [] struct {
name string
underlying * gorm . DB
args args
wantErr bool
want proto . Message
wantIsErr error
}{
{
name : "simple-private-id" ,
underlying : db ,
args : args {
resourceWithIder : scooter ,
opt : [] Option { WithLookup ( true )},
},
wantErr : false ,
want : scooter ,
},
{
name : "simple-public-id" ,
underlying : db ,
args : args {
resourceWithIder : user ,
opt : [] Option { WithLookup ( true )},
},
wantErr : false ,
want : user ,
},
{
name : "no-lookup-private-id" ,
underlying : db ,
args : args {
resourceWithIder : scooter ,
opt : [] Option { WithLookup ( false )},
},
wantErr : false ,
want : nil ,
},
{
name : "no-lookup-public-id" ,
underlying : db ,
args : args {
resourceWithIder : user ,
opt : [] Option { WithLookup ( false )},
},
wantErr : false ,
want : nil ,
},
{
name : "not-an-ider" ,
underlying : db ,
args : args {
resourceWithIder : & db_test . NotIder {},
opt : [] Option { WithLookup ( true )},
},
wantErr : true ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
rw := & Db {
underlying : tt . underlying ,
}
cloner , ok := tt . args . resourceWithIder .( db_test . Cloner )
require . True ( ok )
cp := cloner . Clone ()
err := rw . lookupAfterWrite ( context . Background (), cp , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
return
}
require . NoError ( err )
if tt . want != nil {
assert . True ( proto . Equal ( tt . want , cp .( proto . Message )))
}
})
}
}