2020-08-10 17:15:02 -04:00
// Copyright (c) HashiCorp, Inc.
2020-12-23 11:00:00 -08:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-06-11 16:36:47 -04:00
package iam
import (
"context"
2020-07-10 19:01:23 -04:00
"sort"
2020-06-11 16:36:47 -04:00
"testing"
"time"
2022-10-28 15:52:06 -04:00
"github.com/hashicorp/boundary/globals"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2023-12-21 12:54:27 -08:00
"github.com/hashicorp/boundary/internal/kms"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/oplog"
2020-06-11 16:36:47 -04:00
"github.com/hashicorp/go-uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestRepository_CreateGroup ( t * testing . T ) {
t . Parallel ()
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-11 16:36:47 -04:00
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper )
2020-06-11 16:36:47 -04:00
id := testId ( t )
2020-08-19 16:47:18 -04:00
org , proj := TestScopes ( t , repo )
2020-06-11 16:36:47 -04:00
type args struct {
group * Group
opt [] Option
}
tests := [] struct {
name string
args args
wantDup bool
wantErr bool
wantErrMsg string
2020-12-23 11:00:00 -08:00
wantIsError errors . Code
2020-06-11 16:36:47 -04:00
}{
{
name : "valid-org" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , org . PublicId , WithName ( "valid-org" + id ), WithDescription ( id ))
2020-06-11 16:36:47 -04:00
assert . NoError ( t , err )
return g
}(),
},
wantErr : false ,
},
{
name : "valid-proj" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , proj . PublicId , WithName ( "valid-proj" + id ), WithDescription ( id ))
2020-06-11 16:36:47 -04:00
assert . NoError ( t , err )
return g
}(),
},
wantErr : false ,
},
{
name : "bad-public-id" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , proj . PublicId , WithName ( "valid-proj" + id ), WithDescription ( id ))
2020-06-11 16:36:47 -04:00
assert . NoError ( t , err )
g . PublicId = id
return g
}(),
},
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).CreateGroup: public id not empty: parameter violation: error #100" ,
wantIsError : errors . InvalidParameter ,
2020-06-11 16:36:47 -04:00
wantErr : true ,
},
{
name : "nil-group" ,
args : args {
group : nil ,
},
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).CreateGroup: missing group: parameter violation: error #10" ,
wantIsError : errors . InvalidParameter ,
2020-06-11 16:36:47 -04:00
},
{
name : "nil-store" ,
args : args {
group : func () * Group {
return & Group {
Group : nil ,
}
}(),
},
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).CreateGroup: missing group store: parameter violation: error #100" ,
wantIsError : errors . InvalidParameter ,
2020-06-11 16:36:47 -04:00
},
{
name : "bad-scope-id" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , id )
2020-06-11 16:36:47 -04:00
assert . NoError ( t , err )
return g
}(),
},
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).create: error getting metadata: iam.(Repository).stdMetadata: unable to get scope: iam.LookupScope: db.LookupWhere: record not found, search issue: error #1100" ,
wantIsError : errors . RecordNotFound ,
2020-06-11 16:36:47 -04:00
},
{
name : "dup-name" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , org . PublicId , WithName ( "dup-name" + id ), WithDescription ( id ))
2020-06-11 16:36:47 -04:00
assert . NoError ( t , err )
return g
}(),
opt : [] Option { WithName ( "dup-name" + id )},
},
wantDup : true ,
wantErr : true ,
wantErrMsg : "already exists in scope " ,
2020-12-23 11:00:00 -08:00
wantIsError : errors . NotUnique ,
2020-06-11 16:36:47 -04:00
},
{
name : "dup-name-but-diff-scope" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , proj . PublicId , WithName ( "dup-name-but-diff-scope" + id ), WithDescription ( id ))
2020-06-11 16:36:47 -04:00
assert . NoError ( t , err )
return g
}(),
opt : [] Option { WithName ( "dup-name-but-diff-scope" + id )},
},
wantDup : true ,
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert := assert . New ( t )
if tt . wantDup {
2023-06-06 17:09:08 -04:00
dup , err := NewGroup ( ctx , org . PublicId , tt . args . opt ... )
2020-06-11 16:36:47 -04:00
assert . NoError ( err )
dup , err = repo . CreateGroup ( context . Background (), dup , tt . args . opt ... )
assert . NoError ( err )
assert . NotNil ( dup )
}
grp , err := repo . CreateGroup ( context . Background (), tt . args . group , tt . args . opt ... )
if tt . wantErr {
assert . Error ( err )
assert . Nil ( grp )
assert . Contains ( err . Error (), tt . wantErrMsg )
2020-12-23 11:00:00 -08:00
assert . True ( errors . Match ( errors . T ( tt . wantIsError ), err ))
2020-06-11 16:36:47 -04:00
return
}
assert . NoError ( err )
assert . NotNil ( grp . CreateTime )
assert . NotNil ( grp . UpdateTime )
2020-07-22 15:10:21 -07:00
foundGrp , _ , err := repo . LookupGroup ( context . Background (), grp . PublicId )
2020-06-11 16:36:47 -04:00
assert . NoError ( err )
assert . True ( proto . Equal ( foundGrp , grp ))
err = db . TestVerifyOplog ( t , rw , grp . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_CREATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
})
}
}
func TestRepository_UpdateGroup ( t * testing . T ) {
t . Parallel ()
2021-09-30 09:17:27 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-11 16:36:47 -04:00
a := assert . New ( t )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper )
2020-06-11 16:36:47 -04:00
id , err := uuid . GenerateUUID ()
a . NoError ( err )
2020-08-19 16:47:18 -04:00
org , proj := TestScopes ( t , repo )
2020-06-11 16:36:47 -04:00
pubId := func ( s string ) * string { return & s }
type args struct {
name string
description string
fieldMaskPaths [] string
opt [] Option
ScopeId string
PublicId * string
}
tests := [] struct {
name string
newScopeId string
newGrpOpts [] Option
args args
wantRowsUpdate int
wantErr bool
wantErrMsg string
2020-12-21 10:07:34 -08:00
wantIsError errors . Code
2020-06-11 16:36:47 -04:00
wantDup bool
2020-07-08 17:02:01 -04:00
directUpdate bool
2020-06-11 16:36:47 -04:00
}{
{
name : "valid" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "valid-no-op" ,
args : args {
name : "valid-no-op" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
newGrpOpts : [] Option { WithName ( "valid-no-op" + id )},
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "not-found" ,
args : args {
name : "not-found" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
PublicId : func () * string { s := "1" ; return & s }(),
},
newScopeId : org . PublicId ,
wantErr : true ,
wantRowsUpdate : 0 ,
2022-01-05 14:38:38 -05:00
wantErrMsg : "record not found, search issue: error #1100" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . RecordNotFound ,
2020-06-11 16:36:47 -04:00
},
{
name : "null-name" ,
args : args {
name : "" ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
newGrpOpts : [] Option { WithName ( "null-name" + id )},
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "null-description" ,
args : args {
name : "" ,
fieldMaskPaths : [] string { "Description" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
newGrpOpts : [] Option { WithDescription ( "null-description" + id )},
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "empty-field-mask" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string {},
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateGroup: empty field mask: parameter violation: error #104" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . EmptyFieldMask ,
2020-06-11 16:36:47 -04:00
},
{
name : "nil-fieldmask" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : nil ,
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateGroup: empty field mask: parameter violation: error #104" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . EmptyFieldMask ,
2020-06-11 16:36:47 -04:00
},
{
name : "read-only-fields" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "CreateTime" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateGroup: invalid field mask: CreateTime: parameter violation: error #103" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . InvalidFieldMask ,
2020-06-11 16:36:47 -04:00
},
{
name : "unknown-fields" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "Alice" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : true ,
wantRowsUpdate : 0 ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateGroup: invalid field mask: Alice: parameter violation: error #103" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . InvalidFieldMask ,
2020-06-11 16:36:47 -04:00
},
{
name : "no-public-id" ,
args : args {
name : "valid" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
PublicId : pubId ( "" ),
},
newScopeId : org . PublicId ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateGroup: missing public id: parameter violation: error #100" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . InvalidParameter ,
2020-06-11 16:36:47 -04:00
wantRowsUpdate : 0 ,
},
{
name : "proj-scope-id-no-mask" ,
args : args {
name : "proj-scope-id" + id ,
ScopeId : proj . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).UpdateGroup: empty field mask: parameter violation: error #104" ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . EmptyFieldMask ,
2020-06-11 16:36:47 -04:00
},
{
name : "empty-scope-id-with-name-mask" ,
args : args {
name : "empty-scope-id" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : "" ,
},
newScopeId : org . PublicId ,
wantErr : false ,
wantRowsUpdate : 1 ,
},
{
name : "dup-name-in-diff-scope" ,
args : args {
name : "dup-name-in-diff-scope" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : proj . PublicId ,
},
newScopeId : proj . PublicId ,
newGrpOpts : [] Option { WithName ( "dup-name-in-diff-scope-pre-update" + id )},
wantErr : false ,
wantRowsUpdate : 1 ,
wantDup : true ,
},
{
name : "dup-name" ,
args : args {
name : "dup-name" + id ,
fieldMaskPaths : [] string { "Name" },
ScopeId : org . PublicId ,
},
newScopeId : org . PublicId ,
wantErr : true ,
wantDup : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : " already exists in scope " + org . PublicId ,
2020-12-21 10:07:34 -08:00
wantIsError : errors . NotUnique ,
2020-06-11 16:36:47 -04:00
},
2020-07-08 17:02:01 -04:00
{
name : "modified-scope" ,
args : args {
name : "modified-scope" + id ,
fieldMaskPaths : [] string { "ScopeId" },
ScopeId : proj . PublicId ,
opt : [] Option { WithSkipVetForWrite ( true )},
},
newScopeId : org . PublicId ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : `iam.(Repository).update: db.DoTx: iam.(Repository).update: db.Update: immutable column: iam_group.scope_id: integrity violation: error #1003` ,
2020-07-08 17:02:01 -04:00
directUpdate : true ,
},
2020-06-11 16:36:47 -04:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2020-08-07 12:55:02 -04:00
assert , require := assert . New ( t ), require . New ( t )
2020-06-11 16:36:47 -04:00
if tt . wantDup {
g := TestGroup ( t , conn , org . PublicId )
g . Name = tt . args . name
2020-08-07 12:55:02 -04:00
_ , _ , _ , err := repo . UpdateGroup ( context . Background (), g , g . Version , tt . args . fieldMaskPaths , tt . args . opt ... )
2020-06-11 16:36:47 -04:00
assert . NoError ( err )
}
u := TestGroup ( t , conn , tt . newScopeId , tt . newGrpOpts ... )
updateGrp := allocGroup ()
updateGrp . PublicId = u . PublicId
if tt . args . PublicId != nil {
updateGrp . PublicId = * tt . args . PublicId
}
updateGrp . ScopeId = tt . args . ScopeId
updateGrp . Name = tt . args . name
updateGrp . Description = tt . args . description
2020-08-07 12:55:02 -04:00
updateGrp . Version = u . Version
2020-06-11 16:36:47 -04:00
2020-07-08 17:02:01 -04:00
var groupAfterUpdate * Group
var updatedRows int
var err error
if tt . directUpdate {
g := updateGrp . Clone ()
2022-10-25 18:29:38 -05:00
var resource any
2020-08-07 12:55:02 -04:00
resource , updatedRows , err = repo . update ( context . Background (), g .( * Group ), updateGrp . Version , tt . args . fieldMaskPaths , nil , tt . args . opt ... )
2020-07-08 17:02:01 -04:00
if err == nil {
groupAfterUpdate = resource .( * Group )
}
} else {
2020-08-07 12:55:02 -04:00
groupAfterUpdate , _ , updatedRows , err = repo . UpdateGroup ( context . Background (), & updateGrp , updateGrp . Version , tt . args . fieldMaskPaths , tt . args . opt ... )
2020-07-08 17:02:01 -04:00
}
2020-06-11 16:36:47 -04:00
if tt . wantErr {
assert . Error ( err )
2021-01-12 08:50:19 -08:00
assert . True ( errors . Match ( errors . T ( tt . wantIsError ), err ))
2020-07-08 17:02:01 -04:00
assert . Nil ( groupAfterUpdate )
2020-06-11 16:36:47 -04:00
assert . Equal ( 0 , updatedRows )
assert . Contains ( err . Error (), tt . wantErrMsg )
err = db . TestVerifyOplog ( t , rw , u . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . IsNotFoundError ( err ))
2020-06-11 16:36:47 -04:00
return
}
assert . NoError ( err )
assert . Equal ( tt . wantRowsUpdate , updatedRows )
switch tt . name {
case "valid-no-op" :
2020-07-08 17:02:01 -04:00
assert . Equal ( u . UpdateTime , groupAfterUpdate . UpdateTime )
2020-06-11 16:36:47 -04:00
default :
2020-07-08 17:02:01 -04:00
assert . NotEqual ( u . UpdateTime , groupAfterUpdate . UpdateTime )
2020-06-11 16:36:47 -04:00
}
2020-07-22 15:10:21 -07:00
foundGrp , _ , err := repo . LookupGroup ( context . Background (), u . PublicId )
2020-08-07 12:55:02 -04:00
require . NoError ( err )
2020-07-08 17:02:01 -04:00
assert . True ( proto . Equal ( groupAfterUpdate , foundGrp ))
2021-09-30 09:17:27 -04:00
underlyingDB , err := conn . SqlDB ( ctx )
2021-09-30 08:47:34 -04:00
require . NoError ( err )
dbassert := dbassert . New ( t , underlyingDB )
2020-06-11 16:36:47 -04:00
if tt . args . name == "" {
dbassert . IsNull ( foundGrp , "name" )
}
if tt . args . description == "" {
dbassert . IsNull ( foundGrp , "description" )
}
err = db . TestVerifyOplog ( t , rw , u . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
})
}
}
func TestRepository_DeleteGroup ( t * testing . T ) {
t . Parallel ()
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-11 16:36:47 -04:00
a := assert . New ( t )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper )
org , _ := TestScopes ( t , repo )
2020-06-11 16:36:47 -04:00
2023-06-06 17:09:08 -04:00
grpId , err := newGroupId ( ctx )
2020-12-23 11:00:00 -08:00
a . NoError ( err )
2020-06-11 16:36:47 -04:00
type args struct {
group * Group
opt [] Option
}
tests := [] struct {
name string
args args
wantRowsDeleted int
wantErr bool
wantErrMsg string
}{
{
name : "valid" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
},
wantRowsDeleted : 1 ,
wantErr : false ,
},
{
name : "no-public-id" ,
args : args {
group : func () * Group {
g := allocGroup ()
return & g
}(),
},
wantRowsDeleted : 0 ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).DeleteGroup: missing public id: parameter violation: error #100" ,
2020-06-11 16:36:47 -04:00
},
{
name : "not-found" ,
args : args {
group : func () * Group {
2023-06-06 17:09:08 -04:00
g , err := NewGroup ( ctx , org . PublicId )
2020-12-23 11:00:00 -08:00
g . PublicId = grpId
2020-06-11 16:36:47 -04:00
a . NoError ( err )
return g
}(),
},
wantRowsDeleted : 0 ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantErrMsg : "iam.(Repository).DeleteGroup: for group " + grpId + ": db.LookupById: record not found, search issue: error #1100" ,
2020-06-11 16:36:47 -04:00
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert := assert . New ( t )
2023-06-06 17:09:08 -04:00
deletedRows , err := repo . DeleteGroup ( ctx , tt . args . group . PublicId , tt . args . opt ... )
2020-06-11 16:36:47 -04:00
if tt . wantErr {
assert . Error ( err )
assert . Equal ( 0 , deletedRows )
assert . Contains ( err . Error (), tt . wantErrMsg )
err = db . TestVerifyOplog ( t , rw , tt . args . group . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . IsNotFoundError ( err ))
2020-06-11 16:36:47 -04:00
return
}
assert . NoError ( err )
assert . Equal ( tt . wantRowsDeleted , deletedRows )
2023-06-06 17:09:08 -04:00
foundGroup , _ , err := repo . LookupGroup ( ctx , tt . args . group . PublicId )
2020-09-04 14:48:11 -07:00
assert . NoError ( err )
2020-06-11 16:36:47 -04:00
assert . Nil ( foundGroup )
err = db . TestVerifyOplog ( t , rw , tt . args . group . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
})
}
}
2020-06-19 12:06:25 -04:00
func TestRepository_ListGroups ( t * testing . T ) {
t . Parallel ()
2020-07-06 10:15:01 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
2020-06-19 12:06:25 -04:00
const testLimit = 10
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper , WithLimit ( testLimit ))
org , proj := TestScopes ( t , repo )
2020-06-19 12:06:25 -04:00
type args struct {
withScopeId string
opt [] Option
}
tests := [] struct {
name string
createCnt int
createScopeId string
args args
wantCnt int
wantErr bool
}{
{
2023-12-21 12:54:27 -08:00
name : "negative-limit" ,
2020-06-19 12:06:25 -04:00
createCnt : repo . defaultLimit + 1 ,
createScopeId : org . PublicId ,
args : args {
withScopeId : org . PublicId ,
opt : [] Option { WithLimit ( - 1 )},
},
2023-12-21 12:54:27 -08:00
wantErr : true ,
2020-06-19 12:06:25 -04:00
},
{
name : "no-limit-proj-group" ,
createCnt : repo . defaultLimit + 1 ,
createScopeId : proj . PublicId ,
args : args {
withScopeId : proj . PublicId ,
opt : [] Option { WithLimit ( - 1 )},
},
2023-12-21 12:54:27 -08:00
wantErr : true ,
2020-06-19 12:06:25 -04:00
},
{
name : "default-limit" ,
createCnt : repo . defaultLimit + 1 ,
createScopeId : org . PublicId ,
args : args {
withScopeId : org . PublicId ,
},
wantCnt : repo . defaultLimit ,
wantErr : false ,
},
{
name : "custom-limit" ,
createCnt : repo . defaultLimit + 1 ,
createScopeId : org . PublicId ,
args : args {
withScopeId : org . PublicId ,
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 3 ,
wantErr : false ,
},
{
name : "bad-org" ,
createCnt : 1 ,
createScopeId : org . PublicId ,
args : args {
withScopeId : "bad-id" ,
},
wantCnt : 0 ,
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-10-28 15:52:06 -04:00
db . TestDeleteWhere ( t , conn , func () any {
i := allocGroup ()
return & i
}(), "1=1" )
2020-06-19 12:06:25 -04:00
testGroups := [] * Group {}
for i := 0 ; i < tt . createCnt ; i ++ {
testGroups = append ( testGroups , TestGroup ( t , conn , tt . createScopeId ))
}
assert . Equal ( tt . createCnt , len ( testGroups ))
2023-12-21 12:54:27 -08:00
got , _ , err := repo . listGroups ( context . Background (), [] string { tt . args . withScopeId }, tt . args . opt ... )
2020-06-19 12:06:25 -04:00
if tt . wantErr {
require . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tt . wantCnt , len ( got ))
})
}
}
2020-07-10 19:01:23 -04:00
2021-01-25 19:37:40 -05:00
func TestRepository_ListGroups_Multiple_Scopes ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
repo := TestRepo ( t , conn , wrapper )
org , proj := TestScopes ( t , repo )
2022-10-25 18:29:38 -05:00
db . TestDeleteWhere ( t , conn , func () any { g := allocGroup (); return & g }(), "1=1" )
2021-01-25 19:37:40 -05:00
const numPerScope = 10
var total int
for i := 0 ; i < numPerScope ; i ++ {
TestGroup ( t , conn , "global" )
total ++
TestGroup ( t , conn , org . GetPublicId ())
total ++
TestGroup ( t , conn , proj . GetPublicId ())
total ++
}
2023-12-21 12:54:27 -08:00
got , ttime , err := repo . listGroups ( context . Background (), [] string { "global" , org . GetPublicId (), proj . GetPublicId ()})
2021-01-25 19:37:40 -05:00
require . NoError ( t , err )
assert . Equal ( t , total , len ( got ))
2023-12-21 12:54:27 -08:00
// Transaction timestamp should be within ~10 seconds of now
assert . True ( t , time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( t , time . Now (). After ( ttime . Add ( - 10 * time . Second )))
2021-01-25 19:37:40 -05:00
}
2020-07-10 19:01:23 -04:00
func TestRepository_ListMembers ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
const testLimit = 10
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper , WithLimit ( testLimit ))
org , proj := TestScopes ( t , repo )
2020-07-10 19:01:23 -04:00
pg := TestGroup ( t , conn , proj . PublicId )
og := TestGroup ( t , conn , org . PublicId )
type args struct {
withGroupId string
opt [] Option
}
tests := [] struct {
name string
createCnt int
args args
wantCnt int
wantErr bool
}{
{
name : "no-limit-pg-group" ,
createCnt : repo . defaultLimit + 1 ,
args : args {
withGroupId : pg . PublicId ,
opt : [] Option { WithLimit ( - 1 )},
},
wantCnt : repo . defaultLimit + 1 ,
wantErr : false ,
},
{
name : "no-limit-org-group" ,
createCnt : repo . defaultLimit + 1 ,
args : args {
withGroupId : og . PublicId ,
opt : [] Option { WithLimit ( - 1 )},
},
wantCnt : repo . defaultLimit + 1 ,
wantErr : false ,
},
{
name : "default-limit" ,
createCnt : repo . defaultLimit + 1 ,
args : args {
withGroupId : pg . PublicId ,
},
wantCnt : repo . defaultLimit ,
wantErr : false ,
},
{
name : "custom-limit" ,
createCnt : repo . defaultLimit + 1 ,
args : args {
withGroupId : pg . PublicId ,
opt : [] Option { WithLimit ( 3 )},
},
wantCnt : 3 ,
wantErr : false ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-10-28 15:52:06 -04:00
db . TestDeleteWhere ( t , conn , func () any {
i := allocGroupMember ()
return & i
}(), "1=1" )
2020-07-10 19:01:23 -04:00
gm := [] * GroupMemberUser {}
for i := 0 ; i < tt . createCnt ; i ++ {
2020-08-19 16:47:18 -04:00
u := TestUser ( t , repo , org . PublicId )
2020-07-10 19:01:23 -04:00
gm = append ( gm , TestGroupMember ( t , conn , tt . args . withGroupId , u . PublicId ))
}
assert . Equal ( tt . createCnt , len ( gm ))
got , err := repo . ListGroupMembers ( context . Background (), tt . args . withGroupId , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tt . wantCnt , len ( got ))
})
}
t . Run ( "missing-id" , func ( t * testing . T ) {
require := require . New ( t )
got , err := repo . ListGroupMembers ( context . Background (), "" )
require . Error ( err )
require . Nil ( got )
2020-12-23 11:00:00 -08:00
require . Truef ( errors . Match ( errors . T ( errors . InvalidParameter ), err ), "unexpected error %s" , err . Error ())
2020-07-10 19:01:23 -04:00
})
}
func TestRepository_AddGroupMembers ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper )
org , proj := TestScopes ( t , repo )
2020-07-10 19:01:23 -04:00
group := TestGroup ( t , conn , proj . PublicId )
createUsersFn := func () [] string {
results := [] string {}
for i := 0 ; i < 5 ; i ++ {
2020-08-19 16:47:18 -04:00
u := TestUser ( t , repo , org . PublicId )
2020-07-10 19:01:23 -04:00
results = append ( results , u . PublicId )
}
return results
}
groupVersion := uint32 ( 0 )
type args struct {
groupId string
2020-07-15 13:12:15 -04:00
groupVersion * uint32
2020-07-10 19:01:23 -04:00
userIds [] string
opt [] Option
}
tests := [] struct {
name string
args args
wantErr bool
wantErrIs error
}{
{
name : "valid-members" ,
args : args {
groupId : group . PublicId ,
userIds : createUsersFn (),
},
wantErr : false ,
},
{
name : "valid-next-version" ,
args : args {
groupId : group . PublicId ,
userIds : createUsersFn (),
},
wantErr : false ,
},
{
name : "bad-version" ,
args : args {
groupId : group . PublicId ,
2020-07-15 13:12:15 -04:00
groupVersion : func () * uint32 { v := uint32 ( 1000 ); return & v }(),
userIds : createUsersFn (),
},
wantErr : true ,
},
{
name : "zero-version" ,
args : args {
groupId : group . PublicId ,
groupVersion : func () * uint32 { v := uint32 ( 0 ); return & v }(),
2020-07-10 19:01:23 -04:00
userIds : createUsersFn (),
},
wantErr : true ,
},
{
name : "no-members" ,
args : args {
groupId : group . PublicId ,
userIds : nil ,
},
wantErr : true ,
},
2020-08-20 13:34:03 -04:00
{
name : "recovery-user" ,
args : args {
groupId : group . PublicId ,
2022-10-28 15:52:06 -04:00
userIds : [] string { globals . RecoveryUserId },
2020-08-20 13:34:03 -04:00
},
wantErr : true ,
},
2020-07-10 19:01:23 -04:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
var version uint32
2020-07-15 13:12:15 -04:00
switch {
case tt . args . groupVersion != nil :
version = * tt . args . groupVersion
default :
2020-07-10 19:01:23 -04:00
groupVersion += 1
version = groupVersion
}
2022-01-05 14:38:38 -05:00
gm := allocGroupMember ()
db . TestDeleteWhere ( t , conn , & gm , "1=1" )
2020-07-10 19:01:23 -04:00
got , err := repo . AddGroupMembers ( context . Background (), tt . args . groupId , version , tt . args . userIds , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
if tt . wantErrIs != nil {
assert . Truef ( errors . Is ( err , tt . wantErrIs ), "unexpected error %s" , err . Error ())
}
return
}
require . NoError ( err )
gotMembers := map [ string ] * GroupMember {}
for _ , m := range got {
gotMembers [ m . MemberId ] = m
}
for _ , id := range tt . args . userIds {
assert . NotEmpty ( gotMembers [ id ])
2020-09-25 13:35:32 -04:00
u , _ , err := repo . LookupUser ( context . Background (), id )
2020-07-10 19:01:23 -04:00
assert . NoError ( err )
assert . Equal ( id , u . PublicId )
}
err = db . TestVerifyOplog ( t , rw , group . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_CREATE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
foundMembers , err := repo . ListGroupMembers ( context . Background (), group . PublicId )
require . NoError ( err )
for _ , m := range foundMembers {
assert . NotEmpty ( gotMembers [ m . MemberId ])
assert . Equal ( gotMembers [ m . MemberId ]. GetGroupId (), m . GroupId )
}
})
}
}
func TestRepository_DeleteGroupMembers ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper )
org , _ := TestScopes ( t , repo )
2020-07-10 19:01:23 -04:00
type args struct {
group * Group
groupIdOverride * string
groupVersion uint32
createUserCnt int
deleteUserCnt int
opt [] Option
}
tests := [] struct {
name string
args args
wantRowsDeleted int
wantErr bool
2020-12-23 11:00:00 -08:00
wantIsErr errors . Code
2020-07-10 19:01:23 -04:00
}{
{
name : "valid" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
createUserCnt : 5 ,
deleteUserCnt : 5 ,
groupVersion : 2 ,
},
wantRowsDeleted : 5 ,
wantErr : false ,
},
{
name : "valid-keeping-some" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
createUserCnt : 5 ,
deleteUserCnt : 2 ,
groupVersion : 2 ,
},
wantRowsDeleted : 2 ,
wantErr : false ,
},
{
name : "not-found" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
groupVersion : 2 ,
groupIdOverride : func () * string { id := testId ( t ); return & id }(),
createUserCnt : 5 ,
deleteUserCnt : 5 ,
},
wantRowsDeleted : 0 ,
wantErr : true ,
},
{
name : "missing-group-id" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
groupVersion : 2 ,
groupIdOverride : func () * string { id := "" ; return & id }(),
createUserCnt : 5 ,
deleteUserCnt : 5 ,
},
wantRowsDeleted : 0 ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantIsErr : errors . InvalidParameter ,
2020-07-10 19:01:23 -04:00
},
2020-07-15 13:12:15 -04:00
{
name : "bad-version" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
createUserCnt : 5 ,
deleteUserCnt : 5 ,
groupVersion : 10000 ,
},
wantRowsDeleted : 0 ,
wantErr : true ,
},
{
name : "zero-version" ,
args : args {
group : TestGroup ( t , conn , org . PublicId ),
createUserCnt : 5 ,
deleteUserCnt : 5 ,
groupVersion : 0 ,
},
wantRowsDeleted : 0 ,
wantErr : true ,
2020-12-23 11:00:00 -08:00
wantIsErr : errors . InvalidParameter ,
2020-07-15 13:12:15 -04:00
},
2020-07-10 19:01:23 -04:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
userIds := make ([] string , 0 , tt . args . createUserCnt )
for i := 0 ; i < tt . args . createUserCnt ; i ++ {
2020-08-19 16:47:18 -04:00
u := TestUser ( t , repo , org . PublicId )
2020-07-10 19:01:23 -04:00
userIds = append ( userIds , u . PublicId )
}
members , err := repo . AddGroupMembers ( context . Background (), tt . args . group . PublicId , 1 , userIds , tt . args . opt ... )
require . NoError ( err )
assert . Equal ( tt . args . createUserCnt , len ( members ))
deleteUserIds := make ([] string , 0 , tt . args . deleteUserCnt )
for i := 0 ; i < tt . args . deleteUserCnt ; i ++ {
deleteUserIds = append ( deleteUserIds , userIds [ i ])
}
var groupId string
switch {
case tt . args . groupIdOverride != nil :
groupId = * tt . args . groupIdOverride
default :
groupId = tt . args . group . PublicId
}
deletedRows , err := repo . DeleteGroupMembers ( context . Background (), groupId , tt . args . groupVersion , deleteUserIds , tt . args . opt ... )
if tt . wantErr {
assert . Error ( err )
assert . Equal ( 0 , deletedRows )
2021-01-12 08:50:19 -08:00
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "unexpected error %s" , err . Error ())
2020-07-10 19:01:23 -04:00
err = db . TestVerifyOplog ( t , rw , tt . args . group . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . Error ( err )
2020-12-21 10:07:34 -08:00
assert . True ( errors . Match ( errors . T ( errors . RecordNotFound ), err ))
2020-07-10 19:01:23 -04:00
return
}
require . NoError ( err )
assert . Equal ( tt . wantRowsDeleted , deletedRows )
err = db . TestVerifyOplog ( t , rw , tt . args . group . PublicId , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE ), db . WithCreateNotBefore ( 10 * time . Second ))
assert . NoError ( err )
})
}
}
func TestRepository_SetGroupMembers ( t * testing . T ) {
t . Parallel ()
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
repo := TestRepo ( t , conn , wrapper )
2020-07-10 19:01:23 -04:00
2020-08-19 16:47:18 -04:00
org , proj := TestScopes ( t , repo )
testUser := TestUser ( t , repo , org . PublicId )
2020-07-10 19:01:23 -04:00
createUsersFn := func () [] string {
results := [] string {}
for i := 0 ; i < 5 ; i ++ {
2020-08-19 16:47:18 -04:00
u := TestUser ( t , repo , org . PublicId )
2020-07-10 19:01:23 -04:00
results = append ( results , u . PublicId )
}
return results
}
setupFn := func ( groupId string ) [] string {
users := createUsersFn ()
_ , err := repo . AddGroupMembers ( context . Background (), groupId , 1 , users )
require . NoError ( t , err )
return users
}
type args struct {
group * Group
groupVersion uint32
userIds [] string
addToOrigUsers bool
opt [] Option
}
tests := [] struct {
name string
setup func ( string ) [] string
args args
wantAffectedRows int
wantErr bool
}{
{
name : "clear" ,
setup : setupFn ,
args : args {
group : TestGroup ( t , conn , proj . PublicId ),
groupVersion : 2 , // yep, since setupFn will increment it to 2
userIds : [] string {},
},
wantErr : false ,
wantAffectedRows : 5 ,
},
{
name : "no change" ,
setup : setupFn ,
args : args {
group : TestGroup ( t , conn , proj . PublicId ),
groupVersion : 2 , // yep, since setupFn will increment it to 2
userIds : [] string {},
addToOrigUsers : true ,
},
wantErr : false ,
wantAffectedRows : 0 ,
},
{
name : "add users" ,
setup : setupFn ,
args : args {
group : TestGroup ( t , conn , proj . PublicId ),
groupVersion : 2 , // yep, since setupFn will increment it to 2
userIds : [] string { testUser . PublicId },
addToOrigUsers : true ,
},
wantErr : false ,
wantAffectedRows : 1 ,
},
{
name : "remove existing and add users" ,
setup : setupFn ,
args : args {
group : TestGroup ( t , conn , proj . PublicId ),
groupVersion : 2 , // yep, since setupFn will increment it to 2
userIds : [] string { testUser . PublicId },
addToOrigUsers : false ,
},
wantErr : false ,
wantAffectedRows : 6 ,
},
2020-07-15 13:12:15 -04:00
{
name : "bad version" ,
setup : setupFn ,
args : args {
group : TestGroup ( t , conn , proj . PublicId ),
groupVersion : 1000 ,
userIds : [] string { testUser . PublicId },
addToOrigUsers : true ,
},
wantErr : true ,
wantAffectedRows : 0 ,
},
{
name : "zero version" ,
setup : setupFn ,
args : args {
group : TestGroup ( t , conn , proj . PublicId ),
groupVersion : 0 ,
userIds : [] string { testUser . PublicId },
addToOrigUsers : true ,
},
wantErr : true ,
wantAffectedRows : 0 ,
},
2020-07-10 19:01:23 -04:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
var origUsers [] string
if tt . setup != nil {
origUsers = tt . setup ( tt . args . group . PublicId )
}
setUsers := tt . args . userIds
if tt . args . addToOrigUsers {
setUsers = append ( setUsers , origUsers ... )
}
got , affectedRows , err := repo . SetGroupMembers ( context . Background (), tt . args . group . PublicId , tt . args . groupVersion , setUsers , tt . args . opt ... )
if tt . wantErr {
require . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tt . wantAffectedRows , affectedRows )
var gotIds [] string
for _ , r := range got {
gotIds = append ( gotIds , r . GetMemberId ())
}
var wantIds [] string
wantIds = append ( wantIds , tt . args . userIds ... )
sort . Strings ( wantIds )
sort . Strings ( gotIds )
assert . Equal ( wantIds , wantIds )
})
}
}
2023-12-21 12:54:27 -08:00
func Test_listGroupDeletedIds ( t * testing . T ) {
t . Parallel ()
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
repo := TestRepo ( t , conn , wrapper )
org , _ := TestScopes ( t , repo )
r := TestGroup ( t , conn , org . GetPublicId ())
// Expect no entries at the start
deletedIds , ttime , err := repo . listGroupDeletedIds ( ctx , time . Now (). AddDate ( - 1 , 0 , 0 ))
require . NoError ( t , err )
require . Empty ( t , deletedIds )
// Transaction timestamp should be within ~10 seconds of now
assert . True ( t , time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( t , time . Now (). After ( ttime . Add ( - 10 * time . Second )))
// Delete a group
_ , err = repo . DeleteGroup ( ctx , r . GetPublicId ())
require . NoError ( t , err )
// Expect a single entry
deletedIds , ttime , err = repo . listGroupDeletedIds ( ctx , time . Now (). AddDate ( - 1 , 0 , 0 ))
require . NoError ( t , err )
require . Equal ( t , [] string { r . GetPublicId ()}, deletedIds )
// Transaction timestamp should be within ~10 seconds of now
assert . True ( t , time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( t , time . Now (). After ( ttime . Add ( - 10 * time . Second )))
// Try again with the time set to now, expect no entries
deletedIds , ttime , err = repo . listGroupDeletedIds ( ctx , time . Now ())
require . NoError ( t , err )
require . Empty ( t , deletedIds )
// Transaction timestamp should be within ~10 seconds of now
assert . True ( t , time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( t , time . Now (). After ( ttime . Add ( - 10 * time . Second )))
}
func Test_estimatedGroupCount ( t * testing . T ) {
t . Parallel ()
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
sqlDb , err := conn . SqlDB ( ctx )
require . NoError ( t , err )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
kms := kms . TestKms ( t , conn , wrapper )
repo , err := NewRepository ( ctx , rw , rw , kms )
require . NoError ( t , err )
// Run analyze to update estimate
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
// Check total entries at start, expect 0
numItems , err := repo . estimatedGroupCount ( ctx )
require . NoError ( t , err )
assert . Equal ( t , 0 , numItems )
iamRepo := TestRepo ( t , conn , wrapper )
org , _ := TestScopes ( t , iamRepo )
// Create a group, expect 1 entry
u := TestGroup ( t , conn , org . GetPublicId ())
// Run analyze to update estimate
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
numItems , err = repo . estimatedGroupCount ( ctx )
require . NoError ( t , err )
assert . Equal ( t , 1 , numItems )
// Delete the group, expect 3 again
_ , err = repo . DeleteGroup ( ctx , u . GetPublicId ())
require . NoError ( t , err )
// Run analyze to update estimate
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
numItems , err = repo . estimatedGroupCount ( ctx )
require . NoError ( t , err )
assert . Equal ( t , 0 , numItems )
}