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:
232
schema/handler_test.go
Normal file
232
schema/handler_test.go
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package schema_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/client-go"
|
||||
_ "github.com/ory/jsonschema/v3/fileloader"
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/pkg"
|
||||
"github.com/ory/x/configx"
|
||||
"github.com/ory/x/contextx"
|
||||
"github.com/ory/x/httprouterx"
|
||||
"github.com/ory/x/urlx"
|
||||
)
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
router := httprouterx.NewRouterPublic()
|
||||
ts := contextx.NewConfigurableTestServer(router)
|
||||
t.Cleanup(ts.Close)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /identity.schema.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
file, err := os.Open("./stub/identity.schema.json")
|
||||
require.NoError(t, err)
|
||||
_, err = io.Copy(w, file)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
mux.HandleFunc("GET /500", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(500)
|
||||
})
|
||||
fileServer := httptest.NewServer(mux)
|
||||
t.Cleanup(fileServer.Close)
|
||||
|
||||
schemas := map[string]struct {
|
||||
uri string
|
||||
getRaw func() ([]byte, error)
|
||||
expectedHttpResponseCode int
|
||||
}{
|
||||
"default": {
|
||||
uri: "file://./stub/identity.schema.json",
|
||||
getRaw: func() ([]byte, error) { return os.ReadFile("./stub/identity.schema.json") },
|
||||
expectedHttpResponseCode: http.StatusOK,
|
||||
},
|
||||
"identity2": {
|
||||
uri: "file://./stub/identity-2.schema.json",
|
||||
getRaw: func() ([]byte, error) { return os.ReadFile("./stub/identity-2.schema.json") },
|
||||
expectedHttpResponseCode: http.StatusOK,
|
||||
},
|
||||
"base64": {
|
||||
uri: "base64://ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9",
|
||||
getRaw: func() ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString("ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9")
|
||||
},
|
||||
expectedHttpResponseCode: http.StatusOK,
|
||||
},
|
||||
"unreachable": {
|
||||
uri: "http://127.0.0.1:12345/unreachable-schema",
|
||||
getRaw: func() ([]byte, error) {
|
||||
return nil, fmt.Errorf("connection refused")
|
||||
},
|
||||
expectedHttpResponseCode: http.StatusBadGateway,
|
||||
},
|
||||
"no-file": {
|
||||
uri: "file://./stub/does-not-exist.schema.json",
|
||||
getRaw: func() ([]byte, error) { return nil, fmt.Errorf("no such file or directory") },
|
||||
expectedHttpResponseCode: http.StatusInternalServerError,
|
||||
},
|
||||
"directory": {
|
||||
uri: "file://./stub",
|
||||
getRaw: func() ([]byte, error) { return nil, fmt.Errorf("is a directory") },
|
||||
// On an existing directory, `open(2)` succeeds but `read(2)` fails so it looks like an I/O error.
|
||||
expectedHttpResponseCode: http.StatusInternalServerError,
|
||||
},
|
||||
"file-network": {
|
||||
uri: fileServer.URL + "/identity.schema.json",
|
||||
getRaw: func() ([]byte, error) { return os.ReadFile("./stub/identity.schema.json") },
|
||||
expectedHttpResponseCode: http.StatusOK,
|
||||
},
|
||||
"file-network-not-found": {
|
||||
uri: fileServer.URL + "/not-found",
|
||||
getRaw: func() ([]byte, error) { return nil, fmt.Errorf("could not be found") },
|
||||
expectedHttpResponseCode: http.StatusInternalServerError,
|
||||
},
|
||||
"file-network-500": {
|
||||
uri: fileServer.URL + "/500",
|
||||
getRaw: func() ([]byte, error) { return nil, fmt.Errorf("giving up") },
|
||||
expectedHttpResponseCode: http.StatusBadGateway,
|
||||
},
|
||||
"preset://email": {
|
||||
uri: "file://./stub/identity-2.schema.json",
|
||||
getRaw: func() ([]byte, error) { return os.ReadFile("./stub/identity-2.schema.json") },
|
||||
expectedHttpResponseCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
configSchemas := make(config.Schemas, 0, len(schemas))
|
||||
for id, s := range schemas {
|
||||
configSchemas = append(configSchemas, config.Schema{
|
||||
ID: id,
|
||||
URL: s.uri,
|
||||
})
|
||||
}
|
||||
|
||||
_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{
|
||||
config.ViperKeyPublicBaseURL: ts.URL,
|
||||
config.ViperKeyDefaultIdentitySchemaID: "default",
|
||||
config.ViperKeyIdentitySchemas: configSchemas,
|
||||
}))
|
||||
reg.SchemaHandler().RegisterPublicRoutes(router)
|
||||
|
||||
getReq := func(ctx context.Context, t *testing.T, path string, expectCode int) []byte {
|
||||
res, err := ts.Client(ctx).Get(ts.URL + path)
|
||||
require.NoError(t, err)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, res.Body.Close())
|
||||
|
||||
require.EqualValuesf(t, expectCode, res.StatusCode, "%s", body)
|
||||
return body
|
||||
}
|
||||
|
||||
for id, s := range schemas {
|
||||
t.Run(fmt.Sprintf("case=get %s schema", id), func(t *testing.T) {
|
||||
|
||||
_, err := s.getRaw()
|
||||
actual := getReq(t.Context(), t, fmt.Sprintf("/schemas/%s", url.PathEscape(id)), s.expectedHttpResponseCode)
|
||||
require.True(t, json.Valid(actual), string(actual))
|
||||
|
||||
switch s.expectedHttpResponseCode {
|
||||
case http.StatusOK:
|
||||
require.NoError(t, err)
|
||||
case http.StatusInternalServerError, http.StatusBadGateway:
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("case=get schema with base64 encoded ID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected, err := schemas["preset://email"].getRaw()
|
||||
require.NoError(t, err)
|
||||
|
||||
actual := getReq(t.Context(), t, "/schemas/"+base64.RawURLEncoding.EncodeToString([]byte("preset://email")), http.StatusOK)
|
||||
require.JSONEq(t, string(expected), string(actual))
|
||||
})
|
||||
|
||||
t.Run("case=get all schemas", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
defaultSchema, err := configSchemas.FindSchemaByID("default")
|
||||
require.NoError(t, err)
|
||||
identity2Schema, err := configSchemas.FindSchemaByID("identity2")
|
||||
require.NoError(t, err)
|
||||
ctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{*defaultSchema, *identity2Schema})
|
||||
|
||||
getSchemasPaginated := func(t *testing.T, page, perPage, expectCode int) []byte {
|
||||
return getReq(ctx, t, fmt.Sprintf("/schemas?page=%d&per_page=%d", page, perPage), expectCode)
|
||||
}
|
||||
|
||||
body := getSchemasPaginated(t, 0, 10, http.StatusOK)
|
||||
|
||||
var result []client.IdentitySchemaContainer
|
||||
require.NoErrorf(t, json.Unmarshal(body, &result), "%s", body)
|
||||
|
||||
var actualIDs []string
|
||||
for _, s := range result {
|
||||
actualIDs = append(actualIDs, s.Id)
|
||||
}
|
||||
assert.Equal(t, []string{defaultSchema.ID, identity2Schema.ID}, actualIDs)
|
||||
|
||||
assertCorrectSchema := func(t *testing.T, r client.IdentitySchemaContainer) {
|
||||
expected, err := schemas[r.Id].getRaw()
|
||||
require.NoError(t, err)
|
||||
actual, err := json.Marshal(r.Schema)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, string(expected), string(actual))
|
||||
}
|
||||
|
||||
for _, r := range result {
|
||||
assertCorrectSchema(t, r)
|
||||
}
|
||||
|
||||
for page := range 2 {
|
||||
t.Run(fmt.Sprintf("page=%d", page), func(t *testing.T) {
|
||||
body := getSchemasPaginated(t, page, 1, http.StatusOK)
|
||||
|
||||
var result []client.IdentitySchemaContainer
|
||||
require.NoError(t, json.Unmarshal(body, &result))
|
||||
|
||||
require.Len(t, result, 1)
|
||||
assert.Equal(t, actualIDs[page], result[0].Id)
|
||||
assertCorrectSchema(t, result[0])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("case=read schema", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, s := range schemas {
|
||||
expected, expectedErr := s.getRaw()
|
||||
|
||||
actual, err := reg.SchemaHandler().ReadSchema(t.Context(), urlx.ParseOrPanic(s.uri))
|
||||
if expectedErr == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.ErrorContains(t, err, expectedErr.Error()) // not using error.is because some of the errors are not accessible
|
||||
}
|
||||
|
||||
if expectedErr == nil {
|
||||
require.JSONEq(t, string(expected), string(actual))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user