-
Notifications
You must be signed in to change notification settings - Fork 970
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: enable server-side config from context (#3954)
- Loading branch information
Showing
12 changed files
with
234 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// Copyright © 2024 Ory Corp | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package testhelpers | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"net/http/httptest" | ||
|
||
"github.com/gofrs/uuid" | ||
|
||
"github.com/ory/kratos/embedx" | ||
"github.com/ory/x/configx" | ||
"github.com/ory/x/contextx" | ||
) | ||
|
||
type ( | ||
TestConfigProvider struct { | ||
contextx.Contextualizer | ||
Options []configx.OptionModifier | ||
} | ||
contextKey int | ||
) | ||
|
||
func (t *TestConfigProvider) NewProvider(ctx context.Context, opts ...configx.OptionModifier) (*configx.Provider, error) { | ||
return configx.New(ctx, []byte(embedx.ConfigSchema), append(t.Options, opts...)...) | ||
} | ||
|
||
func (t *TestConfigProvider) Config(ctx context.Context, config *configx.Provider) *configx.Provider { | ||
config = t.Contextualizer.Config(ctx, config) | ||
values, ok := ctx.Value(contextConfigKey).([]map[string]any) | ||
if !ok { | ||
return config | ||
} | ||
opts := make([]configx.OptionModifier, 0, len(values)) | ||
for _, v := range values { | ||
opts = append(opts, configx.WithValues(v)) | ||
} | ||
config, err := t.NewProvider(ctx, 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 ( | ||
_ contextx.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)) | ||
} |
Oops, something went wrong.