zitadel/internal/v2/eventstore/query_test.go
Silvan 5811a7b6a5
refactor(v2): init eventstore package (#7806)
* refactor(v2): init database package

* refactor(v2): init eventstore package

* add mock package

* test query constructors

* option based push analog to query
2024-04-26 15:05:21 +00:00

1064 lines
22 KiB
Go

package eventstore
import (
"database/sql"
"reflect"
"testing"
"time"
"github.com/zitadel/zitadel/internal/v2/database"
)
func TestPaginationOpt(t *testing.T) {
type args struct {
opts []paginationOpt
}
tests := []struct {
name string
args args
want *Pagination
}{
{
name: "desc",
args: args{
opts: []paginationOpt{
Descending(),
},
},
want: &Pagination{
desc: true,
},
},
{
name: "limit",
args: args{
opts: []paginationOpt{
Limit(10),
},
},
want: &Pagination{
pagination: &database.Pagination{
Limit: 10,
},
},
},
{
name: "offset",
args: args{
opts: []paginationOpt{
Offset(10),
},
},
want: &Pagination{
pagination: &database.Pagination{
Offset: 10,
},
},
},
{
name: "limit and offset",
args: args{
opts: []paginationOpt{
Limit(10),
Offset(20),
},
},
want: &Pagination{
pagination: &database.Pagination{
Limit: 10,
Offset: 20,
},
},
},
{
name: "global position greater",
args: args{
opts: []paginationOpt{
GlobalPositionGreater(&GlobalPosition{Position: 10}),
},
},
want: &Pagination{
position: &PositionCondition{
min: &GlobalPosition{
Position: 10,
InPositionOrder: 0,
},
},
},
},
{
name: "position greater",
args: args{
opts: []paginationOpt{
PositionGreater(10, 0),
},
},
want: &Pagination{
position: &PositionCondition{
min: &GlobalPosition{
Position: 10,
InPositionOrder: 0,
},
},
desc: false,
},
},
{
name: "position less",
args: args{
opts: []paginationOpt{
PositionLess(10, 12),
},
},
want: &Pagination{
position: &PositionCondition{
max: &GlobalPosition{
Position: 10,
InPositionOrder: 12,
},
},
},
},
{
name: "global position less",
args: args{
opts: []paginationOpt{
GlobalPositionLess(&GlobalPosition{Position: 12, InPositionOrder: 24}),
},
},
want: &Pagination{
position: &PositionCondition{
max: &GlobalPosition{
Position: 12,
InPositionOrder: 24,
},
},
},
},
{
name: "position between",
args: args{
opts: []paginationOpt{
PositionBetween(
&GlobalPosition{10, 12},
&GlobalPosition{20, 0},
),
},
},
want: &Pagination{
position: &PositionCondition{
min: &GlobalPosition{
Position: 10,
InPositionOrder: 12,
},
max: &GlobalPosition{
Position: 20,
InPositionOrder: 0,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := new(Pagination)
for _, opt := range tt.args.opts {
opt(got)
}
if tt.want.Desc() != got.Desc() {
t.Errorf("unexpected desc %v, want: %v", got.desc, tt.want.desc)
}
if !reflect.DeepEqual(tt.want.Pagination(), got.Pagination()) {
t.Errorf("unexpected pagination %v, want: %v", got.pagination, tt.want.pagination)
}
if !reflect.DeepEqual(tt.want.Position(), got.Position()) {
t.Errorf("unexpected position %v, want: %v", got.position, tt.want.position)
}
if !reflect.DeepEqual(tt.want.Position().Max(), got.Position().Max()) {
t.Errorf("unexpected position.max %v, want: %v", got.Position().max, tt.want.Position().max)
}
if !reflect.DeepEqual(tt.want.Position().Min(), got.Position().Min()) {
t.Errorf("unexpected position.min %v, want: %v", got.Position().min, tt.want.Position().min)
}
})
}
}
func TestEventFilterOpt(t *testing.T) {
type args struct {
opts []EventFilterOpt
}
now := time.Now()
tests := []struct {
name string
args args
want *EventFilter
}{
{
name: "EventType",
args: args{
opts: []EventFilterOpt{
SetEventType("test"),
SetEventType("test2"),
},
},
want: &EventFilter{
types: []string{"test2"},
},
},
{
name: "EventTypes",
args: args{
opts: []EventFilterOpt{
SetEventTypes("a", "s"),
SetEventTypes("d", "f"),
},
},
want: &EventFilter{
types: []string{"d", "f"},
},
},
{
name: "AppendEventTypes",
args: args{
opts: []EventFilterOpt{
AppendEventTypes("a", "s"),
AppendEventTypes("d", "f"),
},
},
want: &EventFilter{
types: []string{"a", "s", "d", "f"},
},
},
{
name: "EventRevisionEquals",
args: args{
opts: []EventFilterOpt{
EventRevisionEquals(12),
},
},
want: &EventFilter{
revision: &filter[uint16]{
condition: database.NewNumberEquals[uint16](12),
value: toPtr(uint16(12)),
},
},
},
{
name: "EventRevisionAtLeast",
args: args{
opts: []EventFilterOpt{
EventRevisionAtLeast(12),
},
},
want: &EventFilter{
revision: &filter[uint16]{
condition: database.NewNumberAtLeast[uint16](12),
value: toPtr(uint16(12)),
},
},
},
{
name: "EventRevisionGreater",
args: args{
opts: []EventFilterOpt{
EventRevisionGreater(12),
},
},
want: &EventFilter{
revision: &filter[uint16]{
condition: database.NewNumberGreater[uint16](12),
value: toPtr(uint16(12)),
},
},
},
{
name: "EventRevisionAtMost",
args: args{
opts: []EventFilterOpt{
EventRevisionAtMost(12),
},
},
want: &EventFilter{
revision: &filter[uint16]{
condition: database.NewNumberAtMost[uint16](12),
value: toPtr(uint16(12)),
},
},
},
{
name: "EventRevisionLess",
args: args{
opts: []EventFilterOpt{
EventRevisionLess(12),
},
},
want: &EventFilter{
revision: &filter[uint16]{
condition: database.NewNumberLess[uint16](12),
value: toPtr(uint16(12)),
},
},
},
{
name: "EventRevisionBetween",
args: args{
opts: []EventFilterOpt{
EventRevisionBetween(12, 20),
},
},
want: &EventFilter{
revision: &filter[uint16]{
condition: database.NewNumberBetween[uint16](12, 20),
min: toPtr(uint16(12)),
max: toPtr(uint16(20)),
},
},
},
{
name: "EventCreatedAtEquals",
args: args{
opts: []EventFilterOpt{
EventCreatedAtEquals(now),
},
},
want: &EventFilter{
createdAt: &filter[time.Time]{
condition: database.NewNumberEquals(now),
value: toPtr(now),
},
},
},
{
name: "EventCreatedAtAtLeast",
args: args{
opts: []EventFilterOpt{
EventCreatedAtAtLeast(now),
},
},
want: &EventFilter{
createdAt: &filter[time.Time]{
condition: database.NewNumberAtLeast(now),
value: toPtr(now),
},
},
},
{
name: "EventCreatedAtGreater",
args: args{
opts: []EventFilterOpt{
EventCreatedAtGreater(now),
},
},
want: &EventFilter{
createdAt: &filter[time.Time]{
condition: database.NewNumberGreater(now),
value: toPtr(now),
},
},
},
{
name: "EventCreatedAtAtMost",
args: args{
opts: []EventFilterOpt{
EventCreatedAtAtMost(now),
},
},
want: &EventFilter{
createdAt: &filter[time.Time]{
condition: database.NewNumberAtMost(now),
value: toPtr(now),
},
},
},
{
name: "EventCreatedAtLess",
args: args{
opts: []EventFilterOpt{
EventCreatedAtLess(now),
},
},
want: &EventFilter{
createdAt: &filter[time.Time]{
condition: database.NewNumberLess(now),
value: toPtr(now),
},
},
},
{
name: "EventCreatedAtBetween",
args: args{
opts: []EventFilterOpt{
EventCreatedAtBetween(now, now.Add(1*time.Second)),
},
},
want: &EventFilter{
createdAt: &filter[time.Time]{
condition: database.NewNumberBetween(now, now.Add(1*time.Second)),
min: toPtr(now),
max: toPtr(now.Add(1 * time.Second)),
},
},
},
{
name: "EventSequenceEquals",
args: args{
opts: []EventFilterOpt{
EventSequenceEquals(12),
},
},
want: &EventFilter{
sequence: &filter[uint32]{
condition: database.NewNumberEquals[uint32](12),
value: toPtr(uint32(12)),
},
},
},
{
name: "EventSequenceAtLeast",
args: args{
opts: []EventFilterOpt{
EventSequenceAtLeast(12),
},
},
want: &EventFilter{
sequence: &filter[uint32]{
condition: database.NewNumberAtLeast[uint32](12),
value: toPtr(uint32(12)),
},
},
},
{
name: "EventSequenceGreater",
args: args{
opts: []EventFilterOpt{
EventSequenceGreater(12),
},
},
want: &EventFilter{
sequence: &filter[uint32]{
condition: database.NewNumberGreater[uint32](12),
value: toPtr(uint32(12)),
},
},
},
{
name: "EventSequenceAtMost",
args: args{
opts: []EventFilterOpt{
EventSequenceAtMost(12),
},
},
want: &EventFilter{
sequence: &filter[uint32]{
condition: database.NewNumberAtMost[uint32](12),
value: toPtr(uint32(12)),
},
},
},
{
name: "EventSequenceLess",
args: args{
opts: []EventFilterOpt{
EventSequenceLess(12),
},
},
want: &EventFilter{
sequence: &filter[uint32]{
condition: database.NewNumberLess[uint32](12),
value: toPtr(uint32(12)),
},
},
},
{
name: "EventSequenceBetween",
args: args{
opts: []EventFilterOpt{
EventSequenceBetween(12, 24),
},
},
want: &EventFilter{
sequence: &filter[uint32]{
condition: database.NewNumberBetween[uint32](12, 24),
min: toPtr(uint32(12)),
max: toPtr(uint32(24)),
},
},
},
{
name: "EventCreatorsEqual",
args: args{
opts: []EventFilterOpt{
EventCreatorsEqual("cr", "ea", "tor"),
},
},
want: &EventFilter{
creators: &filter[[]string]{
condition: database.NewListEquals("cr", "ea", "tor"),
value: toPtr([]string{"cr", "ea", "tor"}),
},
},
},
{
name: "EventCreatorsEqual no params",
args: args{
opts: []EventFilterOpt{
EventCreatorsEqual(),
},
},
want: &EventFilter{},
},
{
name: "EventCreatorsEqual one params",
args: args{
opts: []EventFilterOpt{
EventCreatorsEqual("asdf"),
},
},
want: &EventFilter{
creators: &filter[[]string]{
condition: database.NewTextEqual("asdf"),
value: toPtr([]string{"asdf"}),
},
},
},
{
name: "EventCreatorsContains",
args: args{
opts: []EventFilterOpt{
EventCreatorsContains("cr", "ea", "tor"),
},
},
want: &EventFilter{
creators: &filter[[]string]{
condition: database.NewListContains("cr", "ea", "tor"),
value: toPtr([]string{"cr", "ea", "tor"}),
},
},
},
{
name: "EventCreatorsContains no params",
args: args{
opts: []EventFilterOpt{
EventCreatorsContains(),
},
},
want: &EventFilter{},
},
{
name: "EventCreatorsContains one params",
args: args{
opts: []EventFilterOpt{
EventCreatorsContains("asdf"),
},
},
want: &EventFilter{
creators: &filter[[]string]{
condition: database.NewTextEqual("asdf"),
value: toPtr([]string{"asdf"}),
},
},
},
{
name: "EventCreatorsNotContains",
args: args{
opts: []EventFilterOpt{
EventCreatorsNotContains("cr", "ea", "tor"),
},
},
want: &EventFilter{
creators: &filter[[]string]{
condition: database.NewListNotContains("cr", "ea", "tor"),
value: toPtr([]string{"cr", "ea", "tor"}),
},
},
},
{
name: "EventCreatorsNotContains no params",
args: args{
opts: []EventFilterOpt{
EventCreatorsNotContains(),
},
},
want: &EventFilter{},
},
{
name: "EventCreatorsNotContains one params",
args: args{
opts: []EventFilterOpt{
EventCreatorsNotContains("asdf"),
},
},
want: &EventFilter{
creators: &filter[[]string]{
condition: database.NewTextUnequal("asdf"),
value: toPtr([]string{"asdf"}),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewEventFilter(tt.args.opts...)
if !reflect.DeepEqual(tt.want.Types(), got.Types()) {
t.Errorf("unexpected types %v, want: %v", got.types, tt.want.types)
}
if !reflect.DeepEqual(tt.want.Revision(), got.Revision()) {
t.Errorf("unexpected revision %v, want: %v", got.revision, tt.want.revision)
}
if !reflect.DeepEqual(tt.want.CreatedAt(), got.CreatedAt()) {
t.Errorf("unexpected createdAt %v, want: %v", got.createdAt, tt.want.createdAt)
}
if !reflect.DeepEqual(tt.want.Sequence(), got.Sequence()) {
t.Errorf("unexpected sequence %v, want: %v", got.sequence, tt.want.sequence)
}
if !reflect.DeepEqual(tt.want.Creators(), got.Creators()) {
t.Errorf("unexpected creators %v, want: %v", got.creators, tt.want.creators)
}
})
}
}
func TestAggregateFilter(t *testing.T) {
type args struct {
opts []AggregateFilterOpt
}
tests := []struct {
name string
args args
want *AggregateFilter
}{
{
name: "AggregateID",
args: args{
opts: []AggregateFilterOpt{
SetAggregateID("asdf"),
},
},
want: &AggregateFilter{
ids: []string{"asdf"},
},
},
{
name: "AggregateIDs",
args: args{
opts: []AggregateFilterOpt{
AggregateIDs("a", "s"),
AggregateIDs("d", "f"),
},
},
want: &AggregateFilter{
ids: []string{"d", "f"},
},
},
{
name: "AggregateIDs",
args: args{
opts: []AggregateFilterOpt{
AppendAggregateIDs("a", "s"),
AppendAggregateIDs("d", "f"),
},
},
want: &AggregateFilter{
ids: []string{"a", "s", "d", "f"},
},
},
{
name: "AppendEvent",
args: args{
opts: []AggregateFilterOpt{
AppendEvent(AppendEventTypes("asdf")),
AppendEvent(AppendEventTypes("asdf")),
},
},
want: &AggregateFilter{
events: make([]*EventFilter, 2),
},
},
{
name: "AppendEvents",
args: args{
opts: []AggregateFilterOpt{
AppendEvents(NewEventFilter()),
AppendEvents(NewEventFilter()),
},
},
want: &AggregateFilter{
events: make([]*EventFilter, 2),
},
},
{
name: "Events",
args: args{
opts: []AggregateFilterOpt{
SetEvents(NewEventFilter()),
SetEvents(NewEventFilter()),
},
},
want: &AggregateFilter{
events: make([]*EventFilter, 1),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewAggregateFilter("", tt.args.opts...)
if tt.want.typ != got.typ {
t.Errorf("unexpected typ %v, want: %v", got.typ, tt.want.typ)
}
if !reflect.DeepEqual(tt.want.Type(), got.Type()) {
t.Errorf("unexpected typ %v, want: %v", got.typ, tt.want.typ)
}
if !reflect.DeepEqual(tt.want.IDs(), got.IDs()) {
t.Errorf("unexpected ids %v, want: %v", got.ids, tt.want.ids)
}
if len(tt.want.Events()) != len(got.Events()) {
t.Errorf("unexpected length of events %v, want: %v", len(got.events), len(tt.want.events))
}
})
}
}
func TestFilterOpt(t *testing.T) {
type args struct {
opts []FilterOpt
}
tests := []struct {
name string
args args
want *Filter
}{
{
name: "limit 1",
args: args{
opts: []FilterOpt{
FilterPagination(Limit(10)),
FilterPagination(Limit(1)),
},
},
want: &Filter{
pagination: &Pagination{
pagination: &database.Pagination{
Limit: 1,
},
},
},
},
{
name: "AppendAggregateFilter",
args: args{
opts: []FilterOpt{
AppendAggregateFilter("typ"),
AppendAggregateFilter("typ2"),
},
},
want: &Filter{
aggregateFilters: make([]*AggregateFilter, 2),
},
},
{
name: "AppendAggregateFilters",
args: args{
opts: []FilterOpt{
AppendAggregateFilters(NewAggregateFilter("typ")),
AppendAggregateFilters(NewAggregateFilter("typ2")),
},
},
want: &Filter{
aggregateFilters: make([]*AggregateFilter, 2),
},
},
{
name: "AggregateFilters",
args: args{
opts: []FilterOpt{
SetAggregateFilters(NewAggregateFilter("typ")),
SetAggregateFilters(NewAggregateFilter("typ2")),
},
},
want: &Filter{
aggregateFilters: make([]*AggregateFilter, 1),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewFilter(tt.args.opts...)
parent := NewQuery("instance", nil)
got.parent = parent
tt.want.parent = parent
if !reflect.DeepEqual(tt.want.Pagination(), got.Pagination()) {
t.Errorf("unexpected pagination %v, want: %v", got.pagination, tt.want.pagination)
}
if len(tt.want.AggregateFilters()) != len(got.AggregateFilters()) {
t.Errorf("unexpected length of aggregateFilters %v, want: %v", len(got.aggregateFilters), len(tt.want.aggregateFilters))
}
})
}
}
func TestQueryOpt(t *testing.T) {
type args struct {
opts []QueryOpt
}
var tx sql.Tx
tests := []struct {
name string
args args
want *Query
}{
{
name: "limit 1",
args: args{
opts: []QueryOpt{
QueryPagination(Limit(10)),
QueryPagination(Limit(1)),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
pagination: &Pagination{
pagination: &database.Pagination{
Limit: 1,
},
},
},
},
{
name: "with tx",
args: args{
opts: []QueryOpt{
SetQueryTx(&tx),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
tx: &tx,
},
},
{
name: "instance",
args: args{
opts: []QueryOpt{
SetInstance("instance2"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance2"),
value: toPtr([]string{"instance2"}),
},
},
},
{
name: "InstanceEqual no param",
args: args{
opts: []QueryOpt{
InstancesEqual(),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
},
},
{
name: "InstanceEqual 1 param",
args: args{
opts: []QueryOpt{
InstancesEqual("instance2"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance2"),
value: toPtr([]string{"instance2"}),
},
},
},
{
name: "InstanceEqual 2 params",
args: args{
opts: []QueryOpt{
InstancesEqual("instance2"),
InstancesEqual("inst", "ancestor"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewListEquals("inst", "ancestor"),
value: toPtr([]string{"inst", "ancestor"}),
},
},
},
{
name: "InstancesContains no param",
args: args{
opts: []QueryOpt{
InstancesContains(),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
},
},
{
name: "InstancesContains 1 param",
args: args{
opts: []QueryOpt{
InstancesContains("instance2"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance2"),
value: toPtr([]string{"instance2"}),
},
},
},
{
name: "InstancesContains 2 params",
args: args{
opts: []QueryOpt{
InstancesContains("instance2"),
InstancesContains("inst", "ancestor"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewListContains("inst", "ancestor"),
value: toPtr([]string{"inst", "ancestor"}),
},
},
},
{
name: "InstancesNotContains no param",
args: args{
opts: []QueryOpt{
InstancesNotContains(),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
},
},
{
name: "InstancesNotContains 1 param",
args: args{
opts: []QueryOpt{
InstancesNotContains("instance2"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextUnequal("instance2"),
value: toPtr([]string{"instance2"}),
},
},
},
{
name: "InstancesNotContains 2 params",
args: args{
opts: []QueryOpt{
InstancesNotContains("instance2"),
InstancesNotContains("inst", "ancestor"),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewListNotContains("inst", "ancestor"),
value: toPtr([]string{"inst", "ancestor"}),
},
},
},
{
name: "AppendFilters",
args: args{
opts: []QueryOpt{
AppendFilters(NewFilter()),
AppendFilters(NewFilter()),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
filters: make([]*Filter, 2),
},
},
{
name: "AppendFilter",
args: args{
opts: []QueryOpt{
AppendFilter(),
AppendFilter(),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
filters: make([]*Filter, 2),
},
},
{
name: "Filter",
args: args{
opts: []QueryOpt{
SetFilters(NewFilter()),
SetFilters(NewFilter()),
},
},
want: &Query{
instances: &filter[[]string]{
condition: database.NewTextEqual("instance"),
value: toPtr([]string{"instance"}),
},
filters: make([]*Filter, 1),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewQuery("instance", nil, tt.args.opts...)
if !reflect.DeepEqual(tt.want.Instance(), got.Instance()) {
t.Errorf("unexpected instances %v, want: %v", got.instances, tt.want.instances)
}
if len(tt.want.Filters()) != len(got.Filters()) {
t.Errorf("unexpected length of filters %v, want: %v", len(got.filters), len(tt.want.filters))
}
if !reflect.DeepEqual(tt.want.Tx(), got.Tx()) {
t.Errorf("unexpected tx %v, want: %v", got.tx, tt.want.tx)
}
if !reflect.DeepEqual(tt.want.Pagination(), got.Pagination()) {
t.Errorf("unexpected pagination %v, want: %v", got.pagination, tt.want.pagination)
}
})
}
}
func toPtr[T any](value T) *T {
return &value
}