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
159 lines
4.0 KiB
Go
159 lines
4.0 KiB
Go
// Copyright © 2024 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package contextx
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
"github.com/gofrs/uuid"
|
|
|
|
"github.com/ory/x/configx"
|
|
)
|
|
|
|
type (
|
|
TestConfigProvider struct {
|
|
ConfigSchema []byte
|
|
Options []configx.OptionModifier
|
|
}
|
|
contextKey int
|
|
)
|
|
|
|
func NewTestConfigProvider(schema []byte, opts ...configx.OptionModifier) *TestConfigProvider {
|
|
return &TestConfigProvider{
|
|
ConfigSchema: schema,
|
|
Options: opts,
|
|
}
|
|
}
|
|
|
|
func (t *TestConfigProvider) Network(ctx context.Context, network uuid.UUID) uuid.UUID {
|
|
return (&Default{}).Network(ctx, network)
|
|
}
|
|
|
|
func (t *TestConfigProvider) Config(ctx context.Context, config *configx.Provider) *configx.Provider {
|
|
values, ok := ctx.Value(contextConfigKey).([]map[string]any)
|
|
if !ok {
|
|
return config
|
|
}
|
|
|
|
opts := make([]configx.OptionModifier, 1, 1+len(values))
|
|
opts[0] = configx.WithValues(config.All())
|
|
for _, v := range values {
|
|
opts = append(opts, configx.WithValues(v))
|
|
}
|
|
config, err := configx.New(ctx, t.ConfigSchema, append(t.Options, opts...)...)
|
|
if err != nil {
|
|
// This is not production code. The provider is only used in tests.
|
|
panic(err)
|
|
}
|
|
return config
|
|
}
|
|
|
|
const contextConfigKey contextKey = 1
|
|
|
|
var (
|
|
_ Contextualizer = (*TestConfigProvider)(nil)
|
|
)
|
|
|
|
func WithConfigValue(ctx context.Context, key string, value any) context.Context {
|
|
return WithConfigValues(ctx, map[string]any{key: value})
|
|
}
|
|
|
|
func WithConfigValues(ctx context.Context, setValues ...map[string]any) context.Context {
|
|
values, ok := ctx.Value(contextConfigKey).([]map[string]any)
|
|
if !ok {
|
|
values = make([]map[string]any, 0)
|
|
}
|
|
newValues := make([]map[string]any, len(values), len(values)+len(setValues))
|
|
copy(newValues, values)
|
|
newValues = append(newValues, setValues...)
|
|
|
|
return context.WithValue(ctx, contextConfigKey, newValues)
|
|
}
|
|
|
|
type ConfigurableTestHandler struct {
|
|
configs map[uuid.UUID][]map[string]any
|
|
handler http.Handler
|
|
}
|
|
|
|
func NewConfigurableTestHandler(h http.Handler) *ConfigurableTestHandler {
|
|
return &ConfigurableTestHandler{
|
|
configs: make(map[uuid.UUID][]map[string]any),
|
|
handler: h,
|
|
}
|
|
}
|
|
|
|
func (t *ConfigurableTestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
cID := r.Header.Get("Test-Config-Id")
|
|
if config, ok := t.configs[uuid.FromStringOrNil(cID)]; ok {
|
|
r = r.WithContext(WithConfigValues(r.Context(), config...))
|
|
}
|
|
t.handler.ServeHTTP(w, r)
|
|
}
|
|
|
|
func (t *ConfigurableTestHandler) RegisterConfig(config ...map[string]any) uuid.UUID {
|
|
id := uuid.Must(uuid.NewV4())
|
|
t.configs[id] = config
|
|
return id
|
|
}
|
|
|
|
func (t *ConfigurableTestHandler) UseConfig(r *http.Request, id uuid.UUID) *http.Request {
|
|
r.Header.Set("Test-Config-Id", id.String())
|
|
return r
|
|
}
|
|
|
|
func (t *ConfigurableTestHandler) UseConfigValues(r *http.Request, values ...map[string]any) *http.Request {
|
|
return t.UseConfig(r, t.RegisterConfig(values...))
|
|
}
|
|
|
|
type ConfigurableTestServer struct {
|
|
*httptest.Server
|
|
handler *ConfigurableTestHandler
|
|
transport http.RoundTripper
|
|
}
|
|
|
|
func NewConfigurableTestServer(h http.Handler) *ConfigurableTestServer {
|
|
handler := NewConfigurableTestHandler(h)
|
|
server := httptest.NewServer(handler)
|
|
|
|
t := server.Client().Transport
|
|
cts := &ConfigurableTestServer{
|
|
handler: handler,
|
|
Server: server,
|
|
transport: t,
|
|
}
|
|
server.Client().Transport = cts
|
|
return cts
|
|
}
|
|
|
|
func (t *ConfigurableTestServer) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
config, ok := r.Context().Value(contextConfigKey).([]map[string]any)
|
|
if ok && config != nil {
|
|
r = t.handler.UseConfigValues(r, config...)
|
|
}
|
|
return t.transport.RoundTrip(r)
|
|
}
|
|
|
|
type AutoContextClient struct {
|
|
*http.Client
|
|
transport http.RoundTripper
|
|
ctx context.Context
|
|
}
|
|
|
|
func (t *ConfigurableTestServer) Client(ctx context.Context) *AutoContextClient {
|
|
baseClient := *t.Server.Client()
|
|
autoClient := &AutoContextClient{
|
|
Client: &baseClient,
|
|
transport: t,
|
|
ctx: ctx,
|
|
}
|
|
baseClient.Transport = autoClient
|
|
return autoClient
|
|
}
|
|
|
|
func (c *AutoContextClient) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
return c.transport.RoundTrip(r.WithContext(c.ctx))
|
|
}
|