mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-19 22:14:25 +00:00
## Summary This PR introduces the relational user repository for backend v3, including schema, repository APIs, reducers/projections, and tests for both human and machine users. ## What changed * Added user-focused relational migrations (users, verifications, metadata, PATs, machine keys, passkeys, identity provider links) and related FK wiring. * Implemented/expanded repository domain contracts and storage implementations for human and machine user flows. * Added support for machine secret handling and aligned access token type modeling. * Extended user domain models (verifications, password/email/phone/TOTP state, passkeys, metadata, invite handling, identity provider links). * Updated projection/reduction coverage and integration tests for key user lifecycle events. * Regenerated and updated mocks/enums plus supporting database change/column utilities. ## Notes for reviewers * This PR is large because it establishes the foundational relational user storage layer and related test scaffolding. Sorry for that. ## Closes Closes #10218 Closes #10224 Closes #10225 Closes #10209 Closes #10802 --------- Co-authored-by: Iraq Jaber <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Marco A. <[email protected]> Co-authored-by: Tim Möhlmann <[email protected]>
269 lines
7.0 KiB
Go
269 lines
7.0 KiB
Go
package database
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/muhlemmer/gu"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestChangeWrite(t *testing.T) {
|
|
type want struct {
|
|
stmt string
|
|
args []any
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
change Change
|
|
want want
|
|
}{
|
|
{
|
|
name: "change",
|
|
change: NewChange(NewColumn("table", "column"), "value"),
|
|
want: want{
|
|
stmt: "column = $1",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "change ptr to null",
|
|
change: NewChangePtr[int](NewColumn("table", "column"), nil),
|
|
want: want{
|
|
stmt: "column = NULL",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "change ptr to value",
|
|
change: NewChangePtr(NewColumn("table", "column"), gu.Ptr(42)),
|
|
want: want{
|
|
stmt: "column = $1",
|
|
args: []any{42},
|
|
},
|
|
},
|
|
{
|
|
name: "change to NULL",
|
|
change: NewChangeToNull(NewColumn("table", "column")),
|
|
want: want{
|
|
stmt: "column = NULL",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "multiple changes",
|
|
change: NewChanges(
|
|
NewChange(NewColumn("table", "column1"), "value1"),
|
|
NewChangePtr[int](NewColumn("table", "column2"), nil),
|
|
NewChange(NewColumn("table", "column3"), 123),
|
|
NewChangeToNull(NewColumn("table", "column4")),
|
|
),
|
|
want: want{
|
|
stmt: "column1 = $1, column2 = NULL, column3 = $2, column4 = NULL",
|
|
args: []any{"value1", 123},
|
|
},
|
|
},
|
|
{
|
|
name: "increment change",
|
|
change: NewIncrementColumnChange(NewColumn("table", "counter"), NewColumn("table", "counter")),
|
|
want: want{
|
|
stmt: "counter = table.counter + 1",
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var builder StatementBuilder
|
|
err := test.change.Write(&builder)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, test.want.stmt, builder.String())
|
|
require.Len(t, builder.Args(), len(test.want.args))
|
|
for i, arg := range test.want.args {
|
|
assert.Equal(t, arg, builder.Args()[i])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestChangeToStatement(t *testing.T) {
|
|
type want struct {
|
|
stmt string
|
|
args []any
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
prefillBuilder func(builder *StatementBuilder)
|
|
change *changeToStatement
|
|
want want
|
|
}{
|
|
{
|
|
name: "change to statement",
|
|
change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT 1")
|
|
}).(*changeToStatement),
|
|
want: want{
|
|
stmt: "column = (SELECT 1)",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "change to statement with args",
|
|
change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(42)
|
|
}).(*changeToStatement),
|
|
want: want{
|
|
stmt: "column = (SELECT $1)",
|
|
args: []any{42},
|
|
},
|
|
},
|
|
{
|
|
name: "change to statement with existing builder args",
|
|
prefillBuilder: func(builder *StatementBuilder) {
|
|
builder.WriteString("UPDATE table SET ")
|
|
assert.NoError(t, NewChanges(
|
|
NewChange(NewColumn("table", "field1"), "asdf"),
|
|
NewChangeToNull(NewColumn("table", "field2")),
|
|
NewChangeToColumn(NewColumn("table", "field3"), NewColumn("table", "field4")),
|
|
).Write(builder))
|
|
builder.WriteString(", ")
|
|
},
|
|
change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(42)
|
|
builder.WriteString(" FROM other_table WHERE ")
|
|
NewBooleanCondition(NewColumn("table", "id"), true).Write(builder)
|
|
}).(*changeToStatement),
|
|
want: want{
|
|
stmt: "UPDATE table SET field1 = $1, field2 = NULL, field3 = table.field4, column = (SELECT $2 FROM other_table WHERE table.id = $3)",
|
|
args: []any{"asdf", 42, true},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var builder StatementBuilder
|
|
if test.prefillBuilder != nil {
|
|
test.prefillBuilder(&builder)
|
|
}
|
|
assert.NoError(t, test.change.Write(&builder))
|
|
assert.Equal(t, test.want.stmt, builder.String())
|
|
assert.Equal(t, builder.Args(), test.want.args)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCTEChange(t *testing.T) {
|
|
type want struct {
|
|
stmt string
|
|
args []any
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
prefillBuilder func(builder *StatementBuilder)
|
|
afterCTE func(builder *StatementBuilder)
|
|
change *cteChange
|
|
want want
|
|
}{
|
|
{
|
|
name: "only CTE change",
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT 1 AS value")
|
|
},
|
|
nil,
|
|
).(*cteChange),
|
|
want: want{
|
|
stmt: "WITH cte AS (SELECT 1 AS value) ",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "with existing CTE",
|
|
prefillBuilder: func(builder *StatementBuilder) {
|
|
builder.WriteString("existing_cte AS (SELECT ")
|
|
builder.WriteArg(42)
|
|
builder.WriteString(" AS reason), ")
|
|
},
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
Columns{
|
|
NewColumn("table", "column1"),
|
|
NewColumn("table", "column2"),
|
|
}.WriteQualified(builder)
|
|
builder.WriteString(", ")
|
|
builder.WriteArgs("asdf", 123, false, NullInstruction)
|
|
},
|
|
nil,
|
|
).(*cteChange),
|
|
want: want{
|
|
stmt: "WITH existing_cte AS (SELECT $1 AS reason), cte AS (SELECT table.column1, table.column2, $2, $3, $4, NULL) ",
|
|
args: []any{42, "asdf", 123, false},
|
|
},
|
|
},
|
|
{
|
|
name: "CTE change with after CTE statements",
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(1)
|
|
builder.WriteString(" AS value")
|
|
},
|
|
nil,
|
|
).(*cteChange),
|
|
afterCTE: func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT * FROM cte ")
|
|
},
|
|
want: want{
|
|
stmt: "WITH cte AS (SELECT $1 AS value) SELECT * FROM cte ",
|
|
args: []any{1},
|
|
},
|
|
},
|
|
{
|
|
name: "CTE change with column change after CTE",
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(1)
|
|
builder.WriteString(" AS value")
|
|
},
|
|
func(name string) Change {
|
|
return NewChangeToStatement(NewColumn("table", "field"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT * FROM ")
|
|
builder.WriteString(name)
|
|
builder.WriteString(" WHERE ")
|
|
NewColumnCondition(NewColumn(name, "value"), NewColumn("table", "value")).Write(builder)
|
|
})
|
|
},
|
|
).(*cteChange),
|
|
afterCTE: func(builder *StatementBuilder) {
|
|
builder.WriteString("UPDATE test SET ")
|
|
},
|
|
want: want{
|
|
stmt: "WITH cte AS (SELECT $1 AS value) UPDATE test SET field = (SELECT * FROM cte WHERE cte.value = table.value)",
|
|
args: []any{1},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var builder StatementBuilder
|
|
builder.WriteString("WITH ")
|
|
if test.prefillBuilder != nil {
|
|
test.prefillBuilder(&builder)
|
|
}
|
|
|
|
builder.WriteString("cte AS (")
|
|
test.change.SetName("cte")
|
|
test.change.WriteCTE(&builder)
|
|
builder.WriteString(") ")
|
|
|
|
if test.afterCTE != nil {
|
|
test.afterCTE(&builder)
|
|
}
|
|
assert.NoError(t, test.change.Write(&builder))
|
|
|
|
assert.Equal(t, test.want.stmt, builder.String())
|
|
assert.Equal(t, builder.Args(), test.want.args)
|
|
})
|
|
}
|
|
}
|