Files
zitadel/internal/query/projection/event_testing_relational_test.go
T
f138082b64 feat: add administrator repository (#11803)
Introduces the `administrators` relational table and repository layer
for the v3 backend, unifying instance, organization, project, and
project-grant membership into a single scoped model.

Closes #10220

## Changes

- **Domain**: `Administrator` struct, `AdministratorScope` enum,
`AdministratorRepository` interface with typed CRUD, column, condition,
and change helpers. Generated enumer and GoMock.
- **Migration** (`016`): `zitadel.administrators` table with a generated
`id`, scope-alignment check constraint, cascading foreign keys, and a
child `administrator_roles` table.
- **Repository**: CTE-based role mutations (`AddRole`, `RemoveRole`,
`SetRoles`) for atomicity. Roles aggregated via `ARRAY_AGG`.
- **Projection**: Event reducers for
`member.added/changed/removed/cascade_removed` across all four admin
scopes.
- **Fixes**: `Rows.Close()` error wrapping; `TextArray` nil-safe scan +
full `pgtype` array interface; `authorization` and `projectGrant` scan
helpers fixed for `ARRAY_AGG` under `database/sql`; removed unused `ctx`
parameter from `callReduce`.

---------

Co-authored-by: Marco Ardizzone <[email protected]>
2026-03-18 10:24:09 +01: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, 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(t.Context(), tx, "")
return assert.NoError(t, err)
}