snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (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:
upstream-mirror
2026-09-16 01:08:34 +05:00
commit 9456a4e922
2560 changed files with 120911 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package config
import (
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"testing/synctest"
"github.com/hashicorp/go-retryablehttp"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
"github.com/ory/herodot"
"github.com/ory/x/configx"
"github.com/ory/x/fetcher"
"github.com/ory/x/logrusx"
)
func TestNewOPLConfigWatcher(t *testing.T) {
var hits atomic.Int64
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits.Add(1)
if _, err := io.WriteString(w, testOPL); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}))
t.Cleanup(ts.Close)
ctx := context.Background()
cfg, err := NewDefault(ctx, nil, logrusx.New("", ""), configx.SkipValidation())
require.NoError(t, err)
cw, err := newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.NoError(t, err)
require.EqualValues(t, 1, hits.Load(), "HTTP request made")
_, err = cw.GetNamespaceByName(ctx, "User")
require.NoError(t, err)
_, err = cw.GetNamespaceByName(ctx, "Document")
require.NoError(t, err)
cache.Wait()
cw, err = newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.NoError(t, err)
require.EqualValues(t, 1, hits.Load(), "content was cached")
_, err = cw.GetNamespaceByName(ctx, "User")
require.NoError(t, err)
_, err = cw.GetNamespaceByName(ctx, "Document")
require.NoError(t, err)
}
func TestOPLConfigWatcher(t *testing.T) {
t.Parallel()
t.Run("caches parsed namespaces", func(t *testing.T) {
t.Parallel()
ts, hits := newCountingServer(t, http.StatusOK, testOPL)
cfg := newTestConfig(t)
ctx := t.Context()
first, err := newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.NoError(t, err)
cache.Wait()
second, err := newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.NoError(t, err)
require.EqualValues(t, 1, hits.Load(), "content was fetched only once")
firstUser, err := first.GetNamespaceByName(ctx, "User")
require.NoError(t, err)
secondUser, err := second.GetNamespaceByName(ctx, "User")
require.NoError(t, err)
require.Same(t, firstUser, secondUser, "the parsed namespaces are shared, not re-parsed per construction")
})
t.Run("deduplicates concurrent fetches", func(t *testing.T) {
const parallel = 5
const target = "https://opl.invalid/namespaces.ts"
// The cache is process-global and outlives the test binary's runs.
cache.Del(target)
// The config warms package-level caches whose goroutines live outside
// the bubble, so build it here.
cfg := newTestConfig(t)
synctest.Test(t, func(t *testing.T) {
var hits atomic.Int64
release := make(chan struct{})
hc := retryablehttp.NewClient()
hc.Logger = nil
hc.HTTPClient = &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
hits.Add(1)
<-release
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader(testOPL)),
Header: make(http.Header),
Request: r,
}, nil
})}
newFetcher := func() *fetcher.Fetcher { return fetcher.NewFetcher(fetcher.WithClient(hc)) }
ctx := t.Context()
watchers := make([]*oplConfigWatcher, parallel)
var eg errgroup.Group
for i := range parallel {
eg.Go(func() error {
cw, err := newOPLConfigWatcher(ctx, cfg.l, newFetcher, target)
watchers[i] = cw
return err
})
}
synctest.Wait() // One caller is inside the fetch, the rest joined its flight.
close(release)
require.NoError(t, eg.Wait())
require.EqualValues(t, 1, hits.Load(), "concurrent constructions share a single fetch")
for _, cw := range watchers {
_, err := cw.GetNamespaceByName(ctx, "User")
require.NoError(t, err)
}
})
})
t.Run("retries failed fetches", func(t *testing.T) {
t.Parallel()
ts, hits := newCountingServer(t, http.StatusNotFound, "")
cfg := newTestConfig(t)
ctx := t.Context()
_, err := newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.ErrorContains(t, err, "404")
cache.Wait()
_, err = newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.ErrorContains(t, err, "404")
require.EqualValues(t, 2, hits.Load(), "failed fetches are not cached")
})
t.Run("caches parse failures", func(t *testing.T) {
t.Parallel()
ts, hits := newCountingServer(t, http.StatusOK, "/* unclosed comment")
cfg := newTestConfig(t)
ctx := t.Context()
first, err := newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.NoError(t, err, "a parse failure does not fail construction")
_, err = first.GetNamespaceByName(ctx, "User")
require.ErrorIs(t, err, herodot.ErrNotFound())
cache.Wait()
second, err := newOPLConfigWatcher(ctx, cfg.l, cfg.Fetcher, ts.URL)
require.NoError(t, err)
require.EqualValues(t, 1, hits.Load(), "a parse failure is cached and does not trigger a fetch per construction")
_, err = second.GetNamespaceByName(ctx, "User")
require.ErrorIs(t, err, herodot.ErrNotFound())
})
}
func newTestConfig(t *testing.T) *Config {
t.Helper()
cfg, err := NewDefault(t.Context(), nil, logrusx.New("", ""), configx.SkipValidation())
require.NoError(t, err)
return cfg
}
func newCountingServer(t *testing.T, status int, body string) (*httptest.Server, *atomic.Int64) {
t.Helper()
hits := new(atomic.Int64)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits.Add(1)
w.WriteHeader(status)
_, _ = io.WriteString(w, body)
}))
t.Cleanup(ts.Close)
return ts, hits
}
var testOPL = `
import { Namespace } from "@ory/keto-namespace-types"
class User implements Namespace {}
class Document implements Namespace {}
`
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }