2020-10-05 18:39:36 +00:00
package sql
import (
2020-10-15 06:44:17 +00:00
"context"
2022-01-06 07:29:58 +00:00
"database/sql"
2020-10-19 07:53:32 +00:00
"sync"
2020-10-05 18:39:36 +00:00
"testing"
2021-07-06 11:55:57 +00:00
"github.com/lib/pq"
2022-01-06 07:29:58 +00:00
2022-04-26 23:01:45 +00:00
"github.com/zitadel/zitadel/internal/eventstore/repository"
2020-10-05 18:39:36 +00:00
)
func TestCRDB_placeholder ( t * testing . T ) {
type args struct {
query string
}
type res struct {
query string
}
tests := [ ] struct {
name string
args args
res res
} {
{
name : "no placeholders" ,
args : args {
query : "SELECT * FROM eventstore.events" ,
} ,
res : res {
query : "SELECT * FROM eventstore.events" ,
} ,
} ,
{
name : "one placeholder" ,
args : args {
query : "SELECT * FROM eventstore.events WHERE aggregate_type = ?" ,
} ,
res : res {
query : "SELECT * FROM eventstore.events WHERE aggregate_type = $1" ,
} ,
} ,
{
name : "multiple placeholders" ,
args : args {
query : "SELECT * FROM eventstore.events WHERE aggregate_type = ? AND aggregate_id = ? LIMIT ?" ,
} ,
res : res {
query : "SELECT * FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = $2 LIMIT $3" ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB { }
if query := db . placeholder ( tt . args . query ) ; query != tt . res . query {
t . Errorf ( "CRDB.placeholder() = %v, want %v" , query , tt . res . query )
}
} )
}
}
func TestCRDB_operation ( t * testing . T ) {
type res struct {
op string
}
type args struct {
operation repository . Operation
}
tests := [ ] struct {
name string
args args
res res
} {
{
name : "no op" ,
args : args {
operation : repository . Operation ( - 1 ) ,
} ,
res : res {
op : "" ,
} ,
} ,
{
name : "greater" ,
args : args {
2020-10-06 19:28:09 +00:00
operation : repository . OperationGreater ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
op : ">" ,
} ,
} ,
{
name : "less" ,
args : args {
2020-10-06 19:28:09 +00:00
operation : repository . OperationLess ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
op : "<" ,
} ,
} ,
{
name : "equals" ,
args : args {
2020-10-06 19:28:09 +00:00
operation : repository . OperationEquals ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
op : "=" ,
} ,
} ,
{
name : "in" ,
args : args {
2020-10-06 19:28:09 +00:00
operation : repository . OperationIn ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
op : "=" ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB { }
if got := db . operation ( tt . args . operation ) ; got != tt . res . op {
t . Errorf ( "CRDB.operation() = %v, want %v" , got , tt . res . op )
}
} )
}
}
func TestCRDB_conditionFormat ( t * testing . T ) {
type res struct {
format string
}
type args struct {
operation repository . Operation
}
tests := [ ] struct {
name string
args args
res res
} {
{
name : "default" ,
args : args {
2020-10-06 19:28:09 +00:00
operation : repository . OperationEquals ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
format : "%s %s ?" ,
} ,
} ,
{
name : "in" ,
args : args {
2020-10-06 19:28:09 +00:00
operation : repository . OperationIn ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
format : "%s %s ANY(?)" ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB { }
if got := db . conditionFormat ( tt . args . operation ) ; got != tt . res . format {
t . Errorf ( "CRDB.conditionFormat() = %v, want %v" , got , tt . res . format )
}
} )
}
}
func TestCRDB_columnName ( t * testing . T ) {
type res struct {
name string
}
type args struct {
field repository . Field
}
tests := [ ] struct {
name string
args args
res res
} {
{
name : "invalid field" ,
args : args {
field : repository . Field ( - 1 ) ,
} ,
res : res {
name : "" ,
} ,
} ,
{
name : "aggregate id" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldAggregateID ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "aggregate_id" ,
} ,
} ,
{
name : "aggregate type" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldAggregateType ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "aggregate_type" ,
} ,
} ,
{
name : "editor service" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldEditorService ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "editor_service" ,
} ,
} ,
{
name : "editor user" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldEditorUser ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "editor_user" ,
} ,
} ,
{
name : "event type" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldEventType ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "event_type" ,
} ,
} ,
{
name : "latest sequence" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldSequence ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "event_sequence" ,
} ,
} ,
{
name : "resource owner" ,
args : args {
2020-10-06 19:28:09 +00:00
field : repository . FieldResourceOwner ,
2020-10-05 18:39:36 +00:00
} ,
res : res {
name : "resource_owner" ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB { }
if got := db . columnName ( tt . args . field ) ; got != tt . res . name {
t . Errorf ( "CRDB.operation() = %v, want %v" , got , tt . res . name )
}
} )
}
}
2020-10-15 06:44:17 +00:00
2020-10-15 11:25:25 +00:00
func TestCRDB_Push_OneAggregate ( t * testing . T ) {
2020-10-15 06:44:17 +00:00
type args struct {
2022-03-28 08:05:09 +00:00
ctx context . Context
events [ ] * repository . Event
uniqueConstraints * repository . UniqueConstraint
uniqueDataType string
uniqueDataField string
uniqueDataInstanceID string
2020-10-15 06:44:17 +00:00
}
2020-10-15 11:25:25 +00:00
type eventsRes struct {
pushedEventsCount int
2021-01-21 09:49:38 +00:00
uniqueCount int
2021-04-27 10:58:18 +00:00
assetCount int
2020-10-15 11:25:25 +00:00
aggType repository . AggregateType
aggID [ ] string
}
type res struct {
wantErr bool
eventsRes eventsRes
}
2020-10-15 06:44:17 +00:00
tests := [ ] struct {
2020-10-15 11:25:25 +00:00
name string
args args
res res
2020-10-15 06:44:17 +00:00
} {
{
2021-01-15 08:32:59 +00:00
name : "push 1 event" ,
2020-10-15 06:44:17 +00:00
args : args {
2020-10-19 07:53:32 +00:00
ctx : context . Background ( ) ,
2020-10-15 06:44:17 +00:00
events : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "1" ) ,
2020-10-15 11:25:25 +00:00
} ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 1 ,
aggID : [ ] string { "1" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} } ,
} ,
{
2021-01-15 08:32:59 +00:00
name : "push two events on agg" ,
2020-10-15 11:25:25 +00:00
args : args {
2020-10-19 07:53:32 +00:00
ctx : context . Background ( ) ,
2020-10-15 11:25:25 +00:00
events : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "6" ) ,
generateEvent ( t , "6" ) ,
2020-10-15 11:25:25 +00:00
} ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 2 ,
aggID : [ ] string { "6" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} ,
} ,
} ,
2020-10-19 07:53:32 +00:00
{
name : "failed push because context canceled" ,
args : args {
ctx : canceledCtx ( ) ,
events : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "9" ) ,
2020-10-19 07:53:32 +00:00
} ,
} ,
res : res {
wantErr : true ,
eventsRes : eventsRes {
pushedEventsCount : 0 ,
aggID : [ ] string { "9" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} ,
} ,
} ,
2021-01-21 09:49:38 +00:00
{
name : "push 1 event and add unique constraint" ,
args : args {
ctx : context . Background ( ) ,
events : [ ] * repository . Event {
generateEvent ( t , "10" ) ,
} ,
uniqueConstraints : generateAddUniqueConstraint ( t , "usernames" , "field" ) ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 1 ,
uniqueCount : 1 ,
aggID : [ ] string { "10" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} } ,
} ,
{
name : "push 1 event and remove unique constraint" ,
args : args {
ctx : context . Background ( ) ,
events : [ ] * repository . Event {
generateEvent ( t , "11" ) ,
} ,
uniqueConstraints : generateRemoveUniqueConstraint ( t , "usernames" , "testremove" ) ,
uniqueDataType : "usernames" ,
uniqueDataField : "testremove" ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 1 ,
uniqueCount : 0 ,
aggID : [ ] string { "11" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} } ,
} ,
2021-04-27 10:58:18 +00:00
{
name : "push 1 event and add asset" ,
args : args {
ctx : context . Background ( ) ,
events : [ ] * repository . Event {
generateEvent ( t , "12" ) ,
} ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 1 ,
assetCount : 1 ,
aggID : [ ] string { "12" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} } ,
} ,
{
name : "push 1 event and remove asset" ,
args : args {
ctx : context . Background ( ) ,
events : [ ] * repository . Event {
generateEvent ( t , "13" ) ,
} ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 1 ,
assetCount : 0 ,
aggID : [ ] string { "13" } ,
aggType : repository . AggregateType ( t . Name ( ) ) ,
} } ,
} ,
2020-10-15 11:25:25 +00:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
2021-01-21 09:49:38 +00:00
if tt . args . uniqueDataType != "" && tt . args . uniqueDataField != "" {
2022-03-28 08:05:09 +00:00
err := fillUniqueData ( tt . args . uniqueDataType , tt . args . uniqueDataField , tt . args . uniqueDataInstanceID )
2021-01-21 09:49:38 +00:00
if err != nil {
t . Error ( "unable to prefill insert unique data: " , err )
return
}
}
2021-05-03 08:15:50 +00:00
if err := db . Push ( tt . args . ctx , tt . args . events , tt . args . uniqueConstraints ) ; ( err != nil ) != tt . res . wantErr {
2020-10-15 11:25:25 +00:00
t . Errorf ( "CRDB.Push() error = %v, wantErr %v" , err , tt . res . wantErr )
}
2021-01-21 09:49:38 +00:00
countEventRow := testCRDBClient . QueryRow ( "SELECT COUNT(*) FROM eventstore.events where aggregate_type = $1 AND aggregate_id = ANY($2)" , tt . res . eventsRes . aggType , pq . Array ( tt . res . eventsRes . aggID ) )
var eventCount int
err := countEventRow . Scan ( & eventCount )
2020-10-15 11:25:25 +00:00
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
2021-01-21 09:49:38 +00:00
if eventCount != tt . res . eventsRes . pushedEventsCount {
t . Errorf ( "expected push count %d got %d" , tt . res . eventsRes . pushedEventsCount , eventCount )
}
if tt . args . uniqueConstraints != nil {
2022-03-28 08:05:09 +00:00
countUniqueRow := testCRDBClient . QueryRow ( "SELECT COUNT(*) FROM eventstore.unique_constraints where unique_type = $1 AND unique_field = $2 AND instance_id = $3" , tt . args . uniqueConstraints . UniqueType , tt . args . uniqueConstraints . UniqueField , tt . args . uniqueConstraints . InstanceID )
2021-01-21 09:49:38 +00:00
var uniqueCount int
err := countUniqueRow . Scan ( & uniqueCount )
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
if uniqueCount != tt . res . eventsRes . uniqueCount {
t . Errorf ( "expected unique count %d got %d" , tt . res . eventsRes . uniqueCount , uniqueCount )
}
2020-10-15 11:25:25 +00:00
}
} )
}
}
func TestCRDB_Push_MultipleAggregate ( t * testing . T ) {
type args struct {
events [ ] * repository . Event
}
type eventsRes struct {
pushedEventsCount int
aggType [ ] repository . AggregateType
aggID [ ] string
}
type res struct {
wantErr bool
eventsRes eventsRes
}
tests := [ ] struct {
name string
args args
res res
} {
{
2021-01-15 08:32:59 +00:00
name : "push two aggregates" ,
2020-10-15 06:44:17 +00:00
args : args {
events : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "100" ) ,
generateEvent ( t , "101" ) ,
2020-10-15 11:25:25 +00:00
} ,
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 2 ,
aggID : [ ] string { "100" , "101" } ,
aggType : [ ] repository . AggregateType { repository . AggregateType ( t . Name ( ) ) } ,
} ,
} ,
} ,
{
2021-01-15 08:32:59 +00:00
name : "push two aggregates both multiple events" ,
2020-10-15 11:25:25 +00:00
args : args {
2021-01-15 08:32:59 +00:00
events : [ ] * repository . Event {
generateEvent ( t , "102" ) ,
generateEvent ( t , "102" ) ,
generateEvent ( t , "103" ) ,
generateEvent ( t , "103" ) ,
} ,
2020-10-15 11:25:25 +00:00
} ,
res : res {
wantErr : false ,
eventsRes : eventsRes {
pushedEventsCount : 4 ,
aggID : [ ] string { "102" , "103" } ,
aggType : [ ] repository . AggregateType { repository . AggregateType ( t . Name ( ) ) } ,
} ,
} ,
} ,
{
2021-01-15 08:32:59 +00:00
name : "push two aggregates mixed multiple events" ,
2020-10-15 11:25:25 +00:00
args : args {
2021-01-15 08:32:59 +00:00
events : [ ] * repository . Event {
generateEvent ( t , "106" ) ,
generateEvent ( t , "106" ) ,
generateEvent ( t , "106" ) ,
generateEvent ( t , "106" ) ,
generateEvent ( t , "107" ) ,
generateEvent ( t , "107" ) ,
generateEvent ( t , "107" ) ,
generateEvent ( t , "107" ) ,
generateEvent ( t , "108" ) ,
generateEvent ( t , "108" ) ,
generateEvent ( t , "108" ) ,
generateEvent ( t , "108" ) ,
2020-10-19 07:53:32 +00:00
} ,
} ,
res : res {
2021-01-15 08:32:59 +00:00
wantErr : false ,
2020-10-19 07:53:32 +00:00
eventsRes : eventsRes {
2021-01-15 08:32:59 +00:00
pushedEventsCount : 12 ,
aggID : [ ] string { "106" , "107" , "108" } ,
2020-10-15 11:25:25 +00:00
aggType : [ ] repository . AggregateType { repository . AggregateType ( t . Name ( ) ) } ,
2020-10-15 06:44:17 +00:00
} ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
2021-05-03 08:15:50 +00:00
if err := db . Push ( context . Background ( ) , tt . args . events ) ; ( err != nil ) != tt . res . wantErr {
2020-10-15 11:25:25 +00:00
t . Errorf ( "CRDB.Push() error = %v, wantErr %v" , err , tt . res . wantErr )
}
countRow := testCRDBClient . QueryRow ( "SELECT COUNT(*) FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2)" , pq . Array ( tt . res . eventsRes . aggType ) , pq . Array ( tt . res . eventsRes . aggID ) )
var count int
err := countRow . Scan ( & count )
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
if count != tt . res . eventsRes . pushedEventsCount {
t . Errorf ( "expected push count %d got %d" , tt . res . eventsRes . pushedEventsCount , count )
2020-10-15 06:44:17 +00:00
}
} )
}
}
2020-10-15 11:25:25 +00:00
2022-04-13 05:42:48 +00:00
func TestCRDB_CreateInstance ( t * testing . T ) {
type args struct {
instanceID string
}
type res struct {
wantErr bool
exists bool
}
tests := [ ] struct {
name string
args args
res res
} {
{
name : "no number" ,
args : args {
instanceID : "asdf;use defaultdb;DROP DATABASE zitadel;--" ,
} ,
res : res {
wantErr : true ,
exists : false ,
} ,
} ,
{
name : "no instance id" ,
args : args {
instanceID : "" ,
} ,
res : res {
wantErr : true ,
exists : false ,
} ,
} ,
{
name : "correct number" ,
args : args {
instanceID : "1235" ,
} ,
res : res {
wantErr : false ,
exists : true ,
} ,
} ,
{
name : "correct text" ,
args : args {
instanceID : "system" ,
} ,
res : res {
wantErr : false ,
exists : true ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
if err := db . CreateInstance ( context . Background ( ) , tt . args . instanceID ) ; ( err != nil ) != tt . res . wantErr {
t . Errorf ( "CRDB.CreateInstance() error = %v, wantErr %v" , err , tt . res . wantErr )
}
sequenceRow := testCRDBClient . QueryRow ( "SELECT EXISTS(SELECT 1 FROM [SHOW SEQUENCES FROM eventstore] WHERE sequence_name like $1)" , "i_" + tt . args . instanceID + "%" )
var exists bool
err := sequenceRow . Scan ( & exists )
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
if exists != tt . res . exists {
t . Errorf ( "expected exists %v got %v" , tt . res . exists , exists )
}
} )
}
}
2020-10-19 07:53:32 +00:00
func TestCRDB_Push_Parallel ( t * testing . T ) {
type args struct {
events [ ] [ ] * repository . Event
}
type eventsRes struct {
pushedEventsCount int
aggTypes [ ] repository . AggregateType
aggIDs [ ] string
}
type res struct {
errCount int
eventsRes eventsRes
}
tests := [ ] struct {
name string
args args
res res
} {
{
name : "clients push different aggregates" ,
args : args {
events : [ ] [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
{
generateEvent ( t , "200" ) ,
generateEvent ( t , "200" ) ,
generateEvent ( t , "200" ) ,
generateEvent ( t , "201" ) ,
generateEvent ( t , "201" ) ,
generateEvent ( t , "201" ) ,
} ,
{
generateEvent ( t , "202" ) ,
generateEvent ( t , "203" ) ,
generateEvent ( t , "203" ) ,
} ,
2020-10-19 07:53:32 +00:00
} ,
} ,
res : res {
errCount : 0 ,
eventsRes : eventsRes {
aggIDs : [ ] string { "200" , "201" , "202" , "203" } ,
pushedEventsCount : 9 ,
aggTypes : [ ] repository . AggregateType { repository . AggregateType ( t . Name ( ) ) } ,
} ,
} ,
} ,
{
2021-01-15 08:32:59 +00:00
name : "clients push same aggregates" ,
2020-10-19 07:53:32 +00:00
args : args {
events : [ ] [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
{
generateEvent ( t , "204" ) ,
generateEvent ( t , "204" ) ,
} ,
{
generateEvent ( t , "204" ) ,
generateEvent ( t , "204" ) ,
} ,
{
generateEvent ( t , "205" ) ,
generateEvent ( t , "205" ) ,
generateEvent ( t , "205" ) ,
generateEvent ( t , "206" ) ,
generateEvent ( t , "206" ) ,
generateEvent ( t , "206" ) ,
} ,
{
generateEvent ( t , "204" ) ,
generateEvent ( t , "205" ) ,
generateEvent ( t , "205" ) ,
generateEvent ( t , "206" ) ,
} ,
2020-10-19 07:53:32 +00:00
} ,
} ,
res : res {
errCount : 0 ,
eventsRes : eventsRes {
aggIDs : [ ] string { "204" , "205" , "206" } ,
pushedEventsCount : 14 ,
aggTypes : [ ] repository . AggregateType { repository . AggregateType ( t . Name ( ) ) } ,
} ,
} ,
} ,
{
2021-01-15 08:32:59 +00:00
name : "clients push different aggregates" ,
2020-10-19 07:53:32 +00:00
args : args {
events : [ ] [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
{
generateEvent ( t , "207" ) ,
generateEvent ( t , "207" ) ,
generateEvent ( t , "207" ) ,
generateEvent ( t , "207" ) ,
generateEvent ( t , "207" ) ,
generateEvent ( t , "207" ) ,
} ,
{
generateEvent ( t , "208" ) ,
generateEvent ( t , "208" ) ,
generateEvent ( t , "208" ) ,
generateEvent ( t , "208" ) ,
generateEvent ( t , "208" ) ,
} ,
2020-10-19 07:53:32 +00:00
} ,
} ,
res : res {
errCount : 0 ,
eventsRes : eventsRes {
aggIDs : [ ] string { "207" , "208" } ,
pushedEventsCount : 11 ,
aggTypes : [ ] repository . AggregateType { repository . AggregateType ( t . Name ( ) ) } ,
} ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
wg := sync . WaitGroup { }
errs := make ( [ ] error , 0 , tt . res . errCount )
errsMu := sync . Mutex { }
for _ , events := range tt . args . events {
wg . Add ( 1 )
go func ( events [ ] * repository . Event ) {
2021-04-27 10:58:18 +00:00
err := db . Push ( context . Background ( ) , events , nil )
2020-10-19 07:53:32 +00:00
if err != nil {
errsMu . Lock ( )
errs = append ( errs , err )
errsMu . Unlock ( )
}
wg . Done ( )
} ( events )
}
wg . Wait ( )
if len ( errs ) != tt . res . errCount {
t . Errorf ( "CRDB.Push() error count = %d, wanted err count %d, errs: %v" , len ( errs ) , tt . res . errCount , errs )
}
rows , err := testCRDBClient . Query ( "SELECT event_data FROM eventstore.events where aggregate_type = ANY($1) AND aggregate_id = ANY($2) order by event_sequence" , pq . Array ( tt . res . eventsRes . aggTypes ) , pq . Array ( tt . res . eventsRes . aggIDs ) )
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
var count int
for rows . Next ( ) {
count ++
data := make ( Data , 0 )
err := rows . Scan ( & data )
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
t . Logf ( "inserted data: %v" , string ( data ) )
}
if count != tt . res . eventsRes . pushedEventsCount {
t . Errorf ( "expected push count %d got %d" , tt . res . eventsRes . pushedEventsCount , count )
}
} )
}
}
2020-10-21 17:00:41 +00:00
func TestCRDB_Filter ( t * testing . T ) {
2020-10-19 07:53:32 +00:00
type args struct {
searchQuery * repository . SearchQuery
}
type fields struct {
existingEvents [ ] * repository . Event
}
type res struct {
2020-10-21 07:39:24 +00:00
eventCount int
2020-10-19 07:53:32 +00:00
}
tests := [ ] struct {
name string
fields fields
args args
res res
wantErr bool
} {
{
name : "aggregate type filter no events" ,
2020-10-21 07:39:24 +00:00
args : args {
searchQuery : & repository . SearchQuery {
Columns : repository . ColumnsEvent ,
2021-07-06 11:55:57 +00:00
Filters : [ ] [ ] * repository . Filter {
{
repository . NewFilter ( repository . FieldAggregateType , "not found" , repository . OperationEquals ) ,
} ,
2020-10-21 07:39:24 +00:00
} ,
} ,
} ,
fields : fields {
existingEvents : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "300" ) ,
generateEvent ( t , "300" ) ,
generateEvent ( t , "300" ) ,
2020-10-21 07:39:24 +00:00
} ,
} ,
res : res {
eventCount : 0 ,
} ,
wantErr : false ,
} ,
{
2020-10-21 17:00:41 +00:00
name : "aggregate type and id filter events found" ,
2020-10-21 07:39:24 +00:00
args : args {
searchQuery : & repository . SearchQuery {
Columns : repository . ColumnsEvent ,
2021-07-06 11:55:57 +00:00
Filters : [ ] [ ] * repository . Filter {
{
repository . NewFilter ( repository . FieldAggregateType , t . Name ( ) , repository . OperationEquals ) ,
repository . NewFilter ( repository . FieldAggregateID , "303" , repository . OperationEquals ) ,
} ,
2020-10-21 07:39:24 +00:00
} ,
} ,
} ,
fields : fields {
existingEvents : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "303" ) ,
generateEvent ( t , "303" ) ,
generateEvent ( t , "303" ) ,
generateEvent ( t , "305" ) ,
2020-10-21 07:39:24 +00:00
} ,
} ,
res : res {
eventCount : 3 ,
} ,
wantErr : false ,
} ,
2020-10-21 17:00:41 +00:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
// setup initial data for query
2021-05-03 08:15:50 +00:00
if err := db . Push ( context . Background ( ) , tt . fields . existingEvents ) ; err != nil {
2020-10-21 17:00:41 +00:00
t . Errorf ( "error in setup = %v" , err )
return
}
events , err := db . Filter ( context . Background ( ) , tt . args . searchQuery )
if ( err != nil ) != tt . wantErr {
t . Errorf ( "CRDB.query() error = %v, wantErr %v" , err , tt . wantErr )
}
if len ( events ) != tt . res . eventCount {
t . Errorf ( "CRDB.query() expected event count: %d got %d" , tt . res . eventCount , len ( events ) )
}
} )
}
}
func TestCRDB_LatestSequence ( t * testing . T ) {
type args struct {
searchQuery * repository . SearchQuery
}
type fields struct {
existingEvents [ ] * repository . Event
}
type res struct {
sequence uint64
}
tests := [ ] struct {
name string
fields fields
args args
res res
wantErr bool
} {
2020-10-21 07:39:24 +00:00
{
2020-10-21 17:00:41 +00:00
name : "aggregate type filter no sequence" ,
2020-10-21 07:39:24 +00:00
args : args {
searchQuery : & repository . SearchQuery {
2020-10-21 17:00:41 +00:00
Columns : repository . ColumnsMaxSequence ,
2021-07-06 11:55:57 +00:00
Filters : [ ] [ ] * repository . Filter {
{
repository . NewFilter ( repository . FieldAggregateType , "not found" , repository . OperationEquals ) ,
} ,
2020-10-21 07:39:24 +00:00
} ,
} ,
} ,
fields : fields {
existingEvents : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "400" ) ,
generateEvent ( t , "400" ) ,
generateEvent ( t , "400" ) ,
2020-10-21 07:39:24 +00:00
} ,
} ,
res : res {
2020-10-21 17:00:41 +00:00
sequence : 0 ,
2020-10-21 07:39:24 +00:00
} ,
wantErr : false ,
} ,
{
2020-10-21 17:00:41 +00:00
name : "aggregate type filter sequence" ,
2020-10-19 07:53:32 +00:00
args : args {
2020-10-21 17:00:41 +00:00
searchQuery : & repository . SearchQuery {
Columns : repository . ColumnsMaxSequence ,
2021-07-06 11:55:57 +00:00
Filters : [ ] [ ] * repository . Filter {
{
repository . NewFilter ( repository . FieldAggregateType , t . Name ( ) , repository . OperationEquals ) ,
} ,
2020-10-21 17:00:41 +00:00
} ,
} ,
2020-10-19 07:53:32 +00:00
} ,
fields : fields {
2020-10-21 17:00:41 +00:00
existingEvents : [ ] * repository . Event {
2021-01-15 08:32:59 +00:00
generateEvent ( t , "401" ) ,
generateEvent ( t , "401" ) ,
generateEvent ( t , "401" ) ,
2020-10-21 17:00:41 +00:00
} ,
2020-10-19 07:53:32 +00:00
} ,
res : res {
2020-10-21 17:00:41 +00:00
sequence : 3 ,
2020-10-19 07:53:32 +00:00
} ,
wantErr : false ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
// setup initial data for query
2021-05-03 08:15:50 +00:00
if err := db . Push ( context . Background ( ) , tt . fields . existingEvents ) ; err != nil {
2020-10-19 07:53:32 +00:00
t . Errorf ( "error in setup = %v" , err )
return
}
2020-10-21 17:00:41 +00:00
sequence , err := db . LatestSequence ( context . Background ( ) , tt . args . searchQuery )
if ( err != nil ) != tt . wantErr {
2020-10-19 07:53:32 +00:00
t . Errorf ( "CRDB.query() error = %v, wantErr %v" , err , tt . wantErr )
}
2020-10-21 17:00:41 +00:00
if sequence < tt . res . sequence {
t . Errorf ( "CRDB.query() expected sequence: %d got %d" , tt . res . sequence , sequence )
}
2020-10-19 07:53:32 +00:00
} )
}
}
2020-10-22 16:13:31 +00:00
func TestCRDB_Push_ResourceOwner ( t * testing . T ) {
type args struct {
events [ ] * repository . Event
}
type res struct {
resourceOwners [ ] string
}
type fields struct {
aggregateIDs [ ] string
aggregateType string
}
tests := [ ] struct {
name string
args args
res res
fields fields
} {
{
name : "two events of same aggregate same resource owner" ,
args : args {
events : [ ] * repository . Event {
2022-01-06 07:29:58 +00:00
generateEvent ( t , "500" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "500" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
2020-10-22 16:13:31 +00:00
} ,
} ,
fields : fields {
aggregateIDs : [ ] string { "500" } ,
aggregateType : t . Name ( ) ,
} ,
res : res {
resourceOwners : [ ] string { "caos" , "caos" } ,
} ,
} ,
{
name : "two events of different aggregate same resource owner" ,
args : args {
events : [ ] * repository . Event {
2022-01-06 07:29:58 +00:00
generateEvent ( t , "501" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "502" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
2020-10-22 16:13:31 +00:00
} ,
} ,
fields : fields {
aggregateIDs : [ ] string { "501" , "502" } ,
aggregateType : t . Name ( ) ,
} ,
res : res {
resourceOwners : [ ] string { "caos" , "caos" } ,
} ,
} ,
{
name : "two events of different aggregate different resource owner" ,
args : args {
events : [ ] * repository . Event {
2022-01-06 07:29:58 +00:00
generateEvent ( t , "503" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "504" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "zitadel" , Valid : true } } ) ,
2020-10-22 16:13:31 +00:00
} ,
} ,
fields : fields {
aggregateIDs : [ ] string { "503" , "504" } ,
aggregateType : t . Name ( ) ,
} ,
res : res {
resourceOwners : [ ] string { "caos" , "zitadel" } ,
} ,
} ,
{
name : "events of different aggregate different resource owner" ,
args : args {
2021-01-15 08:32:59 +00:00
events : [ ] * repository . Event {
2022-01-06 07:29:58 +00:00
generateEvent ( t , "505" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "505" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "506" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "zitadel" , Valid : true } } ) ,
generateEvent ( t , "506" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "zitadel" , Valid : true } } ) ,
2021-01-15 08:32:59 +00:00
} ,
2020-10-22 16:13:31 +00:00
} ,
fields : fields {
aggregateIDs : [ ] string { "505" , "506" } ,
aggregateType : t . Name ( ) ,
} ,
res : res {
resourceOwners : [ ] string { "caos" , "caos" , "zitadel" , "zitadel" } ,
} ,
} ,
{
name : "events of different aggregate different resource owner per event" ,
args : args {
2021-01-15 08:32:59 +00:00
events : [ ] * repository . Event {
2022-01-06 07:29:58 +00:00
generateEvent ( t , "507" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "507" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "ignored" , Valid : true } } ) ,
generateEvent ( t , "508" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "zitadel" , Valid : true } } ) ,
generateEvent ( t , "508" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "ignored" , Valid : true } } ) ,
2021-01-15 08:32:59 +00:00
} ,
2020-10-22 16:13:31 +00:00
} ,
fields : fields {
aggregateIDs : [ ] string { "507" , "508" } ,
aggregateType : t . Name ( ) ,
} ,
res : res {
resourceOwners : [ ] string { "caos" , "caos" , "zitadel" , "zitadel" } ,
} ,
} ,
{
name : "events of one aggregate different resource owner per event" ,
args : args {
2021-01-15 08:32:59 +00:00
events : [ ] * repository . Event {
2022-01-06 07:29:58 +00:00
generateEvent ( t , "509" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "caos" , Valid : true } } ) ,
generateEvent ( t , "509" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "ignored" , Valid : true } } ) ,
generateEvent ( t , "509" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "ignored" , Valid : true } } ) ,
generateEvent ( t , "509" , func ( e * repository . Event ) { e . ResourceOwner = sql . NullString { String : "ignored" , Valid : true } } ) ,
2021-01-15 08:32:59 +00:00
} ,
2020-10-22 16:13:31 +00:00
} ,
fields : fields {
aggregateIDs : [ ] string { "509" } ,
aggregateType : t . Name ( ) ,
} ,
res : res {
resourceOwners : [ ] string { "caos" , "caos" , "caos" , "caos" } ,
} ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
db := & CRDB {
client : testCRDBClient ,
}
2021-05-03 08:15:50 +00:00
if err := db . Push ( context . Background ( ) , tt . args . events ) ; err != nil {
2020-10-22 16:13:31 +00:00
t . Errorf ( "CRDB.Push() error = %v" , err )
}
if len ( tt . args . events ) != len ( tt . res . resourceOwners ) {
t . Errorf ( "length of events (%d) and resource owners (%d) must be equal" , len ( tt . args . events ) , len ( tt . res . resourceOwners ) )
return
}
for i , event := range tt . args . events {
2022-01-06 07:29:58 +00:00
if event . ResourceOwner . String != tt . res . resourceOwners [ i ] {
t . Errorf ( "resource owner not expected want: %q got: %q" , tt . res . resourceOwners [ i ] , event . ResourceOwner . String )
2020-10-22 16:13:31 +00:00
}
}
rows , err := testCRDBClient . Query ( "SELECT resource_owner FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY event_sequence" , tt . fields . aggregateType , pq . Array ( tt . fields . aggregateIDs ) )
if err != nil {
t . Error ( "unable to query inserted rows: " , err )
return
}
eventCount := 0
for i := 0 ; rows . Next ( ) ; i ++ {
var resourceOwner string
err = rows . Scan ( & resourceOwner )
if err != nil {
t . Error ( "unable to scan row: " , err )
return
}
if resourceOwner != tt . res . resourceOwners [ i ] {
t . Errorf ( "unexpected resource owner in queried event. want %q, got: %q" , tt . res . resourceOwners [ i ] , resourceOwner )
}
eventCount ++
}
if eventCount != len ( tt . res . resourceOwners ) {
t . Errorf ( "wrong queried event count: want %d, got %d" , len ( tt . res . resourceOwners ) , eventCount )
}
} )
}
}
2020-10-19 07:53:32 +00:00
func canceledCtx ( ) context . Context {
ctx , cancel := context . WithCancel ( context . Background ( ) )
cancel ( )
return ctx
}
2021-01-15 08:32:59 +00:00
func generateEvent ( t * testing . T , aggregateID string , opts ... func ( * repository . Event ) ) * repository . Event {
2020-10-15 11:25:25 +00:00
t . Helper ( )
2020-10-21 17:00:41 +00:00
e := & repository . Event {
2021-01-15 08:32:59 +00:00
AggregateID : aggregateID ,
AggregateType : repository . AggregateType ( t . Name ( ) ) ,
EditorService : "svc" ,
EditorUser : "user" ,
2022-01-06 07:29:58 +00:00
ResourceOwner : sql . NullString { String : "ro" , Valid : true } ,
2021-01-15 08:32:59 +00:00
Type : "test.created" ,
Version : "v1" ,
2020-10-15 11:25:25 +00:00
}
2020-10-21 17:00:41 +00:00
for _ , opt := range opts {
opt ( e )
}
return e
2020-10-15 11:25:25 +00:00
}
2020-10-19 07:53:32 +00:00
2021-01-21 09:49:38 +00:00
func generateAddUniqueConstraint ( t * testing . T , table , uniqueField string ) * repository . UniqueConstraint {
t . Helper ( )
e := & repository . UniqueConstraint {
UniqueType : table ,
UniqueField : uniqueField ,
Action : repository . UniqueConstraintAdd ,
}
return e
}
func generateRemoveUniqueConstraint ( t * testing . T , table , uniqueField string ) * repository . UniqueConstraint {
t . Helper ( )
e := & repository . UniqueConstraint {
UniqueType : table ,
UniqueField : uniqueField ,
2022-03-28 08:05:09 +00:00
InstanceID : "" ,
2021-01-21 09:49:38 +00:00
Action : repository . UniqueConstraintRemoved ,
}
return e
}