mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-16 12:58:00 +00:00
9ec9ad4314
# Which Problems Are Solved id_tokens issued for auth requests created through the login UI currently do not provide a sid claim. This is due to the fact that (SSO) sessions for the login UI do not have one and are only computed by the userAgent(ID), the user(ID) and the authentication checks of the latter. This prevents client to track sessions and terminate specific session on the end_session_endpoint. # How the Problems Are Solved - An `id` column is added to the `auth.user_sessions` table. - The `id` (prefixed with `V1_`) is set whenever a session is added or updated to active (from terminated) - The id is passed to the `oidc session` (as v2 sessionIDs), to expose it as `sid` claim # Additional Changes - refactored `getUpdateCols` to handle different column value types and add arguments for query # Additional Context - closes #8499 - relates to #8501
1735 lines
35 KiB
Go
1735 lines
35 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/zitadel/zitadel/internal/database"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
)
|
|
|
|
type wantExecuter struct {
|
|
params []params
|
|
i int
|
|
t *testing.T
|
|
wasExecuted bool
|
|
shouldExecute bool
|
|
}
|
|
|
|
type params struct {
|
|
query string
|
|
args []interface{}
|
|
}
|
|
|
|
var errTest = errors.New("some error")
|
|
|
|
var _ eventstore.Event = &testEvent{}
|
|
|
|
type testEvent struct {
|
|
eventstore.BaseEvent
|
|
sequence uint64
|
|
previousSequence uint64
|
|
aggregateType eventstore.AggregateType
|
|
instanceID string
|
|
}
|
|
|
|
func (e *testEvent) Sequence() uint64 {
|
|
return e.sequence
|
|
}
|
|
|
|
func (e *testEvent) Aggregate() *eventstore.Aggregate {
|
|
return &eventstore.Aggregate{
|
|
Type: e.aggregateType,
|
|
InstanceID: e.instanceID,
|
|
}
|
|
}
|
|
|
|
func (e *testEvent) PreviousAggregateTypeSequence() uint64 {
|
|
return e.previousSequence
|
|
}
|
|
|
|
func (ex *wantExecuter) check(t *testing.T) {
|
|
t.Helper()
|
|
switch {
|
|
case ex.wasExecuted && !ex.shouldExecute:
|
|
t.Error("executer should not be executed")
|
|
case !ex.wasExecuted && ex.shouldExecute:
|
|
t.Error("executer should be executed")
|
|
case ex.wasExecuted != ex.shouldExecute:
|
|
t.Errorf("executed missmatched should be %t, but was %t", ex.shouldExecute, ex.wasExecuted)
|
|
}
|
|
}
|
|
|
|
func (ex *wantExecuter) Exec(query string, args ...interface{}) (sql.Result, error) {
|
|
ex.t.Helper()
|
|
if strings.Contains(query, "SAVEPOINT") {
|
|
return nil, nil
|
|
}
|
|
ex.wasExecuted = true
|
|
if ex.i >= len(ex.params) {
|
|
ex.t.Errorf("did not expect more exec, but got:\n %q with %q", query, args)
|
|
return nil, nil
|
|
}
|
|
p := ex.params[ex.i]
|
|
if query != p.query {
|
|
ex.t.Errorf("wrong query:\n expected:\n %q\n got:\n %q", p.query, query)
|
|
}
|
|
if !reflect.DeepEqual(p.args, args) {
|
|
ex.t.Errorf("wrong args:\n expected:\n %v\n got:\n %v", p.args, args)
|
|
}
|
|
ex.i++
|
|
return nil, nil
|
|
}
|
|
|
|
func TestNewCreateStatement(t *testing.T) {
|
|
type args struct {
|
|
table string
|
|
event *testEvent
|
|
values []Column
|
|
}
|
|
type want struct {
|
|
aggregateType eventstore.AggregateType
|
|
sequence uint64
|
|
previousSequence uint64
|
|
table string
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no table",
|
|
args: args{
|
|
table: "",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoProjection)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no values",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoValues)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (col1) VALUES ($1)",
|
|
args: []interface{}{"val"},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.want.executer.t = t
|
|
stmt := NewCreateStatement(tt.args.event, tt.args.values)
|
|
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewUpsertStatement(t *testing.T) {
|
|
type args struct {
|
|
table string
|
|
event *testEvent
|
|
conflictCols []Column
|
|
values []Column
|
|
}
|
|
type want struct {
|
|
aggregateType eventstore.AggregateType
|
|
sequence uint64
|
|
previousSequence uint64
|
|
table string
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no table",
|
|
args: args{
|
|
table: "",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoProjection)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no values",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoValues)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no update cols",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conflictCols: []Column{
|
|
NewCol("col1", nil),
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoValues)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct UPDATE multi col",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conflictCols: []Column{
|
|
NewCol("col1", nil),
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: "val",
|
|
},
|
|
{
|
|
Name: "col3",
|
|
Value: "val",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (col1, col2, col3) VALUES ($1, $2, $3) ON CONFLICT (col1) DO UPDATE SET (col2, col3) = (EXCLUDED.col2, EXCLUDED.col3)",
|
|
args: []interface{}{"val", "val", "val"},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct *onlySetValueOnInsert",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conflictCols: []Column{
|
|
NewCol("col1", nil),
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val1",
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: "val2",
|
|
},
|
|
{
|
|
Name: "col3",
|
|
Value: &onlySetValueOnInsert{
|
|
Table: "some.table",
|
|
Value: "val3",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (col1, col2, col3) VALUES ($1, $2, $3) ON CONFLICT (col1) DO UPDATE SET (col2, col3) = (EXCLUDED.col2, some.table.col3)",
|
|
args: []interface{}{"val1", "val2", "val3"},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct all *onlySetValueOnInsert",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conflictCols: []Column{
|
|
NewCol("col1", nil),
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val1",
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: &onlySetValueOnInsert{
|
|
Table: "some.table",
|
|
Value: "val2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (col1, col2) VALUES ($1, $2) ON CONFLICT (col1) DO UPDATE SET col2 = some.table.col2",
|
|
args: []interface{}{"val1", "val2"},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct all *onlySetValueInCase",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conflictCols: []Column{
|
|
NewCol("col1", nil),
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val1",
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: &onlySetValueInCase{
|
|
Table: "some.table",
|
|
Value: "val2",
|
|
Condition: ConditionOr(
|
|
ColumnChangedCondition("some.table", "val3", 0, 1),
|
|
ColumnIsNullCondition("some.table", "val3"),
|
|
),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (col1, col2) VALUES ($1, $2) ON CONFLICT (col1) DO UPDATE SET col2 = CASE WHEN some.table.val3 = $3 AND EXCLUDED.val3 = $4 OR some.table.val3 IS NULL THEN EXCLUDED.col2 ELSE some.table.col2 END",
|
|
args: []interface{}{"val1", "val2", 0, 1},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.want.executer.t = t
|
|
stmt := NewUpsertStatement(tt.args.event, tt.args.conflictCols, tt.args.values)
|
|
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewUpdateStatement(t *testing.T) {
|
|
type args struct {
|
|
table string
|
|
event *testEvent
|
|
values []Column
|
|
conditions []Condition
|
|
}
|
|
type want struct {
|
|
table string
|
|
aggregateType eventstore.AggregateType
|
|
sequence uint64
|
|
previousSequence uint64
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no table",
|
|
args: args{
|
|
table: "",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
conditions: []Condition{
|
|
NewCond("col2", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoProjection)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no values",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{},
|
|
conditions: []Condition{
|
|
NewCond("col2", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoValues)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no conditions",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
conditions: []Condition{},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoCondition)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct single column",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
},
|
|
conditions: []Condition{
|
|
NewCond("col2", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "UPDATE my_table SET col1 = $1 WHERE (col2 = $2)",
|
|
args: []interface{}{"val", 1},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct multi column",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
values: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: "val",
|
|
},
|
|
{
|
|
Name: "col3",
|
|
Value: "val5",
|
|
},
|
|
},
|
|
conditions: []Condition{
|
|
NewCond("col2", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "UPDATE my_table SET (col1, col3) = ($1, $2) WHERE (col2 = $3)",
|
|
args: []interface{}{"val", "val5", 1},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.want.executer.t = t
|
|
stmt := NewUpdateStatement(tt.args.event, tt.args.values, tt.args.conditions)
|
|
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewDeleteStatement(t *testing.T) {
|
|
type args struct {
|
|
table string
|
|
event *testEvent
|
|
conditions []Condition
|
|
}
|
|
|
|
type want struct {
|
|
table string
|
|
aggregateType eventstore.AggregateType
|
|
sequence uint64
|
|
previousSequence uint64
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no table",
|
|
args: args{
|
|
table: "",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conditions: []Condition{
|
|
NewCond("col2", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoProjection)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no conditions",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conditions: []Condition{},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoCondition)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
aggregateType: "agg",
|
|
},
|
|
conditions: []Condition{
|
|
NewCond("col1", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "DELETE FROM my_table WHERE (col1 = $1)",
|
|
args: []interface{}{1},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.want.executer.t = t
|
|
stmt := NewDeleteStatement(tt.args.event, tt.args.conditions)
|
|
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewNoOpStatement(t *testing.T) {
|
|
type args struct {
|
|
event *testEvent
|
|
table string
|
|
}
|
|
type want struct {
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "generate correctly",
|
|
args: args{
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 5,
|
|
previousSequence: 3,
|
|
instanceID: "instanceID",
|
|
},
|
|
},
|
|
want: want{
|
|
executer: nil,
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
stmt := NewNoOpStatement(tt.args.event)
|
|
if tt.want.executer != nil && stmt.Execute == nil {
|
|
t.Error("expected executer, but was nil")
|
|
}
|
|
if stmt.Execute == nil {
|
|
return
|
|
}
|
|
tt.want.executer.t = t
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewMultiStatement(t *testing.T) {
|
|
type args struct {
|
|
table string
|
|
event *testEvent
|
|
execs []func(eventstore.Event) Exec
|
|
}
|
|
|
|
type want struct {
|
|
table string
|
|
aggregateType eventstore.AggregateType
|
|
sequence uint64
|
|
previousSequence uint64
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no op",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
execs: []func(eventstore.Event) Exec{
|
|
AddNoOpStatement(),
|
|
},
|
|
},
|
|
want: want{
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no condition",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
execs: []func(eventstore.Event) Exec{
|
|
AddDeleteStatement(
|
|
[]Condition{},
|
|
),
|
|
AddCreateStatement(
|
|
[]Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoCondition)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
aggregateType: "agg",
|
|
},
|
|
execs: []func(eventstore.Event) Exec{
|
|
AddDeleteStatement(
|
|
[]Condition{
|
|
NewCond("col1", 1),
|
|
}),
|
|
AddCreateStatement(
|
|
[]Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
}),
|
|
AddUpsertStatement(
|
|
[]Column{
|
|
NewCol("col1", nil),
|
|
},
|
|
[]Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: 2,
|
|
},
|
|
}),
|
|
AddUpdateStatement(
|
|
[]Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
},
|
|
[]Condition{
|
|
NewCond("col1", 1),
|
|
}),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "DELETE FROM my_table WHERE (col1 = $1)",
|
|
args: []interface{}{1},
|
|
},
|
|
{
|
|
query: "INSERT INTO my_table (col1) VALUES ($1)",
|
|
args: []interface{}{1},
|
|
},
|
|
{
|
|
query: "INSERT INTO my_table (col1, col2) VALUES ($1, $2) ON CONFLICT (col1) DO UPDATE SET col2 = EXCLUDED.col2",
|
|
args: []interface{}{1, 2},
|
|
},
|
|
{
|
|
query: "UPDATE my_table SET col1 = $1 WHERE (col1 = $2)",
|
|
args: []interface{}{1, 1},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
stmt := NewMultiStatement(tt.args.event, tt.args.execs...)
|
|
|
|
if tt.want.executer != nil && stmt.Execute == nil {
|
|
t.Error("expected executer, but was nil")
|
|
}
|
|
if stmt.Execute == nil {
|
|
return
|
|
}
|
|
tt.want.executer.t = t
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewCopyStatement(t *testing.T) {
|
|
type args struct {
|
|
table string
|
|
event *testEvent
|
|
conflictingCols []Column
|
|
from []Column
|
|
to []Column
|
|
conds []NamespacedCondition
|
|
}
|
|
type want struct {
|
|
aggregateType eventstore.AggregateType
|
|
sequence uint64
|
|
previousSequence uint64
|
|
table string
|
|
executer *wantExecuter
|
|
isErr func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no table",
|
|
args: args{
|
|
table: "",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conds: []NamespacedCondition{
|
|
NewNamespacedCondition("col2", 1),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoProjection)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no conditions",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conds: []NamespacedCondition{},
|
|
from: []Column{
|
|
{
|
|
Name: "col",
|
|
},
|
|
},
|
|
to: []Column{
|
|
{
|
|
Name: "col",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoCondition)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "more to than from cols",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conds: []NamespacedCondition{},
|
|
from: []Column{
|
|
{
|
|
Name: "col",
|
|
},
|
|
},
|
|
to: []Column{
|
|
{
|
|
Name: "col",
|
|
},
|
|
{
|
|
Name: "col2",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoCondition)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "no columns",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
conds: []NamespacedCondition{
|
|
NewNamespacedCondition("col2", nil),
|
|
},
|
|
from: []Column{},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
shouldExecute: false,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, ErrNoValues)
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct same column names",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
from: []Column{
|
|
{
|
|
Name: "state",
|
|
Value: 1,
|
|
},
|
|
{
|
|
Name: "id",
|
|
},
|
|
{
|
|
Name: "col_a",
|
|
},
|
|
{
|
|
Name: "col_b",
|
|
},
|
|
},
|
|
to: []Column{
|
|
{
|
|
Name: "state",
|
|
},
|
|
{
|
|
Name: "id",
|
|
},
|
|
{
|
|
Name: "col_a",
|
|
},
|
|
{
|
|
Name: "col_b",
|
|
},
|
|
},
|
|
conds: []NamespacedCondition{
|
|
NewNamespacedCondition("id", 2),
|
|
NewNamespacedCondition("state", 3),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (state, id, col_a, col_b) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE (copy_table.id = $2) AND (copy_table.state = $3) ON CONFLICT () DO UPDATE SET (state, id, col_a, col_b) = ($1, EXCLUDED.id, EXCLUDED.col_a, EXCLUDED.col_b)",
|
|
args: []interface{}{1, 2, 3},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "correct different column names",
|
|
args: args{
|
|
table: "my_table",
|
|
event: &testEvent{
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 0,
|
|
},
|
|
from: []Column{
|
|
{
|
|
Value: 1,
|
|
},
|
|
{
|
|
Name: "id",
|
|
},
|
|
{
|
|
Name: "col_a",
|
|
},
|
|
{
|
|
Name: "col_b",
|
|
},
|
|
},
|
|
to: []Column{
|
|
{
|
|
Name: "state",
|
|
},
|
|
{
|
|
Name: "id",
|
|
},
|
|
{
|
|
Name: "col_c",
|
|
},
|
|
{
|
|
Name: "col_d",
|
|
},
|
|
},
|
|
conds: []NamespacedCondition{
|
|
NewNamespacedCondition("id", 2),
|
|
NewNamespacedCondition("state", 3),
|
|
},
|
|
},
|
|
want: want{
|
|
table: "my_table",
|
|
aggregateType: "agg",
|
|
sequence: 1,
|
|
previousSequence: 1,
|
|
executer: &wantExecuter{
|
|
params: []params{
|
|
{
|
|
query: "INSERT INTO my_table (state, id, col_c, col_d) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE (copy_table.id = $2) AND (copy_table.state = $3) ON CONFLICT () DO UPDATE SET (state, id, col_c, col_d) = ($1, EXCLUDED.id, EXCLUDED.col_a, EXCLUDED.col_b)",
|
|
args: []interface{}{1, 2, 3},
|
|
},
|
|
},
|
|
shouldExecute: true,
|
|
},
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.want.executer.t = t
|
|
stmt := NewCopyStatement(tt.args.event, tt.args.conflictingCols, tt.args.from, tt.args.to, tt.args.conds)
|
|
|
|
err := stmt.Execute(tt.want.executer, tt.args.table)
|
|
if !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
tt.want.executer.check(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStatement_Execute(t *testing.T) {
|
|
type fields struct {
|
|
execute func(ex Executer, projectionName string) error
|
|
}
|
|
type want struct {
|
|
isErr func(error) bool
|
|
}
|
|
type args struct {
|
|
projectionName string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
fields fields
|
|
want want
|
|
}{
|
|
{
|
|
name: "execute returns no error",
|
|
fields: fields{
|
|
execute: func(ex Executer, projectionName string) error { return nil },
|
|
},
|
|
args: args{
|
|
projectionName: "my_projection",
|
|
},
|
|
want: want{
|
|
isErr: func(err error) bool {
|
|
return err == nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "execute returns error",
|
|
args: args{
|
|
projectionName: "my_projection",
|
|
},
|
|
fields: fields{
|
|
execute: func(ex Executer, projectionName string) error { return errTest },
|
|
},
|
|
want: want{
|
|
isErr: func(err error) bool {
|
|
return errors.Is(err, errTest)
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
stmt := &Statement{
|
|
Execute: tt.fields.execute,
|
|
}
|
|
if err := stmt.Execute(nil, tt.args.projectionName); !tt.want.isErr(err) {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_columnsToQuery(t *testing.T) {
|
|
type args struct {
|
|
cols []Column
|
|
}
|
|
type want struct {
|
|
names []string
|
|
params []string
|
|
values []interface{}
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no columns",
|
|
args: args{},
|
|
want: want{
|
|
names: []string{},
|
|
params: []string{},
|
|
values: []interface{}{},
|
|
},
|
|
},
|
|
{
|
|
name: "one column",
|
|
args: args{
|
|
cols: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
names: []string{"col1"},
|
|
params: []string{"$1"},
|
|
values: []interface{}{1},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple columns",
|
|
args: args{
|
|
cols: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: 3.14,
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
names: []string{"col1", "col2"},
|
|
params: []string{"$1", "$2"},
|
|
values: []interface{}{1, 3.14},
|
|
},
|
|
},
|
|
{
|
|
name: "with copy column",
|
|
args: args{
|
|
cols: []Column{
|
|
{
|
|
Name: "col1",
|
|
Value: 1,
|
|
},
|
|
{
|
|
Name: "col2",
|
|
Value: Column{
|
|
Name: "col1",
|
|
},
|
|
},
|
|
{
|
|
Name: "col3",
|
|
Value: "something",
|
|
},
|
|
},
|
|
},
|
|
want: want{
|
|
names: []string{"col1", "col2", "col3"},
|
|
params: []string{"$1", "col1", "$2"},
|
|
values: []interface{}{1, "something"},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotNames, gotParameters, gotValues := columnsToQuery(tt.args.cols)
|
|
if !reflect.DeepEqual(gotNames, tt.want.names) {
|
|
t.Errorf("columnsToQuery() gotNames = %v, want %v", gotNames, tt.want.names)
|
|
}
|
|
if !reflect.DeepEqual(gotParameters, tt.want.params) {
|
|
t.Errorf("columnsToQuery() gotParameters = %v, want %v", gotParameters, tt.want.params)
|
|
}
|
|
if !reflect.DeepEqual(gotValues, tt.want.values) {
|
|
t.Errorf("columnsToQuery() gotValues = %v, want %v", gotValues, tt.want.values)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_columnsToWhere(t *testing.T) {
|
|
type args struct {
|
|
conds []Condition
|
|
paramOffset int
|
|
}
|
|
type want struct {
|
|
wheres []string
|
|
values []interface{}
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want want
|
|
}{
|
|
{
|
|
name: "no wheres",
|
|
args: args{},
|
|
want: want{
|
|
wheres: []string{},
|
|
values: []interface{}{},
|
|
},
|
|
},
|
|
{
|
|
name: "no offset",
|
|
args: args{
|
|
conds: []Condition{
|
|
NewCond("col1", "val1"),
|
|
},
|
|
paramOffset: 1,
|
|
},
|
|
want: want{
|
|
wheres: []string{"(col1 = $1)"},
|
|
values: []interface{}{"val1"},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple cols",
|
|
args: args{
|
|
conds: []Condition{
|
|
NewCond("col1", "val1"),
|
|
NewCond("col2", "val2"),
|
|
},
|
|
paramOffset: 1,
|
|
},
|
|
want: want{
|
|
wheres: []string{"(col1 = $1)", "(col2 = $2)"},
|
|
values: []interface{}{"val1", "val2"},
|
|
},
|
|
},
|
|
{
|
|
name: "2 offset",
|
|
args: args{
|
|
conds: []Condition{
|
|
NewCond("col1", "val1"),
|
|
},
|
|
paramOffset: 3,
|
|
},
|
|
want: want{
|
|
wheres: []string{"(col1 = $3)"},
|
|
values: []interface{}{"val1"},
|
|
},
|
|
},
|
|
{
|
|
name: "less than",
|
|
args: args{
|
|
conds: []Condition{
|
|
NewLessThanCond("col1", "val1"),
|
|
},
|
|
paramOffset: 1,
|
|
},
|
|
want: want{
|
|
wheres: []string{"(col1 < $1)"},
|
|
values: []interface{}{"val1"},
|
|
},
|
|
},
|
|
{
|
|
name: "is null",
|
|
args: args{
|
|
conds: []Condition{
|
|
NewIsNullCond("col1"),
|
|
},
|
|
},
|
|
want: want{
|
|
wheres: []string{"(col1 IS NULL)"},
|
|
values: []interface{}{},
|
|
},
|
|
},
|
|
{
|
|
name: "text array contains",
|
|
args: args{
|
|
conds: []Condition{
|
|
NewTextArrayContainsCond("col1", "val1"),
|
|
},
|
|
paramOffset: 1,
|
|
},
|
|
want: want{
|
|
wheres: []string{"(col1 @> $1)"},
|
|
values: []interface{}{database.TextArray[string]{"val1"}},
|
|
},
|
|
},
|
|
{
|
|
name: "not",
|
|
args: args{
|
|
conds: []Condition{
|
|
Not(NewCond("col1", "val1")),
|
|
},
|
|
paramOffset: 1,
|
|
},
|
|
want: want{
|
|
wheres: []string{"(NOT (col1 = $1))"},
|
|
values: []interface{}{"val1"},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotWheres, gotValues := conditionsToWhere(tt.args.conds, tt.args.paramOffset)
|
|
if !reflect.DeepEqual(gotWheres, tt.want.wheres) {
|
|
t.Errorf("columnsToWhere() gotWheres = %v, want %v", gotWheres, tt.want.wheres)
|
|
}
|
|
if !reflect.DeepEqual(gotValues, tt.want.values) {
|
|
t.Errorf("columnsToWhere() gotValues = %v, want %v", gotValues, tt.want.values)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParameterOpts(t *testing.T) {
|
|
type args struct {
|
|
column string
|
|
value interface{}
|
|
placeholder string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
constructor func(column string, value interface{}) Column
|
|
want string
|
|
}{
|
|
{
|
|
name: "NewArrayAppendCol",
|
|
args: args{
|
|
column: "testCol",
|
|
value: "val",
|
|
placeholder: "$1",
|
|
},
|
|
constructor: NewArrayAppendCol,
|
|
want: "array_append(testCol, $1)",
|
|
},
|
|
{
|
|
name: "NewArrayRemoveCol",
|
|
args: args{
|
|
column: "testCol",
|
|
value: "val",
|
|
placeholder: "$1",
|
|
},
|
|
constructor: NewArrayRemoveCol,
|
|
want: "array_remove(testCol, $1)",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
col := tt.constructor(tt.args.column, tt.args.value)
|
|
if param := col.ParameterOpt(tt.args.placeholder); param != tt.want {
|
|
t.Errorf("constructor() = %v, want %v", param, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// func TestHandler_reduce(t *testing.T) {
|
|
// type fields struct {
|
|
// projection Projection
|
|
// }
|
|
// type args struct {
|
|
// event eventstore.Event
|
|
// }
|
|
// tests := []struct {
|
|
// name string
|
|
// fields fields
|
|
// args args
|
|
// isErr func(t *testing.T, err error)
|
|
// shouldBeCalled bool
|
|
// }{
|
|
// {
|
|
// name: "",
|
|
// fields: fields{
|
|
// projection: &projection{
|
|
// reducers: []AggregateReducer{
|
|
// {
|
|
// Aggregate: "aggregate",
|
|
// EventRedusers: []EventReducer{
|
|
// {
|
|
// Event: "event",
|
|
// Reduce: (&mockEventReducer{
|
|
// statement: new(Statement),
|
|
// }).reduce,
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// },
|
|
// }
|
|
// for _, tt := range tests {
|
|
// if tt.isErr == nil {
|
|
// tt.isErr = func(t *testing.T, err error) {
|
|
// if err != nil {
|
|
// t.Error("expected no error got:", err)
|
|
// }
|
|
// }
|
|
// }
|
|
// t.Run(tt.name, func(t *testing.T) {
|
|
// h := &Handler{
|
|
// projection: tt.fields.projection,
|
|
// }
|
|
// got, err := h.reduce(tt.args.event)
|
|
// tt.isErr(t, err)
|
|
// if tt.shouldBeCalled != tt.
|
|
// if !reflect.DeepEqual(got, tt.want) {
|
|
// t.Errorf("Handler.reduce() = %v, want %v", got, tt.want)
|
|
// }
|
|
// })
|
|
// }
|
|
// }
|