Files
zitadel/internal/query/projection/event_testing_relational_test.go
T
Marco A.andGitHub 3ebeb61b80 feat: test event reduction to relational tables (#11718)
# Which Problems Are Solved

This PR provides an example for testing DB reduction to relational
tables without exploiting integration tests.

# How the Problems Are Solved

- Set up a disposable embedded Postgres DB used to store the results of
the calls to the reducers and verify such results
- Add utility method to return transactions from the existing DB pool.
These transactions can be used by the single test cases to insert and
read data from DB
- Add a test to verify that the handler contains the expected reducers
(checking also that they are on the right aggregate type)
- Add tests to verify the single reduce functions. A parent test creates
the required objects to satisfy the foreign key constraints.

# Additional Changes

  - Typo fixed in `postgres.DecodeConfig()`
  - Discard unneeded `projectionName` parameter for `Executor` function
 
# Additional Context

- Closes #11430
2026-03-02 16:44:51 +00:00

117 lines
2.9 KiB
Go

package projection
import (
"context"
"database/sql"
"fmt"
"log"
"os"
"slices"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres/embedded"
v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
)
var (
pool database.PoolTest
rawDB *sql.DB
)
func TestMain(m *testing.M) {
os.Exit(runTests(m))
}
func runTests(m *testing.M) int {
var stop func()
var err error
ctx := context.Background()
pool, stop, err = newEmbeddedDB(ctx)
if err != nil {
log.Printf("error with embedded postgres database: %v", err)
return 1
}
defer stop()
rawDB = pool.RawDB()
defer rawDB.Close()
defer pool.Close(ctx)
return m.Run()
}
func newEmbeddedDB(ctx context.Context) (pool database.PoolTest, stop func(), err error) {
connector, stop, err := embedded.StartEmbedded()
if err != nil {
return nil, nil, fmt.Errorf("unable to start embedded postgres: %w", err)
}
dummyPool, err := connector.Connect(ctx)
if err != nil {
return nil, stop, fmt.Errorf("unable to connect to embedded postgres: %w", err)
}
pool, ok := dummyPool.(database.PoolTest)
if !ok {
return nil, stop, fmt.Errorf("expecting database.PoolTest, got %T", dummyPool)
}
err = pool.MigrateTest(ctx)
if err != nil {
return nil, stop, fmt.Errorf("unable to migrate database: %w", err)
}
return pool, stop, err
}
func getTransactions(t *testing.T) (rawTx *sql.Tx, v3SQLTx *v3_sql.Transaction) {
rawTx, err := rawDB.Begin()
require.NoError(t, err)
v3SQLTx = v3_sql.SQLTx(rawTx)
return
}
// callReduce on the input projection tests that:
//
// 1. the aggregate type of the input event exists
// 2. the event type of the input event exists for that aggregate type
// 3. the reducer function for the input event is not nil
// 4. the reducer function for the input event executes without errors
//
// Returns false if any of the above checks fails
func callReduce(t *testing.T, ctx context.Context, tx *sql.Tx, projection handler.Projection, event eventstore.Event) bool {
reducers := projection.Reducers()
aggregateReducerIdx := slices.IndexFunc(reducers, func(r handler.AggregateReducer) bool {
return r.Aggregate == event.Aggregate().Type
})
if !assert.Greater(t, aggregateReducerIdx, -1) {
return false
}
eventReducerIdx := slices.IndexFunc(reducers[aggregateReducerIdx].EventReducers, func(er handler.EventReducer) bool {
return er.Event == event.Type()
})
if !assert.Greater(t, eventReducerIdx, -1) {
return false
}
reduceFn := reducers[aggregateReducerIdx].EventReducers[eventReducerIdx].Reduce
if !assert.NotNil(t, reduceFn) {
return false
}
stmt, err := reduceFn(event)
if !assert.NoError(t, err) {
return false
}
err = stmt.Execute(ctx, tx, "")
return assert.NoError(t, err)
}