2021-11-15 13:52:49 +01:00
package query
import (
2023-01-16 10:55:19 +01:00
"context"
2021-11-15 13:52:49 +01:00
"database/sql"
"database/sql/driver"
2023-12-08 16:30:55 +02:00
"errors"
2021-11-15 13:52:49 +01:00
"fmt"
"regexp"
"testing"
2023-01-16 10:55:19 +01:00
"github.com/DATA-DOG/go-sqlmock"
2023-02-27 22:36:43 +01:00
"github.com/zitadel/zitadel/internal/database"
2024-03-27 14:48:22 +01:00
db_mock "github.com/zitadel/zitadel/internal/database/mock"
2022-04-27 01:01:45 +02:00
"github.com/zitadel/zitadel/internal/domain"
2023-12-08 16:30:55 +02:00
"github.com/zitadel/zitadel/internal/zerrors"
2023-01-16 10:55:19 +01:00
)
var (
2023-09-07 06:54:51 +02:00
orgUniqueQuery = "SELECT COUNT(*) = 0 FROM projections.orgs1 LEFT JOIN projections.org_domains2 ON projections.orgs1.id = projections.org_domains2.org_id AND projections.orgs1.instance_id = projections.org_domains2.instance_id AS OF SYSTEM TIME '-1 ms' WHERE (projections.org_domains2.is_verified = $1 AND projections.orgs1.instance_id = $2 AND (projections.org_domains2.domain ILIKE $3 OR projections.orgs1.name ILIKE $4) AND projections.orgs1.org_state <> $5)"
2023-01-16 10:55:19 +01:00
orgUniqueCols = [ ] string { "is_unique" }
2023-02-27 22:36:43 +01:00
2023-09-07 06:54:51 +02:00
prepareOrgsQueryStmt = ` SELECT projections.orgs1.id, ` +
` projections.orgs1.creation_date, ` +
` projections.orgs1.change_date, ` +
` projections.orgs1.resource_owner, ` +
` projections.orgs1.org_state, ` +
` projections.orgs1.sequence, ` +
` projections.orgs1.name, ` +
` projections.orgs1.primary_domain, ` +
2023-02-27 22:36:43 +01:00
` COUNT(*) OVER () ` +
2023-09-07 06:54:51 +02:00
` FROM projections.orgs1 ` +
2023-02-27 22:36:43 +01:00
` AS OF SYSTEM TIME '-1 ms' `
prepareOrgsQueryCols = [ ] string {
"id" ,
"creation_date" ,
"change_date" ,
"resource_owner" ,
"org_state" ,
"sequence" ,
"name" ,
"primary_domain" ,
"count" ,
}
2023-09-07 06:54:51 +02:00
prepareOrgQueryStmt = ` SELECT projections.orgs1.id, ` +
` projections.orgs1.creation_date, ` +
` projections.orgs1.change_date, ` +
` projections.orgs1.resource_owner, ` +
` projections.orgs1.org_state, ` +
` projections.orgs1.sequence, ` +
` projections.orgs1.name, ` +
` projections.orgs1.primary_domain ` +
` FROM projections.orgs1 ` +
2023-02-27 22:36:43 +01:00
` AS OF SYSTEM TIME '-1 ms' `
prepareOrgQueryCols = [ ] string {
"id" ,
"creation_date" ,
"change_date" ,
"resource_owner" ,
"org_state" ,
"sequence" ,
"name" ,
"primary_domain" ,
}
prepareOrgUniqueStmt = ` SELECT COUNT(*) = 0 ` +
2023-09-07 06:54:51 +02:00
` FROM projections.orgs1 ` +
` LEFT JOIN projections.org_domains2 ON projections.orgs1.id = projections.org_domains2.org_id AND projections.orgs1.instance_id = projections.org_domains2.instance_id ` +
2023-02-27 22:36:43 +01:00
` AS OF SYSTEM TIME '-1 ms' `
prepareOrgUniqueCols = [ ] string {
"count" ,
}
2021-11-15 13:52:49 +01:00
)
func Test_OrgPrepares ( t * testing . T ) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
}
tests := [ ] struct {
name string
prepare interface { }
want want
object interface { }
} {
{
name : "prepareOrgsQuery no result" ,
prepare : prepareOrgsQuery ,
want : want {
sqlExpectations : mockQueries (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgsQueryStmt ) ,
2021-11-15 13:52:49 +01:00
nil ,
nil ,
) ,
} ,
object : & Orgs { Orgs : [ ] * Org { } } ,
} ,
{
name : "prepareOrgsQuery one result" ,
prepare : prepareOrgsQuery ,
want : want {
sqlExpectations : mockQueries (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgsQueryStmt ) ,
prepareOrgsQueryCols ,
2021-11-15 13:52:49 +01:00
[ ] [ ] driver . Value {
{
"id" ,
testNow ,
testNow ,
"ro" ,
domain . OrgStateActive ,
uint64 ( 20211109 ) ,
"org-name" ,
"zitadel.ch" ,
} ,
} ,
) ,
} ,
object : & Orgs {
SearchResponse : SearchResponse {
Count : 1 ,
} ,
Orgs : [ ] * Org {
{
ID : "id" ,
CreationDate : testNow ,
ChangeDate : testNow ,
ResourceOwner : "ro" ,
State : domain . OrgStateActive ,
Sequence : 20211109 ,
Name : "org-name" ,
Domain : "zitadel.ch" ,
} ,
} ,
} ,
} ,
{
name : "prepareOrgsQuery multiple result" ,
prepare : prepareOrgsQuery ,
want : want {
sqlExpectations : mockQueries (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgsQueryStmt ) ,
prepareOrgsQueryCols ,
2021-11-15 13:52:49 +01:00
[ ] [ ] driver . Value {
{
"id-1" ,
testNow ,
testNow ,
"ro" ,
domain . OrgStateActive ,
uint64 ( 20211108 ) ,
"org-name-1" ,
"zitadel.ch" ,
} ,
{
"id-2" ,
testNow ,
testNow ,
"ro" ,
domain . OrgStateActive ,
uint64 ( 20211108 ) ,
"org-name-2" ,
"caos.ch" ,
} ,
} ,
) ,
} ,
object : & Orgs {
SearchResponse : SearchResponse {
Count : 2 ,
} ,
Orgs : [ ] * Org {
{
ID : "id-1" ,
CreationDate : testNow ,
ChangeDate : testNow ,
ResourceOwner : "ro" ,
State : domain . OrgStateActive ,
Sequence : 20211108 ,
Name : "org-name-1" ,
Domain : "zitadel.ch" ,
} ,
{
ID : "id-2" ,
CreationDate : testNow ,
ChangeDate : testNow ,
ResourceOwner : "ro" ,
State : domain . OrgStateActive ,
Sequence : 20211108 ,
Name : "org-name-2" ,
Domain : "caos.ch" ,
} ,
} ,
} ,
} ,
{
name : "prepareOrgsQuery sql err" ,
prepare : prepareOrgsQuery ,
want : want {
sqlExpectations : mockQueryErr (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgsQueryStmt ) ,
2021-11-15 13:52:49 +01:00
sql . ErrConnDone ,
) ,
err : func ( err error ) ( error , bool ) {
2023-12-08 16:30:55 +02:00
if ! errors . Is ( err , sql . ErrConnDone ) {
2021-11-15 13:52:49 +01:00
return fmt . Errorf ( "err should be sql.ErrConnDone got: %w" , err ) , false
}
return nil , true
} ,
} ,
2023-08-22 12:49:22 +02:00
object : ( * Orgs ) ( nil ) ,
2021-11-15 13:52:49 +01:00
} ,
{
name : "prepareOrgQuery no result" ,
prepare : prepareOrgQuery ,
want : want {
2023-08-22 12:49:22 +02:00
sqlExpectations : mockQueriesScanErr (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgQueryStmt ) ,
2021-11-15 13:52:49 +01:00
nil ,
nil ,
) ,
err : func ( err error ) ( error , bool ) {
2023-12-08 16:30:55 +02:00
if ! zerrors . IsNotFound ( err ) {
2021-11-15 13:52:49 +01:00
return fmt . Errorf ( "err should be zitadel.NotFoundError got: %w" , err ) , false
}
return nil , true
} ,
} ,
object : ( * Org ) ( nil ) ,
} ,
{
name : "prepareOrgQuery found" ,
prepare : prepareOrgQuery ,
want : want {
sqlExpectations : mockQuery (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgQueryStmt ) ,
prepareOrgQueryCols ,
2021-11-15 13:52:49 +01:00
[ ] driver . Value {
"id" ,
testNow ,
testNow ,
"ro" ,
domain . OrgStateActive ,
uint64 ( 20211108 ) ,
"org-name" ,
"zitadel.ch" ,
} ,
) ,
} ,
object : & Org {
ID : "id" ,
CreationDate : testNow ,
ChangeDate : testNow ,
ResourceOwner : "ro" ,
State : domain . OrgStateActive ,
Sequence : 20211108 ,
Name : "org-name" ,
Domain : "zitadel.ch" ,
} ,
} ,
{
name : "prepareOrgQuery sql err" ,
prepare : prepareOrgQuery ,
want : want {
sqlExpectations : mockQueryErr (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgQueryStmt ) ,
2021-11-15 13:52:49 +01:00
sql . ErrConnDone ,
) ,
err : func ( err error ) ( error , bool ) {
2023-12-08 16:30:55 +02:00
if ! errors . Is ( err , sql . ErrConnDone ) {
2021-11-15 13:52:49 +01:00
return fmt . Errorf ( "err should be sql.ErrConnDone got: %w" , err ) , false
}
return nil , true
} ,
} ,
2023-08-22 12:49:22 +02:00
object : ( * Org ) ( nil ) ,
2021-11-15 13:52:49 +01:00
} ,
{
name : "prepareOrgUniqueQuery no result" ,
prepare : prepareOrgUniqueQuery ,
want : want {
2023-08-22 12:49:22 +02:00
sqlExpectations : mockQueriesScanErr (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgUniqueStmt ) ,
2021-11-15 13:52:49 +01:00
nil ,
nil ,
) ,
err : func ( err error ) ( error , bool ) {
2023-12-08 16:30:55 +02:00
if ! zerrors . IsInternal ( err ) {
2021-11-15 13:52:49 +01:00
return fmt . Errorf ( "err should be zitadel.Internal got: %w" , err ) , false
}
return nil , true
} ,
} ,
object : false ,
} ,
{
name : "prepareOrgUniqueQuery found" ,
prepare : prepareOrgUniqueQuery ,
want : want {
sqlExpectations : mockQuery (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgUniqueStmt ) ,
prepareOrgUniqueCols ,
2021-11-15 13:52:49 +01:00
[ ] driver . Value {
1 ,
} ,
) ,
} ,
object : true ,
} ,
{
name : "prepareOrgUniqueQuery sql err" ,
prepare : prepareOrgUniqueQuery ,
want : want {
sqlExpectations : mockQueryErr (
2023-02-27 22:36:43 +01:00
regexp . QuoteMeta ( prepareOrgUniqueStmt ) ,
2021-11-15 13:52:49 +01:00
sql . ErrConnDone ,
) ,
err : func ( err error ) ( error , bool ) {
2023-12-08 16:30:55 +02:00
if ! errors . Is ( err , sql . ErrConnDone ) {
2021-11-15 13:52:49 +01:00
return fmt . Errorf ( "err should be sql.ErrConnDone got: %w" , err ) , false
}
return nil , true
} ,
} ,
2023-08-22 12:49:22 +02:00
object : false ,
2021-11-15 13:52:49 +01:00
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2023-02-27 22:36:43 +01:00
assertPrepare ( t , tt . prepare , tt . object , tt . want . sqlExpectations , tt . want . err , defaultPrepareArgs ... )
2021-11-15 13:52:49 +01:00
} )
}
}
2023-01-16 10:55:19 +01:00
func TestQueries_IsOrgUnique ( t * testing . T ) {
type args struct {
name string
domain string
}
type want struct {
err func ( error ) bool
sqlExpectations sqlExpectation
isUnique bool
}
tests := [ ] struct {
name string
args args
want want
} {
{
name : "existing domain" ,
args : args {
domain : "exists" ,
name : "" ,
} ,
want : want {
isUnique : false ,
sqlExpectations : mockQueries ( orgUniqueQuery , orgUniqueCols , [ ] [ ] driver . Value { { false } } , true , "" , "exists" , "" , domain . OrgStateRemoved ) ,
} ,
} ,
{
name : "existing name" ,
args : args {
domain : "" ,
name : "exists" ,
} ,
want : want {
isUnique : false ,
sqlExpectations : mockQueries ( orgUniqueQuery , orgUniqueCols , [ ] [ ] driver . Value { { false } } , true , "" , "" , "exists" , domain . OrgStateRemoved ) ,
} ,
} ,
{
name : "existing name and domain" ,
args : args {
domain : "exists" ,
name : "exists" ,
} ,
want : want {
isUnique : false ,
sqlExpectations : mockQueries ( orgUniqueQuery , orgUniqueCols , [ ] [ ] driver . Value { { false } } , true , "" , "exists" , "exists" , domain . OrgStateRemoved ) ,
} ,
} ,
{
name : "not existing" ,
args : args {
domain : "not-exists" ,
name : "not-exists" ,
} ,
want : want {
isUnique : true ,
sqlExpectations : mockQueries ( orgUniqueQuery , orgUniqueCols , [ ] [ ] driver . Value { { true } } , true , "" , "not-exists" , "not-exists" , domain . OrgStateRemoved ) ,
} ,
} ,
{
name : "no arg" ,
args : args {
domain : "" ,
name : "" ,
} ,
want : want {
isUnique : false ,
2023-12-08 16:30:55 +02:00
err : zerrors . IsErrorInvalidArgument ,
2023-01-16 10:55:19 +01:00
} ,
} ,
}
for _ , tt := range tests {
2024-03-27 14:48:22 +01:00
client , mock , err := sqlmock . New (
sqlmock . QueryMatcherOption ( sqlmock . QueryMatcherEqual ) ,
sqlmock . ValueConverterOption ( new ( db_mock . TypeConverter ) ) ,
)
2023-01-16 10:55:19 +01:00
if err != nil {
t . Fatalf ( "unable to mock db: %v" , err )
}
if tt . want . sqlExpectations != nil {
tt . want . sqlExpectations ( mock )
}
t . Run ( tt . name , func ( t * testing . T ) {
q := & Queries {
2023-02-27 22:36:43 +01:00
client : & database . DB {
DB : client ,
Database : new ( prepareDB ) ,
} ,
2023-01-16 10:55:19 +01:00
}
gotIsUnique , err := q . IsOrgUnique ( context . Background ( ) , tt . args . name , tt . args . domain )
if ( tt . want . err == nil && err != nil ) || ( err != nil && tt . want . err != nil && ! tt . want . err ( err ) ) {
t . Errorf ( "Queries.IsOrgUnique() unexpected error = %v" , err )
return
}
if gotIsUnique != tt . want . isUnique {
t . Errorf ( "Queries.IsOrgUnique() = %v, want %v" , gotIsUnique , tt . want . isUnique )
}
if err := mock . ExpectationsWereMet ( ) ; err != nil {
t . Errorf ( "expectation was met: %v" , err )
}
} )
}
}