snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
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
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
This commit is contained in:
62
persistence/sql/identity/partial_inserts_internal_test.go
Normal file
62
persistence/sql/identity/partial_inserts_internal_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright © 2026 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package identity
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/x/sqlxx"
|
||||
)
|
||||
|
||||
func TestIdentitiesNeedPartialInserts(t *testing.T) {
|
||||
withExternalID := func(id string) *identity.Identity {
|
||||
return &identity.Identity{ExternalID: sqlxx.NullString(id)}
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
identities []*identity.Identity
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty batch",
|
||||
identities: nil,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "single identity without external id",
|
||||
identities: []*identity.Identity{{}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "single identity with external id",
|
||||
identities: []*identity.Identity{withExternalID("ext-1")},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
// The identities insert cannot conflict without an external id (the
|
||||
// only unique constraint), so partial inserts are unnecessary.
|
||||
name: "many identities without external ids",
|
||||
identities: []*identity.Identity{{}, {}, {}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "many identities, one with external id",
|
||||
identities: []*identity.Identity{{}, withExternalID("ext-1"), {}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "many identities all with external ids",
|
||||
identities: []*identity.Identity{withExternalID("ext-1"), withExternalID("ext-2")},
|
||||
want: true,
|
||||
},
|
||||
} {
|
||||
t.Run("case="+tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, identitiesNeedPartialInserts(tc.identities))
|
||||
})
|
||||
}
|
||||
}
|
||||
2188
persistence/sql/identity/persister_identity.go
Normal file
2188
persistence/sql/identity/persister_identity.go
Normal file
File diff suppressed because it is too large
Load Diff
122
persistence/sql/identity/persister_identity_test.go
Normal file
122
persistence/sql/identity/persister_identity_test.go
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright © 2026 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package identity_test
|
||||
|
||||
// Since:
|
||||
// - This is a separate test package
|
||||
// - Go creates one executable per test package
|
||||
// - Go global variables are isolated per process (i.e. executable)
|
||||
// - This test uses its own database
|
||||
// , it means that this test is independent from the others and cannot interfere with them.
|
||||
// We do not need to clear the global maps at the start of the test: they are still pristine (unset).
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
id "github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/persistence/sql/identity"
|
||||
"github.com/ory/kratos/pkg"
|
||||
"github.com/ory/kratos/pkg/testhelpers"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/x/dbal"
|
||||
"github.com/ory/x/sqlxx"
|
||||
)
|
||||
|
||||
// TestCreateIdentities_WritesExtraColumns verifies that WithExtraColumns is
|
||||
// forwarded from CreateIdentities down to the batch INSERT.
|
||||
func TestCreateIdentities_WritesExtraColumns(t *testing.T) {
|
||||
_, reg := pkg.NewRegistryDefaultWithDSN(t, dbal.NewSQLiteTestDatabase(t))
|
||||
_, p := testhelpers.NewNetwork(t, t.Context(), reg.Persister())
|
||||
|
||||
require.NoError(t,
|
||||
p.GetConnection(t.Context()).RawQuery(`ALTER TABLE identities ADD COLUMN extra_foo TEXT`).Exec(),
|
||||
)
|
||||
|
||||
ctx := testhelpers.WithDefaultIdentitySchemaFromRaw(t.Context(), []byte(`{"$id":"test","type":"object"}`))
|
||||
|
||||
ident := id.NewIdentity("")
|
||||
require.NoError(t, p.CreateIdentities(ctx, []*id.Identity{ident},
|
||||
id.WithExtraColumns([]id.ExtraColumn{{K: "extra_foo", V: "bar"}}),
|
||||
))
|
||||
|
||||
var got string
|
||||
require.NoError(t,
|
||||
p.GetConnection(t.Context()).RawQuery(
|
||||
`SELECT extra_foo FROM identities WHERE id = ?`, ident.ID,
|
||||
).First(&got),
|
||||
)
|
||||
assert.Equal(t, "bar", got)
|
||||
}
|
||||
|
||||
func TestNonStandardCredentialTypes(t *testing.T) {
|
||||
_, reg := pkg.NewRegistryDefaultWithDSN(t, dbal.NewSQLiteTestDatabase(t))
|
||||
_, p := testhelpers.NewNetwork(t, t.Context(), reg.Persister())
|
||||
|
||||
// Replace the content of the table with custom UUIDs. This relies on the
|
||||
// process-global credential-types cache being pristine, which the header
|
||||
// comment's package isolation guarantees — no other test in this package
|
||||
// touches the cache first.
|
||||
{
|
||||
require.NoError(t, p.GetConnection(t.Context()).RawQuery(`DELETE FROM identity_credential_types`, x.NewUUID()).Exec())
|
||||
|
||||
// Test the case of empty credentials in the database, so that we can exercise the case of loading credentials failing,
|
||||
// and then subsequent loads succeed, and the service does not get suck.
|
||||
_, err := identity.FindIdentityCredentialsTypeByName(p.GetConnection(t.Context()), id.CredentialsTypeOIDC)
|
||||
require.Error(t, err)
|
||||
|
||||
// Now fill the table with non-standard UUIDs.
|
||||
q := strings.Builder{}
|
||||
q.WriteString(`INSERT INTO identity_credential_types (id, name) VALUES `)
|
||||
for i, ct := range id.AllCredentialTypes {
|
||||
fmt.Fprintf(&q, `('%s', '%s')`, x.NewUUID(), ct)
|
||||
if i < len(id.AllCredentialTypes)-1 {
|
||||
q.WriteRune(',')
|
||||
}
|
||||
}
|
||||
require.NoError(t, p.GetConnection(t.Context()).RawQuery(q.String()).Exec())
|
||||
}
|
||||
|
||||
// Valid credential types are found.
|
||||
for _, ct := range id.AllCredentialTypes {
|
||||
t.Run("type="+ct.String(), func(t *testing.T) {
|
||||
id, err := identity.FindIdentityCredentialsTypeByName(p.GetConnection(t.Context()), ct)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotEqual(t, uuid.Nil, id)
|
||||
name, err := identity.FindIdentityCredentialsTypeByID(p.GetConnection(t.Context()), id)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, ct, name)
|
||||
})
|
||||
}
|
||||
|
||||
// Invalid credential types are not found.
|
||||
_, err := identity.FindIdentityCredentialsTypeByName(p.GetConnection(t.Context()), "unknown")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = identity.FindIdentityCredentialsTypeByID(p.GetConnection(t.Context()), x.NewUUID())
|
||||
require.Error(t, err)
|
||||
|
||||
// Create an identity and find it by credential identifier.
|
||||
ctx := testhelpers.WithDefaultIdentitySchemaFromRaw(t.Context(), []byte(`{"$id":"test","type":"object"}`))
|
||||
email := "test-" + x.NewUUID().String() + "@example.com"
|
||||
i := id.NewIdentity("")
|
||||
i.SetCredentials(id.CredentialsTypePassword, id.Credentials{
|
||||
Type: id.CredentialsTypePassword,
|
||||
Identifiers: []string{email},
|
||||
Config: sqlxx.JSONRawMessage(`{}`),
|
||||
})
|
||||
require.NoError(t, p.CreateIdentity(ctx, i))
|
||||
|
||||
actualIdentity, actualCreds, err := p.FindByCredentialsIdentifier(ctx, id.CredentialsTypePassword, email)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, i.ID, actualIdentity.ID)
|
||||
assert.Equal(t, []string{email}, actualCreds.Identifiers)
|
||||
}
|
||||
Reference in New Issue
Block a user