2020-05-31 19:55:56 -04:00
// Copyright (c) HashiCorp, Inc.
2022-01-05 14:38:38 -05:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
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"
2022-04-08 18:04:12 -07:00
"os"
2020-08-19 16:47:18 -04:00
"strconv"
2020-05-31 19:55:56 -04:00
"strings"
2022-04-08 18:04:12 -07:00
"sync"
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
2022-12-20 07:41:31 -05:00
"github.com/DATA-DOG/go-sqlmock"
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"
2023-09-12 08:15:34 -04:00
"github.com/hashicorp/boundary/internal/event"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/oplog/store"
2022-04-08 18:04:12 -07:00
"github.com/hashicorp/go-hclog"
2022-03-16 17:04:07 -04:00
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
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"
"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 ) {
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
rw := New ( conn )
2020-08-06 12:50:20 -07:00
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 )
2021-09-30 08:47:34 -04:00
assert . Equal ( t , "" , updatedTu . Email )
2020-08-06 12:50:20 -07:00
assert . Equal ( t , "updated" , updatedTu . Name )
}
2020-05-11 16:54:05 -04:00
func TestDb_Update ( t * testing . T ) {
2022-01-05 14:38:38 -05:00
testCtx := context . Background ()
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
2021-05-14 12:23:12 -05:00
now := & timestamp . Timestamp { Timestamp : timestamppb . Now ()}
2023-06-06 17:09:08 -04:00
publicId , err := NewPublicId ( testCtx , "testuser" )
2020-06-29 08:18:57 -04:00
require . NoError ( t , err )
2020-05-31 19:55:56 -04:00
id := testId ( t )
2023-09-06 18:25:49 -04:00
oplogWrapper := TestOplogWrapper ( t , conn )
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 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "parameter violation: error #100: dbw.Update: dbw.whereClausesFromOpts: with version option is zero" ,
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 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "parameter violation: error #103: dbw.Update: not allowed on primary key field Id: invalid field mask" ,
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 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "parameter violation: error #100: dbw.Update: both fieldMaskPaths and setToNullPaths are missing" ,
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 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "parameter violation: error #100: dbw.Update: missing interface: invalid parameter" ,
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 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "parameter violation: error #100: dbw.Update: after filtering non-updated fields, there are no fields left in fieldMaskPaths or setToNullPaths" ,
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 )
2022-01-05 14:38:38 -05:00
rw := New ( conn )
u := testUser ( t , conn , tt . name + id , id , id )
2020-05-31 19:55:56 -04:00
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 )
}
2022-10-25 18:29:38 -05:00
err = rw . LookupWhere ( context . Background (), foundUser , where , [] any { 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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-24 12:33:49 -04:00
id , err := uuid . GenerateUUID ()
assert . NoError ( err )
2022-01-05 14:38:38 -05:00
car := testCar ( t , conn , "foo-" + id , id , int32 ( 100 ))
2020-06-24 12:33:49 -04:00
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2022-01-05 14:38:38 -05:00
user := testUser ( t , conn , "foo-" + id , id , id )
2020-05-05 13:29:47 -04:00
2020-05-05 16:35:24 -04:00
user . Name = "friendly-" + id
2022-11-16 17:17:28 -06:00
rowsUpdated , err := w . Update ( testCtx , user , [] string { "Name" }, nil ,
2020-05-31 19:55:56 -04:00
// write oplogs for this update
WithOplog (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-05-31 19:55:56 -04:00
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 ()},
}),
)
2022-01-05 14:38:38 -05:00
conn . Debug ( false )
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 )
2022-01-05 14:38:38 -05: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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-29 08:18:57 -04:00
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 ),
2023-09-06 18:25:49 -04:00
WithOplog ( oplogWrapper , oplog . Metadata { "alice" : [] string { "bob" }}),
2020-06-29 08:18:57 -04:00
)
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-29 08:18:57 -04:00
2022-01-05 14:38:38 -05:00
ticket , err := w . GetTicket ( testCtx , & db_test . TestUser {})
2020-06-29 08:18:57 -04:00
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()},
}
2023-09-06 18:25:49 -04:00
err = w . WriteOplogEntryWith ( context . Background (), oplogWrapper , ticket , metadata , [] * oplog . Message { & createMsg , & updateMsg })
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2022-01-05 14:38:38 -05:00
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 ,
}
2022-01-05 14:38:38 -05:00
err = w . Create ( context . Background (), user )
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
2022-01-05 14:38:38 -05:00
user := testUser ( t , conn , "foo-" + id , id , id )
2020-05-31 19:55:56 -04:00
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2022-01-05 14:38:38 -05:00
user := testUser ( t , conn , "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 )
2022-01-05 14:38:38 -05:00
assert . Contains ( err . Error (), "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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-05-05 13:29:47 -04:00
id , err := uuid . GenerateUUID ()
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2022-01-05 14:38:38 -05:00
user := testUser ( t , conn , "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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-05-05 13:29:47 -04:00
nil ,
),
)
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-12 11:57:28 -04:00
assert . Equal ( 0 , rowsUpdated )
2022-01-05 14:38:38 -05:00
assert . Contains ( err . Error (), "oplog validation failed: db.validateOplogArgs: missing metadata: parameter violation: error #100" )
2020-05-05 13:29:47 -04:00
})
2021-11-03 14:04:47 -04:00
t . Run ( "multi-column" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-01-05 14:38:38 -05:00
w := New ( conn )
scooter := testScooter ( t , conn , "" , 0 )
accessory := testAccessory ( t , conn , "test accessory" )
scooterAccessory := testScooterAccessory ( t , conn , scooter . Id , accessory . AccessoryId )
2021-11-03 14:04:47 -04:00
scooterAccessory . Review = "this is great"
rowsUpdated , err := w . Update ( context . Background (), scooterAccessory , [] string { "Review" }, nil )
require . NoError ( err )
assert . Equal ( 1 , rowsUpdated )
})
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
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
2023-09-06 18:25:49 -04:00
oplogWrapper := TestOplogWrapper ( t , conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-05-05 13:29:47 -04:00
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-29 08:18:57 -04:00
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 ),
2023-09-06 18:25:49 -04:00
WithOplog ( oplogWrapper , oplog . Metadata { "alice" : [] string { "bob" }}),
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 . InvalidParameter ), err ))
2020-06-29 08:18:57 -04:00
})
t . Run ( "valid-NewOplogMsg" , func ( t * testing . T ) {
2022-01-05 14:38:38 -05:00
testCtx := context . Background ()
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-29 08:18:57 -04:00
2022-01-05 14:38:38 -05:00
ticket , err := w . GetTicket ( testCtx , & db_test . TestUser {})
2020-06-29 08:18:57 -04:00
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 },
}
2021-10-21 11:11:56 -04:00
2023-09-06 18:25:49 -04:00
err = w . WriteOplogEntryWith ( context . Background (), oplogWrapper , ticket , metadata , [] * oplog . Message { & createMsg , & updateMsg })
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2022-01-05 14:38:38 -05:00
err = TestVerifyOplog ( t , w , user . PublicId , WithOperation ( oplog . OpType_OP_TYPE_UNSPECIFIED ), WithCreateNotBefore ( 10 * time . Second ))
2020-06-29 08:18:57 -04:00
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
assert . Contains ( err . Error (), "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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-05-05 13:29:47 -04:00
nil ,
),
)
2020-06-29 08:18:57 -04:00
require . Error ( err )
2022-01-05 14:38:38 -05:00
assert . Contains ( err . Error (), "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 ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
assert . Contains ( err . Error (), "missing primary key: invalid parameter" )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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
2022-10-25 18:29:38 -05:00
err = w . LookupWhere ( context . Background (), & foundUser , "public_id = ?" , [] any { 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
2022-10-25 18:29:38 -05:00
err := w . LookupWhere ( context . Background (), & foundUser , "public_id = ?" , [] any { 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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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
2022-10-25 18:29:38 -05:00
err = w . LookupWhere ( context . Background (), & foundUser , "public_id = ?" , [] any { 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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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
2022-10-25 18:29:38 -05:00
err = w . LookupWhere ( context . Background (), & foundUser , "? = ?" , [] any { id })
2020-06-29 08:18:57 -04:00
require . Error ( err )
2020-05-05 13:29:47 -04:00
})
}
2022-04-08 18:04:12 -07:00
// TestDb_LookupNotFoundEvent validates that not found error events are suppressed when
// LookupById and LookupWhere are called.
func TestDb_LookupNotFoundEvent ( t * testing . T ) {
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
c := event . TestEventerConfig ( t , "TestDb_LookupNotFoundEvent" )
testLock := & sync . Mutex {}
testLogger := hclog . New ( & hclog . LoggerOptions {
Mutex : testLock ,
Name : "test" ,
})
e , err := event . NewEventer ( testLogger , testLock , "TestDb_LookupNotFoundEvent" , c . EventerConfig )
require . NoError ( t , err )
info := & event . RequestInfo { Id : "867-5309" , EventId : "411" }
ctx , err := event . NewEventerContext ( context . Background (), e )
require . NoError ( t , err )
ctx , err = event . NewRequestInfoContext ( ctx , info )
require . NoError ( t , err )
t . Run ( "lookup-where" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := New ( conn )
id , err := uuid . GenerateUUID ()
require . NoError ( err )
var foundUser db_test . TestUser
2022-10-25 18:29:38 -05:00
err = w . LookupWhere ( ctx , & foundUser , "public_id = ?" , [] any { id })
2022-04-08 18:04:12 -07:00
require . Error ( err )
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
defer func () { _ = os . WriteFile ( c . AllEvents . Name (), nil , 0 o666 ) }()
2024-02-13 10:54:48 -05:00
b , err := os . ReadFile ( c . AllEvents . Name ())
2022-04-08 18:04:12 -07:00
require . NoError ( err )
// No events should have been written with the above error
assert . Len ( b , 0 )
})
t . Run ( "lookup-by-id" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
w := New ( conn )
id , err := uuid . GenerateUUID ()
require . NoError ( err )
foundUser := db_test . TestUser {
StoreTestUser : & db_test . StoreTestUser {
PublicId : id ,
},
}
err = w . LookupById ( ctx , & foundUser )
require . Error ( err )
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
defer func () { _ = os . WriteFile ( c . AllEvents . Name (), nil , 0 o666 ) }()
2024-02-13 10:54:48 -05:00
b , err := os . ReadFile ( c . AllEvents . Name ())
2022-04-08 18:04:12 -07:00
require . NoError ( err )
// No events should have been written with the above error
assert . Len ( b , 0 )
})
}
2020-05-11 16:54:05 -04:00
func TestDb_SearchWhere ( t * testing . T ) {
2020-05-05 13:29:47 -04:00
t . Parallel ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
knownUser := testUser ( t , conn , "zedUser" , "" , "" )
2020-06-01 17:32:48 -04:00
2020-06-16 18:59:30 -04:00
type args struct {
where string
2022-10-25 18:29:38 -05:00
arg [] any
2020-06-16 18:59:30 -04:00
opt [] Option
}
tests := [] struct {
2020-08-19 16:47:18 -04:00
name string
2022-01-05 14:38:38 -05:00
db * Db
2020-08-19 16:47:18 -04:00
createCnt int
args args
wantCnt int
wantErr bool
wantNameOrder bool
2020-06-16 18:59:30 -04:00
}{
{
name : "no-limit" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2020-06-16 18:59:30 -04:00
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" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2021-06-20 16:16:51 -07:00
createCnt : 10 ,
args : args {
opt : [] Option { WithLimit ( 10 )},
},
wantCnt : 10 ,
wantErr : false ,
},
2020-06-16 18:59:30 -04:00
{
name : "custom-limit" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2020-06-16 18:59:30 -04:00
createCnt : 10 ,
args : args {
where : "1=1" ,
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 3 ,
wantErr : false ,
},
{
name : "simple" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2020-06-16 18:59:30 -04:00
createCnt : 1 ,
args : args {
where : "public_id = ?" ,
2022-10-25 18:29:38 -05:00
arg : [] any { knownUser . PublicId },
2020-06-16 18:59:30 -04:00
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 1 ,
wantErr : false ,
},
2021-06-20 16:16:51 -07:00
{
name : "no args" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2021-06-20 16:16:51 -07:00
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" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2021-06-20 16:16:51 -07:00
createCnt : 1 ,
args : args {
2022-10-25 18:29:38 -05:00
arg : [] any { knownUser . PublicId },
2021-06-20 16:16:51 -07:00
opt : [] Option { WithLimit ( 3 )},
},
wantErr : true ,
},
2020-06-16 18:59:30 -04:00
{
name : "not-found" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2020-06-16 18:59:30 -04:00
createCnt : 1 ,
args : args {
where : "public_id = ?" ,
2022-10-25 18:29:38 -05:00
arg : [] any { "bad-id" },
2020-06-16 18:59:30 -04:00
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 0 ,
wantErr : false ,
},
{
name : "bad-where" ,
2022-01-05 14:38:38 -05:00
db : New ( conn ),
2020-06-16 18:59:30 -04:00
createCnt : 1 ,
args : args {
where : "bad_column_name = ?" ,
2022-10-25 18:29:38 -05:00
arg : [] any { knownUser . PublicId },
2020-06-16 18:59:30 -04:00
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 0 ,
wantErr : true ,
},
{
name : "nil-underlying" ,
2022-01-05 14:38:38 -05:00
db : New ( nil ),
2020-06-16 18:59:30 -04:00
createCnt : 1 ,
args : args {
where : "public_id = ?" ,
2022-10-25 18:29:38 -05:00
arg : [] any { knownUser . PublicId },
2020-06-16 18:59:30 -04:00
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 ++ {
2022-01-05 14:38:38 -05:00
testUsers = append ( testUsers , testUser ( t , conn , 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
2022-08-10 17:20:06 -04:00
var foundUsers [] * db_test . TestUser
2020-06-16 18:59:30 -04:00
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 ) {
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
2020-09-16 12:02:02 -04:00
require := require . New ( t )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-09-16 12:02:02 -04:00
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 )
2022-10-25 18:29:38 -05:00
rowsAffected , err := w . Exec ( context . Background (), "update db_test_user set name = ? where public_id = ?" , [] any { "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 ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := 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 (), 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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2022-01-05 14:38:38 -05:00
rw := New ( conn )
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 )
})
2022-12-20 07:41:31 -05:00
t . Run ( "commit-and-rollback-errors" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
mockConn , mock := TestSetupWithMock ( t )
mockConn . Debug ( true )
rw := New ( mockConn )
user , err := db_test . NewTestUser ()
require . NoError ( err )
user . Id = 1
mock . ExpectBegin ()
mock . ExpectExec ( `UPDATE` ). WillReturnResult ( sqlmock . NewResult ( 1 , 1 ))
mock . ExpectQuery ( `SELECT` ). WillReturnRows ( sqlmock . NewRows ([] string { "id" }). AddRow ( 1 ))
mock . ExpectCommit (). WillReturnError ( fmt . Errorf ( "commit-err" ))
mock . ExpectRollback (). WillReturnError ( fmt . Errorf ( "rollback-err" ))
_ , err = rw . DoTx ( context . Background (), 10 , ExpBackoff {}, func ( r Reader , w Writer ) error {
user . Name = "friendly-name"
rowsUpdated , err := w . Update ( context . Background (), user , [] string { "Name" }, nil )
if err != nil {
return err
}
if rowsUpdated != 1 {
return fmt . Errorf ( "error in number of rows updated %d" , rowsUpdated )
}
return nil
})
require . Error ( err )
assert . Contains ( err . Error (), "commit error" )
assert . Contains ( err . Error (), "rollback error" )
})
2020-05-05 13:29:47 -04:00
}
2020-05-11 16:54:05 -04:00
func TestDb_Delete ( t * testing . T ) {
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
2023-09-06 18:25:49 -04:00
oplogWrapper := TestOplogWrapper ( t , conn )
2020-06-09 21:22:15 -04:00
newUser := func () * db_test . TestUser {
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-09 21:22:15 -04:00
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 },
}
}
2021-11-11 16:13:44 -05:00
// seed some test users, so we won't just happen to get a false positive
// with only 1 entry in the db
for i := 0 ; i < 1000 ; i ++ {
_ = newUser ()
}
2020-06-09 21:22:15 -04:00
type args struct {
i * db_test . TestUser
opt [] Option
metadata func ( publicId string ) oplog . Metadata
}
tests := [] struct {
name string
2021-09-30 09:17:27 -04:00
underlying * DB
2020-06-09 21:22:15 -04:00
wrapper wrapping . Wrapper
args args
want int
wantOplog bool
2021-11-11 16:13:44 -05:00
wantFound bool
2020-06-09 21:22:15 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-09 21:22:15 -04:00
args : args {
i : newUser (),
},
want : 1 ,
wantErr : false ,
},
2021-11-11 16:13:44 -05:00
{
name : "with-where-no-delete" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2021-11-11 16:13:44 -05:00
args : args {
i : newUser (),
opt : [] Option { WithWhere ( "1 = ?" , 2 )},
},
wantFound : true ,
want : 0 ,
wantErr : false ,
},
{
name : "with-where-and-delete" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2021-11-11 16:13:44 -05:00
args : args {
i : newUser (),
opt : [] Option { WithWhere ( "1 = ?" , 1 )},
},
want : 1 ,
wantErr : false ,
},
2020-06-09 21:22:15 -04:00
{
name : "valid-with-oplog" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-09 21:22:15 -04:00
args : args {
i : newUser (),
metadata : newMetadata ,
},
wantOplog : true ,
want : 1 ,
wantErr : false ,
},
{
name : "nil-wrapper" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-09 21:22:15 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-09 21:22:15 -04:00
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 ,
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-09 21:22:15 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-09 21:22:15 -04:00
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 )
2022-01-05 14:38:38 -05:00
rw := New ( tt . underlying )
2020-06-09 21:22:15 -04:00
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 )
2021-11-11 16:13:44 -05:00
if tt . wantFound {
assert . NoError ( err )
assert . Equal ( tt . args . i . PublicId , foundUser . PublicId )
return
}
2020-06-09 21:22:15 -04:00
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-29 08:18:57 -04:00
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 {}
2023-09-06 18:25:49 -04:00
rowsDeleted , err := w . Delete ( context . Background (), user , NewOplogMsg ( & deleteMsg ), WithOplog ( oplogWrapper , oplog . Metadata { "alice" : [] string { "bob" }}))
2020-06-29 08:18:57 -04:00
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 ) {
2022-01-05 14:38:38 -05:00
testCtx := context . Background ()
2020-06-29 08:18:57 -04:00
assert , require := assert . New ( t ), require . New ( t )
2022-01-05 14:38:38 -05:00
w := New ( conn )
2020-06-29 08:18:57 -04:00
2022-01-05 14:38:38 -05:00
ticket , err := w . GetTicket ( testCtx , & db_test . TestUser {})
2020-06-29 08:18:57 -04:00
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 },
}
2023-09-06 18:25:49 -04:00
err = w . WriteOplogEntryWith ( context . Background (), oplogWrapper , ticket , metadata , [] * oplog . Message { & createMsg , & deleteMsg })
2020-06-29 08:18:57 -04:00
require . NoError ( err )
2022-01-05 14:38:38 -05:00
err = TestVerifyOplog ( t , w , user . PublicId , WithOperation ( oplog . OpType_OP_TYPE_UNSPECIFIED ), WithCreateNotBefore ( 10 * time . Second ))
2020-06-29 08:18:57 -04:00
require . NoError ( err )
})
2021-11-03 14:04:47 -04:00
t . Run ( "multi-column" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-01-05 14:38:38 -05:00
w := New ( conn )
scooter := testScooter ( t , conn , "" , 0 )
accessory := testAccessory ( t , conn , "test accessory" )
scooterAccessory := testScooterAccessory ( t , conn , scooter . Id , accessory . AccessoryId )
2021-11-03 14:04:47 -04:00
rowsDeleted , err := w . Delete ( context . Background (), scooterAccessory )
require . NoError ( err )
assert . Equal ( 1 , rowsDeleted )
})
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 ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
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 )
2022-01-05 14:38:38 -05:00
w := New ( conn )
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 )
2021-09-30 08:47:34 -04:00
where := "select * from db_test_user where name in (?, ?)"
2022-10-25 18:29:38 -05:00
rows , err := w . Query ( context . Background (), where , [] any { "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
2022-01-05 14:38:38 -05:00
err = w . ScanRows ( context . Background (), rows , & u )
2020-09-25 13:35:32 -04:00
require . NoError ( err )
assert . Equal ( user . PublicId , u . PublicId )
}
2024-01-22 14:03:40 -05:00
assert . NoError ( rows . Err ())
2020-09-25 13:35:32 -04:00
})
}
func TestDb_Query ( t * testing . T ) {
t . Parallel ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
2020-09-25 13:35:32 -04:00
t . Run ( "valid" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-01-05 14:38:38 -05:00
rw := New ( conn )
2020-09-25 13:35:32 -04:00
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 )
2021-09-30 08:47:34 -04:00
where := "select * from db_test_user where name in (?, ?)"
2022-10-25 18:29:38 -05:00
rows , err := rw . Query ( context . Background (), where , [] any { "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
2022-01-05 14:38:38 -05:00
err = rw . ScanRows ( context . Background (), 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 )
}
2024-01-22 14:03:40 -05:00
assert . NoError ( rows . Err ())
2020-05-05 13:29:47 -04:00
})
}
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" )
2021-11-09 11:58:57 -05:00
TestCreateTables ( t , db )
2023-09-06 18:25:49 -04:00
oplogWrapper := TestOplogWrapper ( t , db )
2020-06-29 06:31:23 -04:00
testOplogResourceId := testId ( t )
2024-08-01 11:18:53 -04:00
createFn := func () [] * db_test . TestUser {
results := [] * db_test . TestUser {}
2020-06-29 06:31:23 -04:00
for i := 0 ; i < 10 ; i ++ {
u , err := db_test . NewTestUser ()
require . NoError ( t , err )
results = append ( results , u )
}
return results
}
2022-10-25 18:29:38 -05:00
createMixedFn := func () [] any {
2020-06-29 06:31:23 -04:00
u , err := db_test . NewTestUser ()
require . NoError ( t , err )
c , err := db_test . NewTestCar ()
require . NoError ( t , err )
2022-10-25 18:29:38 -05:00
return [] any {
2020-06-29 06:31:23 -04:00
u ,
c ,
}
}
2020-07-01 05:40:02 -04:00
returnedMsgs := [] * oplog . Message {}
2020-06-29 06:31:23 -04:00
type args struct {
2024-08-01 11:18:53 -04:00
createItems any
2020-06-29 06:31:23 -04:00
opt [] Option
}
tests := [] struct {
2020-07-01 05:40:02 -04:00
name string
2021-09-30 09:17:27 -04:00
underlying * DB
2020-07-01 05:40:02 -04:00
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-06-29 06:31:23 -04:00
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-07-01 05:40:02 -04:00
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-06-29 06:31:23 -04:00
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 {
2022-10-25 18:29:38 -05:00
createItems : [] any {},
2020-06-29 06:31:23 -04:00
},
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 )
2022-01-05 14:38:38 -05:00
rw := New ( tt . underlying )
2020-06-29 06:31:23 -04:00
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 )
2024-08-01 11:18:53 -04:00
for _ , item := range tt . args . createItems .([] * db_test . TestUser ) {
2020-06-29 06:31:23 -04:00
u := db_test . AllocTestUser ()
2024-08-01 11:18:53 -04:00
u . PublicId = item . PublicId
2020-06-29 06:31:23 -04:00
err := rw . LookupByPublicId ( context . Background (), & u )
assert . NoError ( err )
2024-08-01 11:18:53 -04:00
assert . Truef ( proto . Equal ( item . 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 {
2024-08-01 11:18:53 -04:00
assert . Equal ( len ( tt . args . createItems .([] * db_test . TestUser )), len ( returnedMsgs ))
2020-07-01 05:40:02 -04:00
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" )
2021-11-09 11:58:57 -05:00
TestCreateTables ( t , db )
2023-09-06 18:25:49 -04:00
oplogWrapper := TestOplogWrapper ( t , db )
2020-06-29 06:31:23 -04:00
testOplogResourceId := testId ( t )
2024-08-01 11:18:53 -04:00
createFn := func () [] * db_test . TestUser {
results := [] * db_test . TestUser {}
2020-06-29 06:31:23 -04:00
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 {
2024-08-01 11:18:53 -04:00
deleteItems [] * db_test . TestUser
2020-06-29 06:31:23 -04:00
opt [] Option
}
tests := [] struct {
name string
2021-09-30 09:17:27 -04:00
underlying * DB
2020-06-29 06:31:23 -04:00
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-07-01 05:40:02 -04:00
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-06-29 06:31:23 -04:00
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 (
2023-09-06 18:25:49 -04:00
oplogWrapper ,
2020-06-29 06:31:23 -04:00
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 {
2024-08-01 11:18:53 -04:00
deleteItems : [] * db_test . TestUser {},
2020-06-29 06:31:23 -04:00
},
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 )
2022-01-05 14:38:38 -05:00
rw := New ( tt . underlying )
2020-06-29 06:31:23 -04:00
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 ()
2024-08-01 11:18:53 -04:00
u . PublicId = item . PublicId
2020-06-29 06:31:23 -04:00
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
})
}
}
2021-09-30 09:17:27 -04:00
func testUser ( t * testing . T , conn * DB , name , email , phoneNumber string ) * db_test . TestUser {
2020-05-31 19:55:56 -04:00
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 {
2022-01-05 14:38:38 -05:00
rw := New ( conn )
err := rw . Create ( context . Background (), u )
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
2021-09-30 09:17:27 -04:00
func testCar ( t * testing . T , conn * DB , name , model string , mpg int32 ) * db_test . TestCar {
2020-06-24 12:33:49 -04:00
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 {
2022-01-05 14:38:38 -05:00
rw := New ( conn )
err := rw . Create ( context . Background (), c )
2020-06-24 12:33:49 -04:00
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
2021-09-30 09:17:27 -04:00
func testScooter ( t * testing . T , conn * DB , model string , mpg int32 ) * db_test . TestScooter {
2020-06-29 08:18:57 -04:00
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 {
2022-01-05 14:38:38 -05:00
rw := New ( conn )
err := rw . Create ( context . Background (), u )
2020-06-29 08:18:57 -04:00
require . NoError ( err )
}
return u
}
2021-11-03 14:04:47 -04:00
func testAccessory ( t * testing . T , conn * DB , description string ) * db_test . TestAccessory {
t . Helper ()
require := require . New ( t )
a , err := db_test . NewTestAccessory ( description )
require . NoError ( err )
if conn != nil {
2022-01-05 14:38:38 -05:00
rw := New ( conn )
rw . Create ( context . Background (), a )
2021-11-03 14:04:47 -04:00
require . NoError ( err )
}
return a
}
func testScooterAccessory ( t * testing . T , conn * DB , scooterId , accessoryId uint32 ) * db_test . TestScooterAccessory {
t . Helper ()
require := require . New ( t )
a , err := db_test . NewTestScooterAccessory ( scooterId , accessoryId )
require . NoError ( err )
if conn != nil {
2022-01-05 14:38:38 -05:00
rw := New ( conn )
rw . Create ( context . Background (), a )
2021-11-03 14:04:47 -04:00
require . NoError ( err )
}
return a
}
2020-06-29 08:18:57 -04:00
func TestDb_LookupById ( t * testing . T ) {
t . Parallel ()
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
scooter := testScooter ( t , conn , "" , 0 )
user := testUser ( t , conn , "" , "" , "" )
accessory := testAccessory ( t , conn , "test accessory" )
scooterAccessory := testScooterAccessory ( t , conn , scooter . Id , accessory . AccessoryId )
2021-11-03 14:04:47 -04:00
2020-06-29 08:18:57 -04:00
type args struct {
2022-10-25 18:29:38 -05:00
resource any
2021-11-03 14:04:47 -04:00
opt [] Option
2020-06-29 08:18:57 -04:00
}
tests := [] struct {
name string
2021-09-30 09:17:27 -04:00
underlying * DB
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2021-11-03 14:04:47 -04:00
resource : scooter ,
2020-06-29 08:18:57 -04:00
},
wantErr : false ,
want : scooter ,
},
{
name : "simple-public-id" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2021-11-03 14:04:47 -04:00
resource : user ,
2020-06-29 08:18:57 -04:00
},
wantErr : false ,
want : user ,
},
2021-11-03 14:04:47 -04:00
{
name : "simple-non-public-non-private-id" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2021-11-03 14:04:47 -04:00
args : args {
resource : accessory ,
},
wantErr : false ,
want : accessory ,
},
{
name : "compond" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2021-11-03 14:04:47 -04:00
args : args {
resource : scooterAccessory ,
},
wantErr : false ,
want : scooterAccessory ,
},
{
name : "compond-with-zero-value-pk" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2021-11-03 14:04:47 -04:00
args : args {
2022-10-25 18:29:38 -05:00
resource : func () any {
2021-11-03 14:04:47 -04:00
cp := scooterAccessory . Clone ()
cp .( * db_test . TestScooterAccessory ). ScooterId = 0
return cp
}(),
},
wantErr : true ,
wantIsErr : errors . InvalidParameter ,
},
2020-06-29 08:18:57 -04:00
{
name : "missing-public-id" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2021-11-03 14:04:47 -04:00
resource : & db_test . TestUser {
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2021-11-03 14:04:47 -04:00
resource : & db_test . TestScooter {
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2021-11-03 14:04:47 -04:00
resource : & db_test . NotIder {},
2020-06-29 08:18:57 -04:00
},
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 {
2021-11-03 14:04:47 -04:00
resource : user ,
2020-06-29 08:18:57 -04:00
},
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 )
2022-01-05 14:38:38 -05:00
rw := New ( tt . underlying )
2021-11-03 14:04:47 -04:00
cloner , ok := tt . args . resource .( db_test . Cloner )
2020-06-29 08:18:57 -04:00
require . True ( ok )
cp := cloner . Clone ()
err := rw . LookupById ( context . Background (), cp , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
2021-11-03 14:04:47 -04:00
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "want err code: %q got: %q" , 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 ) {
2022-01-05 14:38:38 -05:00
u := testUser ( t , conn , "" , "" , "" )
rw := New ( conn )
2020-06-29 08:18:57 -04:00
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 ) {
2022-01-05 14:38:38 -05:00
testCtx := context . Background ()
2020-07-06 10:15:01 -07:00
db , _ := TestSetup ( t , "postgres" )
2021-11-09 11:58:57 -05:00
TestCreateTables ( t , db )
2020-06-29 08:18:57 -04:00
type notReplayable struct {}
tests := [] struct {
name string
2021-09-30 09:17:27 -04:00
underlying * DB
2022-10-25 18:29:38 -05:00
aggregateType any
2020-06-29 08:18:57 -04:00
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 )
2022-01-05 14:38:38 -05:00
rw := New ( tt . underlying )
got , err := rw . GetTicket ( testCtx , tt . aggregateType )
2020-06-29 08:18:57 -04:00
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 ) {
2022-01-05 14:38:38 -05:00
conn , _ := TestSetup ( t , "postgres" )
TestCreateTables ( t , conn )
2023-09-06 18:25:49 -04:00
oplogWrapper := TestOplogWrapper ( t , conn )
2022-01-05 14:38:38 -05:00
w := New ( conn )
testCtx := context . Background ()
2020-06-29 08:18:57 -04:00
2022-01-05 14:38:38 -05:00
ticket , err := w . GetTicket ( testCtx , & db_test . TestUser {})
2020-06-29 08:18:57 -04:00
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
2021-09-30 09:17:27 -04:00
underlying * DB
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-29 08:18:57 -04:00
ticket : ticket ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg },
},
wantErr : false ,
},
{
name : "valid-multiple" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-29 08:18:57 -04:00
ticket : ticket ,
metadata : metadata ,
msgs : [] * oplog . Message { & createMsg , & createMsg },
},
wantErr : false ,
},
{
name : "missing-ticket" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-29 08:18:57 -04:00
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 {
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-29 08:18:57 -04:00
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" ,
2022-01-05 14:38:38 -05:00
underlying : conn ,
2020-06-29 08:18:57 -04:00
args : args {
2023-09-06 18:25:49 -04:00
wrapper : oplogWrapper ,
2020-06-29 08:18:57 -04:00
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 )
2022-01-05 14:38:38 -05:00
rw := New ( tt . underlying )
2020-06-29 08:18:57 -04:00
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 )
})
}
}
2022-01-05 14:38:38 -05: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
// }
// tests := []struct {
// name string
// args args
// want interface{}
// err errors.Code
// }{
// {
// name: "nil",
// args: args{
// v: nil,
// f: []string{"field"},
// d: 1,
// },
// err: errors.InvalidParameter,
// },
// {
// name: "string",
// args: args{
// v: "blank",
// f: []string{"field"},
// d: 1,
// },
// err: errors.InvalidParameter,
// },
// {
// name: "pointer-to-nil-struct",
// args: args{
// v: nilZ,
// f: []string{"field"},
// d: 1,
// },
// err: errors.InvalidParameter,
// },
// {
// name: "pointer-to-string",
// args: args{
// v: &s,
// f: []string{"field"},
// d: 1,
// },
// err: errors.InvalidParameter,
// },
// {
// name: "not-pointer",
// args: args{
// v: Z{
// F: "foo",
// },
// f: []string{"field"},
// d: 1,
// },
// err: errors.InvalidParameter,
// },
// {
// name: "map",
// args: args{
// v: map[string]int{
// "A": 31,
// "B": 34,
// },
// f: []string{"field"},
// d: 1,
// },
// err: errors.InvalidParameter,
// },
// {
// 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)
// if tt.err != 0 {
// assert.True(errors.Match(errors.T(tt.err), err))
// 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
// }
// tests := []struct {
// 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
// }
// tests := []struct {
// 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 }
2022-10-25 18:29:38 -05:00
var users [] any
2020-07-01 05:40:02 -04:00
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 )
2022-10-25 18:29:38 -05:00
mixed := [] any {
2020-07-01 05:40:02 -04:00
& 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
2022-10-25 18:29:38 -05:00
items [] any
2020-07-01 05:40:02 -04:00
}
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 ,
2022-10-25 18:29:38 -05:00
items : [] any {},
2020-07-01 05:40:02 -04:00
},
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
2022-01-05 14:38:38 -05:00
// func TestDb_lookupAfterWrite(t *testing.T) {
// t.Parallel()
// db, _ := TestSetup(t, "postgres")
// TestCreateTables(t, db)
// scooter := testScooter(t, db, "", 0)
// user := testUser(t, db, "", "", "")
// type args struct {
// resourceWithIder interface{}
// opt []Option
// }
// tests := []struct {
// name string
// underlying *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 := New(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)))
// }
// })
// }
// }
2023-10-25 12:48:00 -07:00
func TestDb_Now ( t * testing . T ) {
t . Parallel ()
assert , require := assert . New ( t ), require . New ( t )
conn , _ := TestSetup ( t , "postgres" )
rw := New ( conn )
ctx := context . Background ()
now , err := rw . Now ( ctx )
require . NoError ( err )
now2 , err := rw . Now ( ctx )
require . NoError ( err )
// Calling Now() outside a transaction should result in different timestamps.
// Using time.Equal instead of assert.Equal since it's more consistently
// accurate for time.Time structs.
assert . False ( now . Equal ( now2 ))
_ , err = rw . DoTx ( ctx , StdRetryCnt , ExpBackoff {}, func ( r Reader , _ Writer ) error {
now3 , err := r . Now ( ctx )
require . NoError ( err )
now4 , err := r . Now ( ctx )
require . NoError ( err )
// Calling Now() inside a transaction should result in the same timestamp
assert . True ( now3 . Equal ( now4 ))
// The timestamps within the transaction should differ from those created outside.
assert . False ( now3 . Equal ( now ))
assert . False ( now3 . Equal ( now2 ))
return nil
})
require . NoError ( err )
}