2020-07-11 12:00:49 -04:00
// Copyright (c) HashiCorp, Inc.
2020-09-10 16:37:45 -04:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-07-07 07:44:49 -07:00
package authtoken
import (
"context"
2020-07-27 14:06:59 -07:00
"sort"
2020-07-07 07:44:49 -07:00
"testing"
"time"
"github.com/google/go-cmp/cmp"
2023-03-10 15:06:39 -05:00
"github.com/hashicorp/boundary/globals"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/auth/password"
2021-04-08 20:54:32 -04:00
"github.com/hashicorp/boundary/internal/db/timestamp"
2020-08-19 16:47:18 -04:00
"github.com/hashicorp/boundary/internal/kms"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/oplog"
2020-07-07 07:44:49 -07:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"
2024-02-14 17:14:31 -05:00
"google.golang.org/protobuf/types/known/timestamppb"
2020-07-07 07:44:49 -07:00
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/authtoken/store"
"github.com/hashicorp/boundary/internal/db"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/iam"
2020-07-07 07:44:49 -07:00
)
func TestRepository_New ( t * testing . T ) {
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kmsCache := kms . TestKms ( t , conn , wrapper )
2020-07-07 07:44:49 -07:00
type args struct {
2020-08-19 16:47:18 -04:00
r db . Reader
w db . Writer
kms * kms . Kms
opts [] Option
2020-07-07 07:44:49 -07:00
}
2021-01-07 10:02:32 -05:00
tests := [] struct {
2021-02-03 17:34:46 -06:00
name string
args args
want * Repository
wantIsErr errors . Code
wantErrMsg string
2020-07-07 07:44:49 -07:00
}{
{
2020-07-23 09:53:37 -07:00
name : "valid default limit" ,
2020-11-16 10:39:18 -08:00
args : args {
r : rw ,
w : rw ,
kms : kmsCache ,
opts : [] Option {},
},
want : & Repository {
reader : rw ,
writer : rw ,
kms : kmsCache ,
limit : db . DefaultLimit ,
timeToLiveDuration : defaultTokenTimeToLiveDuration ,
timeToStaleDuration : defaultTokenTimeToStaleDuration ,
},
},
{
name : "valid new limit" ,
2020-07-07 07:44:49 -07:00
args : args {
2020-08-19 16:47:18 -04:00
r : rw ,
w : rw ,
kms : kmsCache ,
2020-11-16 10:39:18 -08:00
opts : [] Option {
WithLimit ( 5 ),
},
2020-07-07 07:44:49 -07:00
},
want : & Repository {
2020-11-16 10:39:18 -08:00
reader : rw ,
writer : rw ,
kms : kmsCache ,
limit : 5 ,
timeToLiveDuration : defaultTokenTimeToLiveDuration ,
timeToStaleDuration : defaultTokenTimeToStaleDuration ,
2020-07-23 09:53:37 -07:00
},
},
{
2020-11-16 10:39:18 -08:00
name : "valid token time to live" ,
2020-07-23 09:53:37 -07:00
args : args {
2020-11-16 10:39:18 -08:00
r : rw ,
w : rw ,
kms : kmsCache ,
opts : [] Option {
WithTokenTimeToLiveDuration ( 1 * time . Hour ),
},
2020-07-23 09:53:37 -07:00
},
want : & Repository {
2020-11-16 10:39:18 -08:00
reader : rw ,
writer : rw ,
kms : kmsCache ,
limit : db . DefaultLimit ,
timeToLiveDuration : 1 * time . Hour ,
timeToStaleDuration : defaultTokenTimeToStaleDuration ,
2020-07-07 07:44:49 -07:00
},
},
2020-11-16 10:39:18 -08:00
{
name : "valid token time to stale" ,
args : args {
r : rw ,
w : rw ,
kms : kmsCache ,
opts : [] Option {
WithTokenTimeToStaleDuration ( 1 * time . Hour ),
},
},
want : & Repository {
reader : rw ,
writer : rw ,
kms : kmsCache ,
limit : db . DefaultLimit ,
timeToStaleDuration : 1 * time . Hour ,
timeToLiveDuration : defaultTokenTimeToLiveDuration ,
},
},
2020-07-07 07:44:49 -07:00
{
name : "nil-reader" ,
args : args {
2020-08-19 16:47:18 -04:00
r : nil ,
w : rw ,
kms : kmsCache ,
2020-07-07 07:44:49 -07:00
},
2021-02-03 17:34:46 -06:00
want : nil ,
wantIsErr : errors . InvalidParameter ,
wantErrMsg : "authtoken.NewRepository: nil db reader: parameter violation: error #100" ,
2020-07-07 07:44:49 -07:00
},
{
name : "nil-writer" ,
args : args {
2020-08-19 16:47:18 -04:00
r : rw ,
w : nil ,
kms : kmsCache ,
2020-07-07 07:44:49 -07:00
},
2021-02-03 17:34:46 -06:00
want : nil ,
wantIsErr : errors . InvalidParameter ,
wantErrMsg : "authtoken.NewRepository: nil db writer: parameter violation: error #100" ,
2020-07-07 07:44:49 -07:00
},
{
2020-08-19 16:47:18 -04:00
name : "nil-kms" ,
2020-07-07 07:44:49 -07:00
args : args {
2020-08-19 16:47:18 -04:00
r : rw ,
w : rw ,
kms : nil ,
2020-07-07 07:44:49 -07:00
},
2021-02-03 17:34:46 -06:00
want : nil ,
wantIsErr : errors . InvalidParameter ,
wantErrMsg : "authtoken.NewRepository: nil kms: parameter violation: error #100" ,
2020-07-07 07:44:49 -07:00
},
{
name : "all-nils" ,
args : args {
2020-08-19 16:47:18 -04:00
r : nil ,
w : nil ,
kms : nil ,
2020-07-07 07:44:49 -07:00
},
2021-02-03 17:34:46 -06:00
want : nil ,
wantIsErr : errors . InvalidParameter ,
wantErrMsg : "authtoken.NewRepository: nil db reader: parameter violation: error #100" ,
2020-07-07 07:44:49 -07:00
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert := assert . New ( t )
2023-06-06 17:09:08 -04:00
got , err := NewRepository ( context . Background (), tt . args . r , tt . args . w , tt . args . kms , tt . args . opts ... )
2021-02-03 17:34:46 -06:00
if tt . wantIsErr != 0 {
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "Unexpected error %s" , err )
assert . Equal ( tt . wantErrMsg , err . Error ())
2020-07-07 07:44:49 -07:00
return
}
assert . NoError ( err )
assert . NotNil ( got )
assert . Equal ( tt . want , got )
})
}
}
func TestRepository_CreateAuthToken ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-07 07:44:49 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kms := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
2020-07-07 07:44:49 -07:00
2020-08-19 16:47:18 -04:00
org1 , _ := iam . TestScopes ( t , repo )
2020-07-31 11:43:03 -07:00
am := password . TestAuthMethods ( t , conn , org1 . GetPublicId (), 1 )[ 0 ]
2021-04-20 20:33:56 -04:00
aAcct := password . TestAccount ( t , conn , am . GetPublicId (), "name1" )
2021-04-08 20:54:32 -04:00
iam . TestSetPrimaryAuthMethod ( t , repo , org1 , am . PublicId )
u1 := iam . TestUser ( t , repo , org1 . PublicId , iam . WithAccountIds ( aAcct . PublicId ))
2020-07-07 07:44:49 -07:00
2020-08-19 16:47:18 -04:00
org2 , _ := iam . TestScopes ( t , repo )
u2 := iam . TestUser ( t , repo , org2 . GetPublicId ())
2020-07-07 07:44:49 -07:00
2023-06-06 17:09:08 -04:00
testId , err := NewAuthTokenId ( ctx )
2021-04-08 20:54:32 -04:00
require . NoError ( t , err )
2021-01-07 10:02:32 -05:00
tests := [] struct {
2020-07-07 07:44:49 -07:00
name string
2020-08-19 16:47:18 -04:00
iamUser * iam . User
2020-07-07 07:44:49 -07:00
authAcctId string
2021-04-08 20:54:32 -04:00
opt [] Option
2020-07-07 07:44:49 -07:00
want * AuthToken
wantErr bool
}{
{
name : "valid" ,
2020-08-19 16:47:18 -04:00
iamUser : u1 ,
2020-07-07 07:44:49 -07:00
authAcctId : aAcct . GetPublicId (),
want : & AuthToken {
AuthToken : & store . AuthToken {
AuthAccountId : aAcct . GetPublicId (),
},
},
},
2021-04-08 20:54:32 -04:00
{
name : "WithPublicId-WithStatus" ,
iamUser : u1 ,
authAcctId : aAcct . GetPublicId (),
opt : [] Option { WithPublicId ( testId ), WithStatus ( PendingStatus )},
want : & AuthToken {
AuthToken : & store . AuthToken {
AuthAccountId : aAcct . GetPublicId (),
PublicId : testId ,
Status : string ( PendingStatus ),
},
},
},
2020-07-07 07:44:49 -07:00
{
name : "unconnected-authaccount-user" ,
2020-08-19 16:47:18 -04:00
iamUser : u2 ,
2020-07-07 07:44:49 -07:00
authAcctId : aAcct . GetPublicId (),
wantErr : true ,
},
{
2020-08-19 16:47:18 -04:00
name : "no-authacctid" ,
iamUser : u1 ,
wantErr : true ,
2020-07-07 07:44:49 -07:00
},
{
name : "no-userid" ,
authAcctId : aAcct . GetPublicId (),
wantErr : true ,
},
{
name : "invalid-authacctid" ,
2020-08-19 16:47:18 -04:00
iamUser : u1 ,
2020-07-07 07:44:49 -07:00
authAcctId : "this_is_invalid" ,
wantErr : true ,
},
{
name : "invalid-userid" ,
2020-08-19 16:47:18 -04:00
iamUser : func () * iam . User { u := u1 . Clone ().( * iam . User ); u . PublicId = "this_is_invalid" ; return u }(),
2020-07-07 07:44:49 -07:00
authAcctId : aAcct . GetPublicId (),
wantErr : true ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms )
2020-07-07 07:44:49 -07:00
require . NoError ( err )
require . NotNil ( repo )
2023-06-06 17:09:08 -04:00
got , err := repo . CreateAuthToken ( ctx , tt . iamUser , tt . authAcctId , tt . opt ... )
2020-07-07 07:44:49 -07:00
if tt . wantErr {
assert . Error ( err )
assert . Nil ( got )
return
}
2020-08-19 16:47:18 -04:00
require . NoError ( err , "Got error for CreateAuthToken(ctx, %v, %v)" , tt . iamUser , tt . authAcctId )
2020-07-07 07:44:49 -07:00
assert . NotNil ( got )
2023-03-10 15:06:39 -05:00
db . AssertPublicId ( t , globals . AuthTokenPrefix , got . PublicId )
2020-07-07 07:44:49 -07:00
assert . Equal ( tt . authAcctId , got . GetAuthAccountId ())
assert . Equal ( got . CreateTime , got . UpdateTime )
assert . Equal ( got . CreateTime , got . ApproximateLastAccessTime )
2021-04-08 20:54:32 -04:00
opts := getOpts ( tt . opt ... )
if opts . withPublicId != "" {
assert . Equal ( opts . withPublicId , got . PublicId )
}
if opts . withStatus != "" {
assert . Equal ( string ( opts . withStatus ), got . Status )
}
2020-09-10 16:37:45 -04:00
// We should find no oplog since tokens are not replicated, so they don't need oplog entries.
assert . Error ( db . TestVerifyOplog ( t , rw , got . GetPublicId (), db . WithOperation ( oplog . OpType_OP_TYPE_CREATE )))
2020-07-07 07:44:49 -07:00
})
}
}
func TestRepository_LookupAuthToken ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-07 07:44:49 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kms := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
at := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2020-07-07 07:44:49 -07:00
at . Token = ""
at . CtToken = nil
2020-08-19 16:47:18 -04:00
at . KeyId = ""
2020-07-07 07:44:49 -07:00
2023-06-06 17:09:08 -04:00
badId , err := NewAuthTokenId ( ctx )
2020-07-07 07:44:49 -07:00
require . NoError ( t , err )
require . NotNil ( t , badId )
2021-01-07 10:02:32 -05:00
tests := [] struct {
2021-02-03 17:34:46 -06:00
name string
id string
want * AuthToken
wantIsErr errors . Code
wantErrMsg string
2020-07-07 07:44:49 -07:00
}{
{
name : "found" ,
id : at . GetPublicId (),
want : at ,
},
{
name : "not-found" ,
id : badId ,
want : nil ,
},
{
2021-02-03 17:34:46 -06:00
name : "bad-public-id" ,
id : "" ,
want : nil ,
2021-02-11 13:32:45 -06:00
wantIsErr : errors . InvalidPublicId ,
wantErrMsg : "authtoken.(Repository).LookupAuthToken: missing public id: parameter violation: error #102" ,
2020-07-07 07:44:49 -07:00
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms )
2020-07-07 07:44:49 -07:00
require . NoError ( err )
require . NotNil ( repo )
2023-06-06 17:09:08 -04:00
got , err := repo . LookupAuthToken ( ctx , tt . id )
2021-02-03 17:34:46 -06:00
if tt . wantIsErr != 0 {
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "Unexpected error %s" , err )
assert . Equal ( tt . wantErrMsg , err . Error ())
2020-07-07 07:44:49 -07:00
return
}
assert . NoError ( err )
if got == nil {
assert . Nil ( tt . want )
return
}
require . NotNil ( tt . want , "got %v, wanted nil" , got )
2020-07-11 12:00:49 -04:00
// TODO: This test fails by a very small amount -- 500 nanos ish in
// my experience -- if they are required to be equal. I think this
// is because the resolution of the timestamp in the db does not
// match the resolution in Go code. But might be worth checking
// into.
2024-02-13 10:54:48 -05:00
wantGoTimeExpr := tt . want . AuthToken . GetExpirationTime (). Timestamp . AsTime ()
gotGoTimeExpr := got . AuthToken . GetExpirationTime (). Timestamp . AsTime ()
2020-07-11 12:00:49 -04:00
assert . WithinDuration ( wantGoTimeExpr , gotGoTimeExpr , time . Millisecond )
tt . want . AuthToken . ExpirationTime = got . AuthToken . ExpirationTime
2020-07-07 07:44:49 -07:00
assert . Empty ( cmp . Diff ( tt . want . AuthToken , got . AuthToken , protocmp . Transform ()))
})
}
}
func TestRepository_ValidateToken ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-07 07:44:49 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
lastAccessedUpdateDuration = 0
2020-09-10 16:37:45 -04:00
timeSkew = 20 * time . Millisecond
2020-07-07 07:44:49 -07:00
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kms := kms . TestKms ( t , conn , wrapper )
iamRepo := iam . TestRepo ( t , conn , wrapper )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms )
2020-11-16 10:39:18 -08:00
2020-07-07 07:44:49 -07:00
require . NoError ( t , err )
require . NotNil ( t , repo )
2020-08-19 16:47:18 -04:00
org , _ := iam . TestScopes ( t , iamRepo )
at := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2020-07-07 07:44:49 -07:00
atToken := at . GetToken ()
at . Token = ""
at . CtToken = nil
2020-08-19 16:47:18 -04:00
at . KeyId = ""
2024-02-13 10:54:48 -05:00
atTime := at . GetApproximateLastAccessTime ()
2020-07-07 07:44:49 -07:00
require . NotNil ( t , atTime )
2023-06-06 17:09:08 -04:00
badId , err := NewAuthTokenId ( ctx )
2020-07-07 07:44:49 -07:00
require . NoError ( t , err )
require . NotNil ( t , badId )
2023-06-06 17:09:08 -04:00
badToken , err := newAuthToken ( ctx )
2020-07-07 07:44:49 -07:00
require . NoError ( t , err )
require . NotNil ( t , badToken )
2021-01-07 10:02:32 -05:00
tests := [] struct {
2021-02-03 17:34:46 -06:00
name string
id string
token string
want * AuthToken
wantIsErr errors . Code
wantErrMsg string
2020-07-07 07:44:49 -07:00
}{
{
name : "exists" ,
id : at . GetPublicId (),
token : atToken ,
want : at ,
},
{
name : "doesnt-exist" ,
id : badId ,
2021-02-10 13:52:42 -05:00
token : badToken . Token ,
2020-07-07 07:44:49 -07:00
want : nil ,
},
{
2021-02-03 17:34:46 -06:00
name : "empty-token" ,
id : at . GetPublicId (),
token : "" ,
want : nil ,
wantIsErr : errors . InvalidParameter ,
wantErrMsg : "authtoken.(Repository).ValidateToken: missing token: parameter violation: error #100" ,
2020-07-07 07:44:49 -07:00
},
{
2021-02-03 17:34:46 -06:00
name : "mismatched-token" ,
id : at . GetPublicId (),
2021-02-10 13:52:42 -05:00
token : badToken . Token ,
2021-02-03 17:34:46 -06:00
want : nil ,
wantIsErr : errors . Unknown ,
2020-07-07 07:44:49 -07:00
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
got , err := repo . ValidateToken ( ctx , tt . id , tt . token )
2020-09-10 16:37:45 -04:00
2021-02-03 17:34:46 -06:00
if tt . wantIsErr != 0 {
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "Unexpected error %s" , err )
assert . Equal ( tt . wantErrMsg , err . Error ())
2020-07-07 07:44:49 -07:00
return
}
assert . NoError ( err )
if got == nil {
assert . Nil ( tt . want )
// No need to compare updated time if we didn't get an initial auth token to compare against.
return
}
require . NotNil ( tt . want , "Got %v but wanted nil" , got )
2020-07-11 12:00:49 -04:00
// NOTE: See comment in LookupAuthToken about this logic
2024-02-14 17:14:31 -05:00
wantGoTimeExpr := tt . want . AuthToken . GetExpirationTime (). AsTime ()
gotGoTimeExpr := got . AuthToken . GetExpirationTime (). AsTime ()
2020-07-11 12:00:49 -04:00
assert . WithinDuration ( wantGoTimeExpr , gotGoTimeExpr , time . Millisecond )
tt . want . AuthToken . ExpirationTime = got . AuthToken . ExpirationTime
2020-07-07 07:44:49 -07:00
assert . Empty ( cmp . Diff ( tt . want . AuthToken , got . AuthToken , protocmp . Transform ()))
2020-07-11 12:00:49 -04:00
2020-07-07 07:44:49 -07:00
// preTime1 should be the value prior to the ValidateToken was called so it should equal creation time
2024-02-13 10:54:48 -05:00
preTime1 := got . GetApproximateLastAccessTime ()
2020-07-07 07:44:49 -07:00
require . NoError ( err )
2024-02-13 10:54:48 -05:00
assert . True ( preTime1 . AsTime (). Equal ( atTime . AsTime ()), "Create time %q doesn't match the time from the first call to MaybeUpdateLastAccesssed: %q." , atTime , preTime1 )
2020-07-07 07:44:49 -07:00
// Enable the duration which limits how frequently a token's approximate last accessed time can be updated
// so the next call doesn't cause the last accessed time to be updated.
lastAccessedUpdateDuration = 1 * time . Hour
2023-06-06 17:09:08 -04:00
got2 , err := repo . ValidateToken ( ctx , tt . id , tt . token )
2020-07-07 07:44:49 -07:00
assert . NoError ( err )
2024-02-13 10:54:48 -05:00
preTime2 := got2 . GetApproximateLastAccessTime (). GetTimestamp ()
assert . True ( preTime2 . AsTime (). After ( preTime1 . AsTime ()), "First updated time %q was not after the creation time %q" , preTime2 , preTime1 )
2020-07-07 07:44:49 -07:00
2020-09-10 16:37:45 -04:00
// We should find no oplog since tokens are not replicated, so they don't need oplog entries.
assert . Error ( db . TestVerifyOplog ( t , rw , got . GetPublicId (), db . WithOperation ( oplog . OpType_OP_TYPE_UPDATE )))
2020-07-07 07:44:49 -07:00
2023-06-06 17:09:08 -04:00
got3 , err := repo . ValidateToken ( ctx , tt . id , tt . token )
2020-09-10 16:37:45 -04:00
require . NoError ( err )
2024-02-13 10:54:48 -05:00
preTime3 := got3 . GetApproximateLastAccessTime ()
assert . True ( preTime3 . AsTime (). Equal ( preTime2 . AsTime ()), "The 3rd timestamp %q was not equal to the second time %q" , preTime3 , preTime2 )
2020-07-07 07:44:49 -07:00
})
}
}
func TestRepository_ValidateToken_expired ( t * testing . T ) {
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kms := kms . TestKms ( t , conn , wrapper )
iamRepo := iam . TestRepo ( t , conn , wrapper )
2020-07-07 07:44:49 -07:00
2020-08-19 16:47:18 -04:00
org , _ := iam . TestScopes ( t , iamRepo )
baseAT := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2020-07-07 07:44:49 -07:00
baseAT . GetAuthAccountId ()
2020-08-10 15:17:15 -04:00
aAcct := allocAuthAccount ()
aAcct . PublicId = baseAT . GetAuthAccountId ()
2020-07-07 07:44:49 -07:00
require . NoError ( t , rw . LookupByPublicId ( context . Background (), aAcct ))
2020-09-25 13:35:32 -04:00
iamUser , _ , err := iamRepo . LookupUser ( context . Background (), aAcct . GetIamUserId ())
2020-08-19 16:47:18 -04:00
require . NoError ( t , err )
require . NotNil ( t , iamUser )
2020-07-07 07:44:49 -07:00
2021-01-07 10:02:32 -05:00
tests := [] struct {
2020-07-07 07:44:49 -07:00
name string
staleDuration time . Duration
expirationDuration time . Duration
wantReturned bool
}{
{
name : "not-stale-or-expired" ,
2020-11-16 10:39:18 -08:00
staleDuration : defaultTokenTimeToStaleDuration ,
expirationDuration : defaultTokenTimeToLiveDuration ,
2020-07-07 07:44:49 -07:00
wantReturned : true ,
},
{
name : "stale" ,
2020-11-16 10:39:18 -08:00
staleDuration : 1 * time . Millisecond ,
expirationDuration : defaultTokenTimeToLiveDuration ,
2020-07-07 07:44:49 -07:00
wantReturned : false ,
},
{
name : "expired" ,
2020-11-16 10:39:18 -08:00
staleDuration : defaultTokenTimeToStaleDuration ,
expirationDuration : 1 * time . Millisecond ,
2020-07-07 07:44:49 -07:00
wantReturned : false ,
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-07 07:44:49 -07:00
2020-09-10 16:37:45 -04:00
timeSkew = 20 * time . Millisecond
2020-07-07 07:44:49 -07:00
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms ,
2020-11-16 10:39:18 -08:00
WithTokenTimeToLiveDuration ( tt . expirationDuration ),
WithTokenTimeToStaleDuration ( tt . staleDuration ))
require . NoError ( err )
require . NotNil ( repo )
2020-08-19 16:47:18 -04:00
at , err := repo . CreateAuthToken ( ctx , iamUser , baseAT . GetAuthAccountId ())
2020-07-07 07:44:49 -07:00
require . NoError ( err )
got , err := repo . ValidateToken ( ctx , at . GetPublicId (), at . GetToken ())
require . NoError ( err )
if tt . wantReturned {
assert . NotNil ( got )
} else {
2020-09-10 16:37:45 -04:00
// We should find no oplog since tokens are not replicated, so they don't need oplog entries.
assert . Error ( db . TestVerifyOplog ( t , rw , at . GetPublicId (), db . WithOperation ( oplog . OpType_OP_TYPE_DELETE )))
2020-07-07 07:44:49 -07:00
assert . Nil ( got )
}
})
}
}
func TestRepository_DeleteAuthToken ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-07 07:44:49 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kms := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
at := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2023-06-06 17:09:08 -04:00
badId , err := NewAuthTokenId ( ctx )
2020-07-07 07:44:49 -07:00
require . NoError ( t , err )
require . NotNil ( t , badId )
2021-01-07 10:02:32 -05:00
tests := [] struct {
2021-02-03 17:34:46 -06:00
name string
id string
want int
wantIsErr errors . Code
wantErrMsg string
2020-07-07 07:44:49 -07:00
}{
{
name : "found" ,
id : at . GetPublicId (),
want : 1 ,
},
{
name : "not-found" ,
id : badId ,
want : 0 ,
},
{
2021-02-03 17:34:46 -06:00
name : "empty-public-id" ,
id : "" ,
want : 0 ,
2021-02-11 13:32:45 -06:00
wantIsErr : errors . InvalidPublicId ,
wantErrMsg : "authtoken.(Repository).DeleteAuthToken: missing public id: parameter violation: error #102" ,
2020-07-07 07:44:49 -07:00
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms )
2020-07-07 07:44:49 -07:00
require . NoError ( err )
require . NotNil ( repo )
2023-06-06 17:09:08 -04:00
got , err := repo . DeleteAuthToken ( ctx , tt . id )
2021-02-03 17:34:46 -06:00
if tt . wantIsErr != 0 {
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "Unexpected error %s" , err )
assert . Equal ( tt . wantErrMsg , err . Error ())
2020-07-07 07:44:49 -07:00
return
}
assert . NoError ( err )
assert . Equal ( tt . want , got , "row count" )
if tt . want != 0 {
2020-09-10 16:37:45 -04:00
// We should find no oplog since tokens are not replicated, so they don't need oplog entries.
assert . Error ( db . TestVerifyOplog ( t , rw , tt . id , db . WithOperation ( oplog . OpType_OP_TYPE_DELETE )))
2020-07-07 07:44:49 -07:00
}
})
}
}
2020-07-23 09:53:37 -07:00
func TestRepository_ListAuthTokens ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-23 09:53:37 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
kms := kms . TestKms ( t , conn , wrapper )
repo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , repo )
at1 := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2020-07-23 09:53:37 -07:00
at1 . Token = ""
2020-08-19 16:47:18 -04:00
at1 . KeyId = ""
at2 := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2020-07-23 09:53:37 -07:00
at2 . Token = ""
2020-08-19 16:47:18 -04:00
at2 . KeyId = ""
at3 := TestAuthToken ( t , conn , kms , org . GetPublicId ())
2020-07-23 09:53:37 -07:00
at3 . Token = ""
2020-08-19 16:47:18 -04:00
at3 . KeyId = ""
2020-07-23 09:53:37 -07:00
2020-08-19 16:47:18 -04:00
emptyOrg , _ := iam . TestScopes ( t , repo )
2020-07-23 09:53:37 -07:00
2021-01-07 10:02:32 -05:00
tests := [] struct {
2023-12-07 17:01:15 -05:00
name string
orgId string
want [] * AuthToken
wantTTime time . Time
2020-07-23 09:53:37 -07:00
}{
{
2023-12-07 17:01:15 -05:00
name : "populated" ,
orgId : org . GetPublicId (),
want : [] * AuthToken { at1 , at2 , at3 },
wantTTime : time . Now (),
2020-07-23 09:53:37 -07:00
},
{
2023-12-07 17:01:15 -05:00
name : "empty" ,
orgId : emptyOrg . GetPublicId (),
want : [] * AuthToken {},
wantTTime : time . Now (),
2020-07-23 09:53:37 -07:00
},
{
2023-12-07 17:01:15 -05:00
name : "empty-org-id" ,
orgId : "" ,
want : [] * AuthToken {},
wantTTime : time . Now (),
2020-07-23 09:53:37 -07:00
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms )
2020-07-23 09:53:37 -07:00
require . NoError ( err )
require . NotNil ( repo )
2023-12-07 17:01:15 -05:00
got , ttime , err := repo . listAuthTokens ( ctx , [] string { tt . orgId })
2020-07-23 09:53:37 -07:00
assert . NoError ( err )
2020-07-27 14:06:59 -07:00
sort . Slice ( tt . want , func ( i , j int ) bool { return tt . want [ i ]. PublicId < tt . want [ j ]. PublicId })
sort . Slice ( got , func ( i , j int ) bool { return got [ i ]. PublicId < got [ j ]. PublicId })
assert . Empty ( cmp . Diff ( tt . want , got , protocmp . Transform ()), "row count" )
2023-12-07 17:01:15 -05:00
// Transaction timestamp should be within ~10 seconds of now
assert . True ( tt . wantTTime . Before ( ttime . Add ( 10 * time . Second )))
assert . True ( tt . wantTTime . After ( ttime . Add ( - 10 * time . Second )))
2020-07-23 09:53:37 -07:00
})
}
2023-12-07 17:01:15 -05:00
t . Run ( "withStartPageAfter" , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
for i := 0 ; i < 7 ; i ++ {
at := TestAuthToken ( t , conn , kms , org . GetPublicId ())
at . Token = ""
at . KeyId = ""
}
repo , err := NewRepository ( ctx , rw , rw , kms )
require . NoError ( err )
page1 , ttime , err := repo . listAuthTokens ( ctx , [] string { org . GetPublicId ()}, WithLimit ( 2 ))
require . NoError ( err )
require . Len ( page1 , 2 )
// Transaction timestamp should be within ~10 seconds of now
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
page2 , ttime , err := repo . listAuthTokens ( ctx , [] string { org . GetPublicId ()}, WithLimit ( 2 ), WithStartPageAfterItem ( page1 [ 1 ]))
require . NoError ( err )
require . Len ( page2 , 2 )
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
for _ , item := range page1 {
assert . NotEqual ( item . GetPublicId (), page2 [ 0 ]. GetPublicId ())
assert . NotEqual ( item . GetPublicId (), page2 [ 1 ]. GetPublicId ())
}
page3 , ttime , err := repo . listAuthTokens ( ctx , [] string { org . GetPublicId ()}, WithLimit ( 2 ), WithStartPageAfterItem ( page2 [ 1 ]))
require . NoError ( err )
require . Len ( page3 , 2 )
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
for _ , item := range page2 {
assert . NotEqual ( item . GetPublicId (), page3 [ 0 ]. GetPublicId ())
assert . NotEqual ( item . GetPublicId (), page3 [ 1 ]. GetPublicId ())
}
page4 , ttime , err := repo . listAuthTokens ( ctx , [] string { org . GetPublicId ()}, WithLimit ( 2 ), WithStartPageAfterItem ( page3 [ 1 ]))
require . NoError ( err )
require . Len ( page4 , 2 )
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
for _ , item := range page3 {
assert . NotEqual ( item . GetPublicId (), page4 [ 0 ]. GetPublicId ())
assert . NotEqual ( item . GetPublicId (), page4 [ 1 ]. GetPublicId ())
}
page5 , ttime , err := repo . listAuthTokens ( ctx , [] string { org . GetPublicId ()}, WithLimit ( 2 ), WithStartPageAfterItem ( page4 [ 1 ]))
require . NoError ( err )
require . Len ( page5 , 2 )
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
for _ , item := range page4 {
assert . NotEqual ( item . GetPublicId (), page5 [ 0 ]. GetPublicId ())
assert . NotEqual ( item . GetPublicId (), page5 [ 1 ]. GetPublicId ())
}
page6 , ttime , err := repo . listAuthTokens ( ctx , [] string { org . GetPublicId ()}, WithLimit ( 2 ), WithStartPageAfterItem ( page5 [ 1 ]))
require . NoError ( err )
require . Empty ( page6 )
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
// Create 2 new auth tokens
newAt1 := TestAuthToken ( t , conn , kms , org . GetPublicId ())
newAt1 . Token = ""
newAt1 . KeyId = ""
newAt2 := TestAuthToken ( t , conn , kms , org . GetPublicId ())
newAt2 . Token = ""
newAt2 . KeyId = ""
// since it will return newest to oldest, we get page1[1] first
page7 , ttime , err := repo . listAuthTokensRefresh (
ctx ,
time . Now (). Add ( - 1 * time . Second ),
[] string { org . GetPublicId ()},
WithLimit ( 1 ),
)
require . NoError ( err )
require . Len ( page7 , 1 )
require . Equal ( page7 [ 0 ]. GetPublicId (), newAt2 . GetPublicId ())
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
page8 , ttime , err := repo . listAuthTokensRefresh (
context . Background (),
time . Now (). Add ( - 1 * time . Second ),
[] string { org . GetPublicId ()},
WithLimit ( 1 ),
WithStartPageAfterItem ( page7 [ 0 ]),
)
require . NoError ( err )
require . Len ( page8 , 1 )
require . Equal ( page8 [ 0 ]. GetPublicId (), newAt1 . GetPublicId ())
assert . True ( time . Now (). Before ( ttime . Add ( 10 * time . Second )))
assert . True ( time . Now (). After ( ttime . Add ( - 10 * time . Second )))
})
2020-07-23 09:53:37 -07:00
}
2021-01-25 19:37:40 -05:00
func TestRepository_ListAuthTokens_Multiple_Scopes ( t * testing . T ) {
t . Parallel ()
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2021-01-25 19:37:40 -05:00
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
kms := kms . TestKms ( t , conn , wrapper )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kms )
2021-01-25 19:37:40 -05:00
require . NoError ( t , err )
iamRepo := iam . TestRepo ( t , conn , wrapper )
org , proj := iam . TestScopes ( t , iamRepo )
const numPerScope = 10
var total int
for i := 0 ; i < numPerScope ; i ++ {
TestAuthToken ( t , conn , kms , "global" )
total ++
TestAuthToken ( t , conn , kms , org . GetPublicId ())
total ++
}
2023-12-07 17:01:15 -05:00
got , ttime , err := repo . listAuthTokens ( ctx , [] string { "global" , org . GetPublicId (), proj . GetPublicId ()})
2021-01-25 19:37:40 -05:00
require . NoError ( t , err )
2023-12-07 17:01:15 -05:00
assert . Equal ( t , total , len ( got )) // 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
}
2021-04-08 20:54:32 -04:00
func Test_IssuePendingToken ( t * testing . T ) {
t . Parallel ()
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
rootWrapper := db . TestWrapper ( t )
kmsCache := kms . TestKms ( t , conn , rootWrapper )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kmsCache )
2021-04-08 20:54:32 -04:00
require . NoError ( t , err )
org , _ := iam . TestScopes ( t , iam . TestRepo ( t , conn , rootWrapper ))
tests := [] struct {
name string
tokenRequestId string
wantErrMatch * errors . Template
wantErrContains string
}{
{
name : "missing-id" ,
wantErrMatch : errors . T ( errors . InvalidParameter ),
wantErrContains : "missing token request id" ,
},
{
name : "not-found" ,
tokenRequestId : func () string {
2023-06-06 17:09:08 -04:00
tokenPublicId , err := NewAuthTokenId ( ctx )
2021-04-08 20:54:32 -04:00
require . NoError ( t , err )
tk := TestAuthToken ( t , conn , kmsCache , org . PublicId , WithPublicId ( tokenPublicId ))
return tk . PublicId
}(),
wantErrMatch : errors . T ( errors . RecordNotFound ),
wantErrContains : "pending auth token" ,
},
{
name : "success" ,
tokenRequestId : func () string {
2023-06-06 17:09:08 -04:00
tokenPublicId , err := NewAuthTokenId ( ctx )
2021-04-08 20:54:32 -04:00
require . NoError ( t , err )
tk := TestAuthToken ( t , conn , kmsCache , org . PublicId , WithStatus ( PendingStatus ), WithPublicId ( tokenPublicId ))
return tk . PublicId
}(),
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
tk , err := repo . IssueAuthToken ( ctx , tt . tokenRequestId )
if tt . wantErrMatch != nil {
require . Error ( err )
assert . Truef ( errors . Match ( tt . wantErrMatch , err ), "wanted %s and got: %+v" , tt . wantErrMatch . Code , err )
if tt . wantErrContains != "" {
assert . Contains ( err . Error (), tt . wantErrContains )
}
return
}
require . NoError ( err )
assert . NotEmpty ( tk )
2024-02-13 10:54:48 -05:00
accessTime := tk . GetApproximateLastAccessTime ()
createTime := tk . GetCreateTime (). GetTimestamp ()
assert . True ( accessTime . AsTime (). After ( createTime . AsTime ()), "last access time %q was not after the creation time %q" , accessTime , createTime )
2021-04-08 20:54:32 -04:00
})
}
}
func Test_CloseExpiredPendingTokens ( t * testing . T ) {
t . Parallel ()
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
rootWrapper := db . TestWrapper ( t )
kmsCache := kms . TestKms ( t , conn , rootWrapper )
2023-06-06 17:09:08 -04:00
repo , err := NewRepository ( ctx , rw , rw , kmsCache )
2021-04-08 20:54:32 -04:00
require . NoError ( t , err )
2024-01-11 18:05:15 +00:00
iamRepo := iam . TestRepo ( t , conn , rootWrapper )
org , _ := iam . TestScopes ( t , iamRepo )
user := iam . TestUser ( t , iamRepo , org . GetPublicId ())
2021-04-08 20:54:32 -04:00
databaseWrapper , err := kmsCache . GetWrapper ( ctx , org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
createWith := func ( cnt int , expIn time . Duration , status Status ) {
authMethods := password . TestAuthMethods ( t , conn , org . PublicId , 1 )
authMethodId := authMethods [ 0 ]. PublicId
2021-04-20 20:33:56 -04:00
accts := password . TestMultipleAccounts ( t , conn , authMethodId , cnt )
2021-04-08 20:54:32 -04:00
for i := 0 ; i < cnt ; i ++ {
2024-01-11 18:05:15 +00:00
user , _ , err = iamRepo . LookupUser ( ctx , user . GetPublicId ())
require . NoError ( t , err )
2024-02-23 15:49:49 +00:00
_ , _ = iamRepo . AddUserAccounts ( ctx , user . GetPublicId (), user . GetVersion (), [] string { accts [ i ]. GetPublicId ()})
2024-01-11 18:05:15 +00:00
2021-04-08 20:54:32 -04:00
at := allocAuthToken ()
2023-06-06 17:09:08 -04:00
id , err := NewAuthTokenId ( ctx )
2021-04-08 20:54:32 -04:00
require . NoError ( t , err )
at . PublicId = id
2024-02-14 17:14:31 -05:00
exp := timestamppb . New ( time . Now (). Add ( expIn ). Truncate ( time . Second ))
2021-04-08 20:54:32 -04:00
at . ExpirationTime = & timestamp . Timestamp { Timestamp : exp }
at . Status = string ( status )
at . AuthAccountId = accts [ i ]. PublicId
2022-03-16 17:04:07 -04:00
keyId , err := databaseWrapper . KeyId ( ctx )
require . NoError ( t , err )
at . KeyId = keyId
2021-04-08 20:54:32 -04:00
at . CtToken = [] byte ( id )
err = rw . Create ( ctx , at )
require . NoError ( t , err )
}
}
tests := [] struct {
name string
setup func ()
wantCnt int
wantErrMatch * errors . Template
wantErrContains string
}{
{
name : "nada-todo" ,
},
{
name : "close-2" ,
setup : func () {
createWith ( 2 , - 10 * time . Second , PendingStatus )
createWith ( 1 , 10 * time . Second , IssuedStatus )
createWith ( 1 , 10 * time . Second , PendingStatus )
},
wantCnt : 2 ,
},
{
name : "close-zero" ,
setup : func () {
createWith ( 2 , 10 * time . Second , PendingStatus )
createWith ( 1 , 10 * time . Second , IssuedStatus )
},
wantCnt : 0 ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
// start with no tokens in the db.
_ , err := rw . Exec ( ctx , "delete from auth_token" , nil )
require . NoError ( err )
_ , err = rw . Exec ( ctx , "delete from auth_account" , nil )
require . NoError ( err )
if tt . setup != nil {
tt . setup ()
}
tokensClosed , err := repo . CloseExpiredPendingTokens ( ctx )
if tt . wantErrMatch != nil {
require . Error ( err )
assert . Equal ( 0 , tokensClosed )
assert . Truef ( errors . Match ( tt . wantErrMatch , err ), "wanted %s and got: %s" , tt . wantErrMatch . Code , err . Error ())
if tt . wantErrContains != "" {
assert . Contains ( err . Error (), tt . wantErrContains )
}
return
}
require . NoError ( err )
assert . Equal ( tt . wantCnt , tokensClosed )
})
}
}
2023-12-07 17:01:15 -05:00
func Test_listDeletedIds ( t * testing . T ) {
t . Parallel ()
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
wrapper := db . TestWrapper ( t )
kms := kms . TestKms ( t , conn , wrapper )
iamRepo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , iamRepo )
at := TestAuthToken ( t , conn , kms , org . GetPublicId ())
at . Token = ""
at . KeyId = ""
repo , err := NewRepository ( ctx , rw , rw , kms )
require . NoError ( t , err )
// Expect no entries at the start
deletedIds , ttime , err := repo . listDeletedIds ( 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 an auth token
_ , err = repo . DeleteAuthToken ( ctx , at . GetPublicId ())
require . NoError ( t , err )
// Expect a single entry
deletedIds , ttime , err = repo . listDeletedIds ( ctx , time . Now (). AddDate ( - 1 , 0 , 0 ))
require . NoError ( t , err )
require . Equal ( t , [] string { at . 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 . listDeletedIds ( 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_estimatedCount ( 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 . estimatedCount ( ctx )
require . NoError ( t , err )
assert . Equal ( t , 0 , numItems )
iamRepo := iam . TestRepo ( t , conn , wrapper )
org , _ := iam . TestScopes ( t , iamRepo )
// Create an auth token, expect 1 entry
at := TestAuthToken ( t , conn , kms , org . GetPublicId ())
at . Token = ""
at . KeyId = ""
// Run analyze to update estimate
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
numItems , err = repo . estimatedCount ( ctx )
require . NoError ( t , err )
assert . Equal ( t , 1 , numItems )
// Delete the auth token, expect 0 again
_ , err = repo . DeleteAuthToken ( ctx , at . GetPublicId ())
require . NoError ( t , err )
// Run analyze to update estimate
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
numItems , err = repo . estimatedCount ( ctx )
require . NoError ( t , err )
assert . Equal ( t , 0 , numItems )
}