mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved As part of going to a relation storage model, we need to implement the repository for handling sessions and their events. # How the Problems Are Solved - Adds a migration step to create the necessary tables and types in the database. - Adds the repository implementation for sessions. - Implements a projection handler for session events to handle the events written by the old command handlers. - Updates the `Session` type and `SessionRepository` interface: - added `CreatorID` - Adjusted `Delete` method to return amount of affected columns - Removed sub-repository clumns from the main interface - Added `CreatorIDCondition`, `ExpirationCondition`, `ExistsFactor` and `ExistsMetadata` - Removed `SetUserAgent` - Methods of `SessionFactor` and `SessionChallenge` have been made public - fixed enumer on `SessionFactorType` - Adds a `JSON` type to the repository package to easy can JSON columns, resp. jsonb object inside a property of a `JSONArray` object. - Adds a `NoChange` interface, which can be implemented to signal that a `Change` interface implementation is not writing any changes to the statement builder, e.g. `CTEChange` # Additional Changes - Fixes the operators enums. `TextOperations` had wrong comments and `NumberOperations` additionally also had from comparisons. - Fixes downgrade of migration step 008. # Additional Context - closes https://github.com/zitadel/zitadel/issues/10212
422 lines
11 KiB
Go
422 lines
11 KiB
Go
package database
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func Test_writeOperation(t *testing.T) {
|
|
type want struct {
|
|
shouldPanic bool
|
|
stmt string
|
|
args []any
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
write func(builder *StatementBuilder)
|
|
// col Column
|
|
// op string
|
|
// value any
|
|
want want
|
|
}{
|
|
{
|
|
name: "unsupported operation panics",
|
|
write: func(builder *StatementBuilder) {
|
|
writeOperation[string](builder, NewColumn("table", "column"), "", "value")
|
|
},
|
|
want: want{
|
|
shouldPanic: true,
|
|
},
|
|
},
|
|
{
|
|
name: "unsupported value type panics",
|
|
write: func(builder *StatementBuilder) {
|
|
writeOperation[string](builder, NewColumn("table", "column"), " = ", struct{}{})
|
|
},
|
|
want: want{
|
|
shouldPanic: true,
|
|
},
|
|
},
|
|
{
|
|
name: "unsupported wrapped value type panics",
|
|
write: func(builder *StatementBuilder) {
|
|
writeOperation[string](builder, NewColumn("table", "column"), " = ", SHA256Value(123))
|
|
},
|
|
want: want{
|
|
shouldPanic: true,
|
|
},
|
|
},
|
|
{
|
|
name: "text equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, NewColumn("table", "column"), TextOperationEqual, "value")
|
|
},
|
|
want: want{
|
|
stmt: "table.column = $1",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text not equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, NewColumn("table", "column"), TextOperationNotEqual, "value")
|
|
},
|
|
want: want{
|
|
stmt: "table.column <> $1",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text starts with",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, NewColumn("table", "column"), TextOperationStartsWith, "value")
|
|
},
|
|
want: want{
|
|
stmt: "table.column LIKE $1 || '%'",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text ends with",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, NewColumn("table", "column"), TextOperationEndsWith, "value")
|
|
},
|
|
want: want{
|
|
stmt: "table.column LIKE '%' || $1",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text contains",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, NewColumn("table", "column"), TextOperationContains, "value")
|
|
},
|
|
want: want{
|
|
stmt: "table.column LIKE '%' || $1 || '%'",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text equal with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, LowerColumn(NewColumn("table", "column")), TextOperationEqual, LowerValue("value"))
|
|
},
|
|
want: want{
|
|
stmt: "LOWER(table.column) = LOWER($1)",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text not equal with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, LowerColumn(NewColumn("table", "column")), TextOperationNotEqual, LowerValue("value"))
|
|
},
|
|
want: want{
|
|
stmt: "LOWER(table.column) <> LOWER($1)",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text starts with with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, LowerColumn(NewColumn("table", "column")), TextOperationStartsWith, LowerValue("value"))
|
|
},
|
|
want: want{
|
|
stmt: "LOWER(table.column) LIKE LOWER($1) || '%'",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text ends with with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, LowerColumn(NewColumn("table", "column")), TextOperationEndsWith, LowerValue("value"))
|
|
},
|
|
want: want{
|
|
stmt: "LOWER(table.column) LIKE '%' || LOWER($1)",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "text contains with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeTextOperation[string](builder, LowerColumn(NewColumn("table", "column")), TextOperationContains, LowerValue("value"))
|
|
},
|
|
want: want{
|
|
stmt: "LOWER(table.column) LIKE '%' || LOWER($1) || '%'",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "number equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeNumberOperation[int](builder, NewColumn("table", "column"), NumberOperationEqual, 123)
|
|
},
|
|
want: want{
|
|
stmt: "table.column = $1",
|
|
args: []any{123},
|
|
},
|
|
},
|
|
{
|
|
name: "number not equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeNumberOperation[int](builder, NewColumn("table", "column"), NumberOperationNotEqual, 123)
|
|
},
|
|
want: want{
|
|
stmt: "table.column <> $1",
|
|
args: []any{123},
|
|
},
|
|
},
|
|
{
|
|
name: "number less than",
|
|
write: func(builder *StatementBuilder) {
|
|
writeNumberOperation[int](builder, NewColumn("table", "column"), NumberOperationLessThan, 123)
|
|
},
|
|
want: want{
|
|
stmt: "table.column < $1",
|
|
args: []any{123},
|
|
},
|
|
},
|
|
{
|
|
name: "number less than or equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeNumberOperation[int](builder, NewColumn("table", "column"), NumberOperationLessThanOrEqual, 123)
|
|
},
|
|
want: want{
|
|
stmt: "table.column <= $1",
|
|
args: []any{123},
|
|
},
|
|
},
|
|
{
|
|
name: "number greater than",
|
|
write: func(builder *StatementBuilder) {
|
|
writeNumberOperation[int](builder, NewColumn("table", "column"), NumberOperationGreaterThan, 123)
|
|
},
|
|
want: want{
|
|
stmt: "table.column > $1",
|
|
args: []any{123},
|
|
},
|
|
},
|
|
{
|
|
name: "number greater than or equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeNumberOperation[int](builder, NewColumn("table", "column"), NumberOperationGreaterThanOrEqual, 123)
|
|
},
|
|
want: want{
|
|
stmt: "table.column >= $1",
|
|
args: []any{123},
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "boolean is true",
|
|
write: func(builder *StatementBuilder) {
|
|
writeBooleanOperation[bool](builder, NewColumn("table", "column"), true)
|
|
},
|
|
want: want{
|
|
stmt: "table.column = $1",
|
|
args: []any{true},
|
|
},
|
|
},
|
|
{
|
|
name: "boolean is false",
|
|
write: func(builder *StatementBuilder) {
|
|
writeBooleanOperation[bool](builder, NewColumn("table", "column"), false)
|
|
},
|
|
want: want{
|
|
stmt: "table.column = $1",
|
|
args: []any{false},
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "bytes equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeBytesOperation[[]byte](builder, NewColumn("table", "column"), BytesOperationEqual, []byte{0x01, 0x02, 0x03})
|
|
},
|
|
want: want{
|
|
stmt: "table.column = $1",
|
|
args: []any{[]byte{0x01, 0x02, 0x03}},
|
|
},
|
|
},
|
|
{
|
|
name: "bytes not equal",
|
|
write: func(builder *StatementBuilder) {
|
|
writeBytesOperation[[]byte](builder, NewColumn("table", "column"), BytesOperationNotEqual, []byte{0x01, 0x02, 0x03})
|
|
},
|
|
want: want{
|
|
stmt: "table.column <> $1",
|
|
args: []any{[]byte{0x01, 0x02, 0x03}},
|
|
},
|
|
},
|
|
{
|
|
name: "bytes equal with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeBytesOperation[[]byte](builder, SHA256Column(NewColumn("table", "column")), BytesOperationEqual, SHA256Value([]byte{0x01, 0x02, 0x03}))
|
|
},
|
|
want: want{
|
|
stmt: "SHA256(table.column) = SHA256($1)",
|
|
args: []any{[]byte{0x01, 0x02, 0x03}},
|
|
},
|
|
},
|
|
{
|
|
name: "bytes not equal with wrapped value",
|
|
write: func(builder *StatementBuilder) {
|
|
writeBytesOperation[[]byte](builder, SHA256Column(NewColumn("table", "column")), BytesOperationNotEqual, SHA256Value([]byte{0x01, 0x02, 0x03}))
|
|
},
|
|
want: want{
|
|
stmt: "SHA256(table.column) <> SHA256($1)",
|
|
args: []any{[]byte{0x01, 0x02, 0x03}},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
defer func() {
|
|
r := recover()
|
|
assert.Equal(t, tt.want.shouldPanic, r != nil)
|
|
}()
|
|
var builder StatementBuilder
|
|
tt.write(&builder)
|
|
|
|
assert.Equal(t, tt.want.stmt, builder.String())
|
|
assert.Equal(t, tt.want.args, builder.Args())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTextOperation_Value(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
op TextOperation
|
|
want TextOperation
|
|
}{
|
|
{
|
|
name: "TextOperationStartsWith returns self",
|
|
op: TextOperationStartsWith,
|
|
want: TextOperationStartsWith,
|
|
},
|
|
{
|
|
name: "TextOperationContains returns self",
|
|
op: TextOperationContains,
|
|
want: TextOperationContains,
|
|
},
|
|
{
|
|
name: "TextOperationEndsWith returns self",
|
|
op: TextOperationEndsWith,
|
|
want: TextOperationEndsWith,
|
|
},
|
|
{
|
|
name: "TextOperationEqual returns self",
|
|
op: TextOperationEqual,
|
|
want: TextOperationEqual,
|
|
},
|
|
{
|
|
name: "TextOperationNotEqual returns self",
|
|
op: TextOperationNotEqual,
|
|
want: TextOperationNotEqual,
|
|
},
|
|
{
|
|
name: "TextOperationContainsIgnoreCase returns Contains",
|
|
op: TextOperationContainsIgnoreCase,
|
|
want: TextOperationContains,
|
|
},
|
|
{
|
|
name: "TextOperationEndsWithIgnoreCase returns EndsWith",
|
|
op: TextOperationEndsWithIgnoreCase,
|
|
want: TextOperationEndsWith,
|
|
},
|
|
{
|
|
name: "TextOperationStartsWithIgnoreCase returns StartsWith",
|
|
op: TextOperationStartsWithIgnoreCase,
|
|
want: TextOperationStartsWith,
|
|
},
|
|
{
|
|
name: "TextOperationEqualIgnoreCase returns Equal",
|
|
op: TextOperationEqualIgnoreCase,
|
|
want: TextOperationEqual,
|
|
},
|
|
{
|
|
name: "TextOperationNotEqualIgnoreCase returns NotEqual",
|
|
op: TextOperationNotEqualIgnoreCase,
|
|
want: TextOperationNotEqual,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := tt.op.Value()
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTextOperation_IsIgnoreCaseOperation(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
op TextOperation
|
|
want bool
|
|
}{
|
|
{
|
|
name: "TextOperationContainsIgnoreCase returns true",
|
|
op: TextOperationContainsIgnoreCase,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "TextOperationEndsWithIgnoreCase returns true",
|
|
op: TextOperationEndsWithIgnoreCase,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "TextOperationStartsWithIgnoreCase returns true",
|
|
op: TextOperationStartsWithIgnoreCase,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "TextOperationEqualIgnoreCase returns true",
|
|
op: TextOperationEqualIgnoreCase,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "TextOperationNotEqualIgnoreCase returns true",
|
|
op: TextOperationNotEqualIgnoreCase,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "TextOperationContains returns false",
|
|
op: TextOperationContains,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "TextOperationEndsWith returns false",
|
|
op: TextOperationEndsWith,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "TextOperationStartsWith returns false",
|
|
op: TextOperationStartsWith,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "TextOperationEqual returns false",
|
|
op: TextOperationEqual,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "TextOperationNotEqual returns false",
|
|
op: TextOperationNotEqual,
|
|
want: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := tt.op.IsIgnoreCaseOperation()
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|