Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
241 lines
6.6 KiB
Go
241 lines
6.6 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package batch
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid"
|
|
"github.com/jmoiron/sqlx/reflectx"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ory/kratos/identity"
|
|
"github.com/ory/x/dbal"
|
|
"github.com/ory/x/snapshotx"
|
|
"github.com/ory/x/sqlxx"
|
|
)
|
|
|
|
type (
|
|
testModel struct {
|
|
ID uuid.UUID `db:"id"`
|
|
NID uuid.UUID `db:"nid"`
|
|
String string `db:"string"`
|
|
Int int `db:"int"`
|
|
Traits identity.Traits `db:"traits"`
|
|
NullTimePtr *sqlxx.NullTime `db:"null_time_ptr"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
|
}
|
|
testQuoter struct{}
|
|
)
|
|
|
|
func (i testModel) TableName(ctx context.Context) string {
|
|
return "test_models"
|
|
}
|
|
|
|
func (tq testQuoter) Quote(s string) string { return fmt.Sprintf("%q", s) }
|
|
|
|
func makeModels[T any]() []*T {
|
|
models := make([]*T, 10)
|
|
for k := range models {
|
|
models[k] = new(T)
|
|
}
|
|
return models
|
|
}
|
|
|
|
func Test_buildInsertQueryArgs(t *testing.T) {
|
|
ctx := context.Background()
|
|
t.Run("case=testModel", func(t *testing.T) {
|
|
models := makeModels[testModel]()
|
|
opts := &createOpts{
|
|
dialect: "other",
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
snapshotx.SnapshotT(t, args)
|
|
|
|
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES\n%s", args.TableName, args.ColumnsDecl, args.Placeholders)
|
|
assert.Equal(t, `INSERT INTO "test_models" ("created_at", "id", "int", "nid", "null_time_ptr", "string", "traits", "updated_at") VALUES
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?),
|
|
(?, ?, ?, ?, ?, ?, ?, ?)`, query)
|
|
})
|
|
|
|
t.Run("case=Identities", func(t *testing.T) {
|
|
models := makeModels[identity.Identity]()
|
|
opts := &createOpts{
|
|
dialect: "other",
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
snapshotx.SnapshotT(t, args)
|
|
})
|
|
|
|
t.Run("case=RecoveryAddress", func(t *testing.T) {
|
|
models := makeModels[identity.RecoveryAddress]()
|
|
opts := &createOpts{
|
|
dialect: "other",
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
snapshotx.SnapshotT(t, args)
|
|
})
|
|
|
|
t.Run("case=RecoveryAddress", func(t *testing.T) {
|
|
models := makeModels[identity.RecoveryAddress]()
|
|
opts := &createOpts{
|
|
dialect: "other",
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
snapshotx.SnapshotT(t, args)
|
|
})
|
|
|
|
t.Run("case=cockroach", func(t *testing.T) {
|
|
models := makeModels[testModel]()
|
|
for k := range models {
|
|
if k%3 == 0 {
|
|
models[k].ID = uuid.FromStringOrNil(fmt.Sprintf("ae0125a9-2786-4ada-82d2-d169cf75047%d", k))
|
|
}
|
|
}
|
|
opts := &createOpts{
|
|
dialect: dbal.DriverCockroachDB,
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
snapshotx.SnapshotT(t, args)
|
|
})
|
|
|
|
for _, dialect := range []string{dbal.DriverPostgreSQL, dbal.DriverCockroachDB, dbal.DriverYugabyteDB} {
|
|
t.Run("case=database generated UUID/"+dialect, func(t *testing.T) {
|
|
models := makeModels[testModel]()
|
|
opts := &createOpts{
|
|
dialect: dialect,
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
assert.Contains(t, args.Placeholders, "gen_random_uuid()")
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_buildInsertQueryArgs_ExtraColumns(t *testing.T) {
|
|
ctx := context.Background()
|
|
t.Run("case=testModel", func(t *testing.T) {
|
|
models := makeModels[testModel]()
|
|
opts := &createOpts{
|
|
dialect: "other",
|
|
quoter: testQuoter{},
|
|
mapper: reflectx.NewMapper("db"),
|
|
extraColumns: []identity.ExtraColumn{{K: "crdb_region", V: "gcp-europe-west3"}},
|
|
}
|
|
args := buildInsertQueryArgs(ctx, models, opts)
|
|
snapshotx.SnapshotT(t, args)
|
|
|
|
assert.Contains(t, args.ColumnsDecl, `"crdb_region"`)
|
|
assert.Contains(t, args.Placeholders, "?")
|
|
})
|
|
}
|
|
|
|
func Test_buildInsertQueryValues_ExtraColumns(t *testing.T) {
|
|
t.Run("case=testModel", func(t *testing.T) {
|
|
model := &testModel{
|
|
String: "string",
|
|
Int: 42,
|
|
Traits: []byte(`{"foo": "bar"}`),
|
|
}
|
|
|
|
frozenTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
opts := &createOpts{
|
|
dialect: "other",
|
|
mapper: reflectx.NewMapper("db"),
|
|
quoter: testQuoter{},
|
|
now: func() time.Time { return frozenTime },
|
|
extraColumns: []identity.ExtraColumn{{K: "crdb_region", V: "gcp-europe-west3"}},
|
|
}
|
|
|
|
// columns includes the extra column name, mirroring what buildInsertQueryArgs returns.
|
|
values, err := buildInsertQueryValues(
|
|
[]string{"created_at", "updated_at", "id", "string", "int", "null_time_ptr", "traits", "crdb_region"},
|
|
[]*testModel{model},
|
|
opts,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// The last value should be the extra column value.
|
|
// 7 model fields + 1 extra column = 8 total.
|
|
require.Len(t, values, 8)
|
|
assert.Equal(t, "gcp-europe-west3", values[len(values)-1])
|
|
})
|
|
}
|
|
|
|
func Test_buildInsertQueryValues(t *testing.T) {
|
|
t.Run("case=testModel", func(t *testing.T) {
|
|
model := &testModel{
|
|
String: "string",
|
|
Int: 42,
|
|
Traits: []byte(`{"foo": "bar"}`),
|
|
}
|
|
|
|
frozenTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
opts := &createOpts{
|
|
mapper: reflectx.NewMapper("db"),
|
|
quoter: testQuoter{},
|
|
now: func() time.Time { return frozenTime },
|
|
}
|
|
|
|
t.Run("case=cockroach", func(t *testing.T) {
|
|
opts.dialect = dbal.DriverCockroachDB
|
|
values, err := buildInsertQueryValues(
|
|
[]string{"created_at", "updated_at", "id", "string", "int", "null_time_ptr", "traits"},
|
|
[]*testModel{model},
|
|
opts,
|
|
)
|
|
require.NoError(t, err)
|
|
snapshotx.SnapshotT(t, values)
|
|
})
|
|
|
|
t.Run("case=others", func(t *testing.T) {
|
|
opts.dialect = "other"
|
|
values, err := buildInsertQueryValues(
|
|
[]string{"created_at", "updated_at", "id", "string", "int", "null_time_ptr", "traits"},
|
|
[]*testModel{model},
|
|
opts,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, frozenTime, model.CreatedAt)
|
|
assert.Equal(t, model.CreatedAt, values[0])
|
|
|
|
assert.Equal(t, frozenTime, model.UpdatedAt)
|
|
assert.Equal(t, model.UpdatedAt, values[1])
|
|
|
|
assert.NotZero(t, model.ID)
|
|
assert.Equal(t, model.ID, values[2])
|
|
|
|
assert.Equal(t, model.String, values[3])
|
|
assert.Equal(t, model.Int, values[4])
|
|
|
|
assert.Nil(t, model.NullTimePtr)
|
|
})
|
|
})
|
|
}
|