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:
233
pkg/testhelpers/handler_mock.go
Normal file
233
pkg/testhelpers/handler_mock.go
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-faker/faker/v4"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/session"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/x/httprouterx"
|
||||
)
|
||||
|
||||
type mockDeps interface {
|
||||
identity.PrivilegedPoolProvider
|
||||
identity.ManagementProvider
|
||||
session.ManagementProvider
|
||||
session.PersistenceProvider
|
||||
config.Provider
|
||||
}
|
||||
|
||||
func MockSetSession(t *testing.T, reg mockDeps, conf *config.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
i := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)
|
||||
i.NID = uuid.Must(uuid.NewV4())
|
||||
require.NoError(t, i.SetCredentialsWithConfig(
|
||||
identity.CredentialsTypePassword,
|
||||
identity.Credentials{
|
||||
Type: identity.CredentialsTypePassword,
|
||||
Identifiers: []string{faker.Email()},
|
||||
},
|
||||
json.RawMessage(`{"hashed_password":"$"}`)))
|
||||
require.NoError(t, reg.IdentityManager().Create(t.Context(), i))
|
||||
|
||||
MockSetSessionWithIdentity(t, reg, conf, i)(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func MockSetSessionWithIdentity(t *testing.T, reg mockDeps, _ *config.Config, i *identity.Identity) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
activeSession, err := NewActiveSession(r, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)
|
||||
require.NoError(t, err)
|
||||
if aal := r.URL.Query().Get("set_aal"); len(aal) > 0 {
|
||||
activeSession.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel(aal)
|
||||
}
|
||||
require.NoError(t, reg.SessionManager().UpsertAndIssueCookie(t.Context(), w, r, activeSession))
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func MockMakeAuthenticatedRequest(t *testing.T, reg mockDeps, conf *config.Config, router *httprouterx.RouterPublic, req *http.Request) ([]byte, *http.Response) {
|
||||
return MockMakeAuthenticatedRequestWithClient(t, reg, conf, router, req, NewClientWithCookies(t))
|
||||
}
|
||||
|
||||
func MockMakeAuthenticatedRequestWithClient(t *testing.T, reg mockDeps, conf *config.Config, router *httprouterx.RouterPublic, req *http.Request, client *http.Client) ([]byte, *http.Response) {
|
||||
return MockMakeAuthenticatedRequestWithClientAndID(t, reg, conf, router, req, client, nil)
|
||||
}
|
||||
|
||||
func MockMakeAuthenticatedRequestWithClientAndID(t *testing.T, reg mockDeps, conf *config.Config, router *httprouterx.RouterPublic, req *http.Request, client *http.Client, id *identity.Identity) ([]byte, *http.Response) {
|
||||
set := "/" + uuid.Must(uuid.NewV4()).String() + "/set"
|
||||
if id == nil {
|
||||
router.GET(set, MockSetSession(t, reg, conf))
|
||||
} else {
|
||||
router.GET(set, MockSetSessionWithIdentity(t, reg, conf, id))
|
||||
}
|
||||
|
||||
MockHydrateCookieClient(t, client, "http://"+req.URL.Host+set+"?"+req.URL.Query().Encode())
|
||||
|
||||
res, err := client.Do(req)
|
||||
require.NoError(t, errors.WithStack(err))
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, errors.WithStack(err))
|
||||
|
||||
require.NoError(t, res.Body.Close())
|
||||
|
||||
return body, res
|
||||
}
|
||||
|
||||
// NewTestTransport returns a transport dedicated to a single test client.
|
||||
//
|
||||
// Test helpers spin up short-lived httptest.Server instances and close them
|
||||
// when the helper returns. httptest.Server.Close unconditionally calls
|
||||
// http.DefaultTransport.CloseIdleConnections as a courtesy for users of the
|
||||
// default transport. When many parallel subtests share http.DefaultTransport
|
||||
// (the zero value of http.Client.Transport), one subtest closing its server
|
||||
// tears down idle connections that another subtest's in-flight request is
|
||||
// using, surfacing as "http: CloseIdleConnections called". Giving every test
|
||||
// client its own transport means that courtesy close only ever affects the
|
||||
// unused global transport, never a live test client.
|
||||
func NewTestTransport(t testing.TB) *http.Transport {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
t.Cleanup(transport.CloseIdleConnections)
|
||||
return transport
|
||||
}
|
||||
|
||||
// NewTestClient returns a jar-less client with a dedicated transport, see NewTestTransport.
|
||||
func NewTestClient(t testing.TB) *http.Client {
|
||||
return &http.Client{Transport: NewTestTransport(t)}
|
||||
}
|
||||
|
||||
func NewClientWithCookies(t *testing.T) *http.Client {
|
||||
cj, err := cookiejar.New(&cookiejar.Options{})
|
||||
require.NoError(t, err)
|
||||
return &http.Client{Jar: cj, Transport: NewTestTransport(t)}
|
||||
}
|
||||
|
||||
func NewNoRedirectClientWithCookies(t *testing.T) *http.Client {
|
||||
cj, err := cookiejar.New(&cookiejar.Options{})
|
||||
require.NoError(t, err)
|
||||
return &http.Client{
|
||||
Jar: cj,
|
||||
Transport: NewTestTransport(t),
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func MockHydrateCookieClient(t *testing.T, c *http.Client, u string) *http.Cookie {
|
||||
var sessionCookie *http.Cookie
|
||||
res, err := c.Get(u)
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = res.Body.Close() }()
|
||||
body := x.MustReadAll(res.Body)
|
||||
assert.EqualValues(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
var found bool
|
||||
for _, rc := range res.Cookies() {
|
||||
if rc.Name == config.DefaultSessionCookieName {
|
||||
found = true
|
||||
sessionCookie = rc
|
||||
}
|
||||
}
|
||||
require.True(t, found, "got body: %s\ngot url: %s", body, res.Request.URL.String())
|
||||
return sessionCookie
|
||||
}
|
||||
|
||||
func MockSessionCreateHandlerWithIdentity(t *testing.T, reg mockDeps, i *identity.Identity) (http.HandlerFunc, *session.Session) {
|
||||
var ct []identity.CredentialsType
|
||||
|
||||
// if identity was not created with any credentials,
|
||||
// then assume a 'password' credential type
|
||||
if len(i.Credentials) == 0 {
|
||||
return MockSessionCreateHandlerWithIdentityAndAMR(t, reg, i, []identity.CredentialsType{"password"})
|
||||
}
|
||||
|
||||
// otherwise, mock session with appropriate credential types
|
||||
for _, c := range i.Credentials {
|
||||
ct = append(ct, c.Type)
|
||||
}
|
||||
|
||||
return MockSessionCreateHandlerWithIdentityAndAMR(t, reg, i, ct)
|
||||
}
|
||||
|
||||
// mockMethodAAL maps a credential type to the AAL a successful login
|
||||
// through that method contributes. Mirrors the defaults the real
|
||||
// strategies pass to CompletedLoginFor, so tests that only specify the
|
||||
// method name get a realistic session AAL.
|
||||
func mockMethodAAL(m identity.CredentialsType) identity.AuthenticatorAssuranceLevel {
|
||||
switch m {
|
||||
case identity.CredentialsTypeTOTP,
|
||||
identity.CredentialsTypeWebAuthn,
|
||||
identity.CredentialsTypeLookup:
|
||||
return identity.AuthenticatorAssuranceLevel2
|
||||
default:
|
||||
return identity.AuthenticatorAssuranceLevel1
|
||||
}
|
||||
}
|
||||
|
||||
func MockSessionCreateHandlerWithIdentityAndAMR(t *testing.T, reg mockDeps, i *identity.Identity, methods []identity.CredentialsType) (http.HandlerFunc, *session.Session) {
|
||||
var sess session.Session
|
||||
require.NoError(t, faker.FakeData(&sess))
|
||||
// require AuthenticatedAt to be time.Now() as we always compare it to the current time
|
||||
sess.AuthenticatedAt = time.Now().UTC()
|
||||
sess.IssuedAt = time.Now().UTC()
|
||||
sess.ExpiresAt = time.Now().UTC().Add(time.Hour * 24)
|
||||
sess.Active = true
|
||||
|
||||
for _, m := range methods {
|
||||
if m == identity.CredentialsTypeOIDC {
|
||||
if c, ok := i.Credentials[m]; ok {
|
||||
var target identity.CredentialsOIDC
|
||||
if err := json.Unmarshal(c.Config, &target); err == nil {
|
||||
for _, t := range target.Providers {
|
||||
sess.CompletedLoginForWithProvider(c.Type, identity.AuthenticatorAssuranceLevel1, t.Provider, "")
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
sess.CompletedLoginFor(m, mockMethodAAL(m))
|
||||
}
|
||||
|
||||
sess.SetAuthenticatorAssuranceLevel()
|
||||
|
||||
if _, err := reg.Config().DefaultIdentityTraitsSchemaURL(t.Context()); err != nil {
|
||||
SetDefaultIdentitySchema(reg.Config(), "file://./stub/fake-session.schema.json")
|
||||
}
|
||||
|
||||
require.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), i))
|
||||
|
||||
inserted, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), i.ID)
|
||||
require.NoError(t, err)
|
||||
sess.Identity = inserted
|
||||
|
||||
require.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &sess))
|
||||
require.Len(t, inserted.Credentials, len(i.Credentials))
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
require.NoError(t, reg.SessionManager().IssueCookie(t.Context(), w, r, &sess))
|
||||
}, &sess
|
||||
}
|
||||
|
||||
func MockSessionCreateHandler(t *testing.T, reg mockDeps) (http.HandlerFunc, *session.Session) {
|
||||
return MockSessionCreateHandlerWithIdentity(t, reg, &identity.Identity{
|
||||
ID: x.NewUUID(), State: identity.StateActive, Traits: identity.Traits(`{"baz":"bar","foo":true,"bar":2.5}`),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user