fix: handle multiple statements for a single event in projections (#2313)

* fix: handle multiple statements for a single event in projections

* export func type

* fix test

* Update internal/eventstore/handler/crdb/statement.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/eventstore/handler/crdb/statement.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* change to pointers

* add error test case

Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
Livio Amstutz
2021-09-08 13:54:31 +02:00
committed by GitHub
parent ec6a3a1847
commit 1ac1492fd3
11 changed files with 546 additions and 301 deletions

View File

@@ -11,13 +11,18 @@ import (
)
type wantExecuter struct {
query string
args []interface{}
params []params
i int
t *testing.T
wasExecuted bool
shouldExecute bool
}
type params struct {
query string
args []interface{}
}
var errTestErr = errors.New("some error")
func (ex *wantExecuter) check(t *testing.T) {
@@ -34,12 +39,18 @@ func (ex *wantExecuter) check(t *testing.T) {
func (ex *wantExecuter) Exec(query string, args ...interface{}) (sql.Result, error) {
ex.t.Helper()
ex.wasExecuted = true
if query != ex.query {
ex.t.Errorf("wrong query:\n expected:\n %q\n got:\n %q", ex.query, query)
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
}
if !reflect.DeepEqual(ex.args, args) {
ex.t.Errorf("wrong args:\n expected:\n %v\n got:\n %v", ex.args, args)
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
}
@@ -137,9 +148,13 @@ func TestNewCreateStatement(t *testing.T) {
sequence: 1,
previousSequence: 1,
executer: &wantExecuter{
query: "INSERT INTO my_table (col1) VALUES ($1)",
params: []params{
{
query: "INSERT INTO my_table (col1) VALUES ($1)",
args: []interface{}{"val"},
},
},
shouldExecute: true,
args: []interface{}{"val"},
},
isErr: func(err error) bool {
return err == nil
@@ -255,9 +270,13 @@ func TestNewUpsertStatement(t *testing.T) {
sequence: 1,
previousSequence: 1,
executer: &wantExecuter{
query: "UPSERT INTO my_table (col1) VALUES ($1)",
params: []params{
{
query: "UPSERT INTO my_table (col1) VALUES ($1)",
args: []interface{}{"val"},
},
},
shouldExecute: true,
args: []interface{}{"val"},
},
isErr: func(err error) bool {
return err == nil
@@ -422,9 +441,13 @@ func TestNewUpdateStatement(t *testing.T) {
sequence: 1,
previousSequence: 1,
executer: &wantExecuter{
query: "UPDATE my_table SET (col1) = ($1) WHERE (col2 = $2)",
params: []params{
{
query: "UPDATE my_table SET (col1) = ($1) WHERE (col2 = $2)",
args: []interface{}{"val", 1},
},
},
shouldExecute: true,
args: []interface{}{"val", 1},
},
isErr: func(err error) bool {
return err == nil
@@ -541,9 +564,13 @@ func TestNewDeleteStatement(t *testing.T) {
sequence: 1,
previousSequence: 1,
executer: &wantExecuter{
query: "DELETE FROM my_table WHERE (col1 = $1)",
params: []params{
{
query: "DELETE FROM my_table WHERE (col1 = $1)",
args: []interface{}{1},
},
},
shouldExecute: true,
args: []interface{}{1},
},
isErr: func(err error) bool {
return err == nil
@@ -572,7 +599,7 @@ func TestNewNoOpStatement(t *testing.T) {
tests := []struct {
name string
args args
want handler.Statement
want *handler.Statement
}{
{
name: "generate correctly",
@@ -583,7 +610,7 @@ func TestNewNoOpStatement(t *testing.T) {
previousSequence: 3,
},
},
want: handler.Statement{
want: &handler.Statement{
AggregateType: "agg",
Execute: nil,
Sequence: 5,
@@ -600,6 +627,174 @@ func TestNewNoOpStatement(t *testing.T) {
}
}
func TestNewMultiStatement(t *testing.T) {
type args struct {
table string
event *testEvent
execs []func(eventstore.EventReader) 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: nil,
},
want: want{
executer: nil,
},
},
{
name: "no condition",
args: args{
table: "my_table",
event: &testEvent{
aggregateType: "agg",
sequence: 1,
previousSequence: 0,
},
execs: []func(eventstore.EventReader) Exec{
AddDeleteStatement(
[]handler.Condition{},
),
AddCreateStatement(
[]handler.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, handler.ErrNoCondition)
},
},
},
{
name: "correct",
args: args{
table: "my_table",
event: &testEvent{
sequence: 1,
previousSequence: 0,
aggregateType: "agg",
},
execs: []func(eventstore.EventReader) Exec{
AddDeleteStatement(
[]handler.Condition{
{
Name: "col1",
Value: 1,
},
}),
AddCreateStatement(
[]handler.Column{
{
Name: "col1",
Value: 1,
},
}),
AddUpsertStatement(
[]handler.Column{
{
Name: "col1",
Value: 1,
},
}),
AddUpdateStatement(
[]handler.Column{
{
Name: "col1",
Value: 1,
},
},
[]handler.Condition{
{
Name: "col1",
Value: 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: "UPSERT INTO my_table (col1) VALUES ($1)",
args: []interface{}{1},
},
{
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 TestStatement_Execute(t *testing.T) {
type fields struct {
execute func(ex handler.Executer, projectionName string) error