mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-14 05:18:54 +00:00
fix(docs,integration): example env completeness, flaky reset, cache key assertion
Task 5 re-review fixes: 1 Important + 3 folded. 1. (Important) examples/kubernetes.yaml was construction-broken on copy-paste: the inline dashboard-admin user resolves env:LLMGW_ADMIN_KEY at construction time regardless of admin.enabled, but the Helm env snippet never listed it. Added it there, alongside a comment on the admin user explaining the env var is required either way. Fixed the two reversed above/below cross-references (kubernetes.yaml:90/:115) and reworded the misleading "self-contained" comment on the inline admin user. Verified with a throwaway round-trip test covering: no env vars set fails, every documented env var set succeeds (admin.enabled staying false as committed), and — the actual bug — every var set EXCEPT LLMGW_ADMIN_KEY still fails even with admin disabled. 2. README: Admin section's "(disabled still 404s)" now points at the same 404/passthroughUnknown wording the section already uses above it. Caching section's "block absent entirely is a construction error" now says "not enabled" — auth.go's actual check is !cfg.Cache.Enabled, which is identically true whether the block is omitted or present with enabled: false. 3. integration/mock/main.go: added POST /flaky/reset (handleFlakyReset), reachable through the "flaky" provider's own native passthrough route, resetting the atomic hit counter on demand. TestRetryFlakyRecovers now calls it at the start of the test, removing the make integration-keep re-run trap the old doc comment warned about instead of fixing. dynamic.yml.tmpl's stale "test 8" comment now reads "task 5". 4. integration_test.go: added redisKeys (docker compose exec redis-cli KEYS, mirroring flushRedis's own pattern) and a TestResponseCache assertion that redis holds at least one llmgw:cache:* key immediately after the miss request. go test ./... -count=1, gofmt, vet, make yaegi-check all pass; make integration exits 0 across two fresh full runs.
This commit is contained in:
@@ -339,6 +339,26 @@ func flushRedis(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// redisKeys returns every key in the shared Redis matching pattern (a
|
||||
// redis-cli KEYS glob), via the same docker compose exec pattern
|
||||
// flushRedis uses above. KEYS is O(n) and blocks Redis briefly — fine
|
||||
// against this suite's tiny keyspace, and used only for a one-off test
|
||||
// assertion, never on a hot path.
|
||||
func redisKeys(t *testing.T, pattern string) []string {
|
||||
t.Helper()
|
||||
out, err := exec.Command("docker", "compose", "-f", composeFile(), "exec", "-T", "redis", "redis-cli", "KEYS", pattern).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("redis-cli KEYS %s: %v\n%s", pattern, err, out)
|
||||
}
|
||||
var keys []string
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
if line != "" {
|
||||
keys = append(keys, line)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// TestCrossReplicaLimits covers integration test 4: bob's limited group
|
||||
// (requestsPerMinute: 3) is enforced globally across both Traefik
|
||||
// "replicas" via the Redis they share, regardless of which one a given
|
||||
@@ -533,12 +553,19 @@ func TestRealUpstreamSmoke(t *testing.T) {
|
||||
// retry made exactly two upstream tries, not merely that the mock
|
||||
// happened to succeed on a fresh process.
|
||||
//
|
||||
// This test is NOT safe to re-run against a `make integration-keep`
|
||||
// stack without restarting mockopenai first: handleFlakyOpenAIChat's own
|
||||
// hit counter is process-lifetime state with no reset endpoint (see its
|
||||
// doc comment, integration/mock/main.go) — a second run against the same
|
||||
// container never observes the forced-503-then-success sequence again.
|
||||
// Resets the mock's hit counter first via POST /flaky/reset — reachable
|
||||
// through the "flaky" provider's own native passthrough route
|
||||
// (routes_passthrough.go), which reaches handleFlakyReset
|
||||
// (integration/mock/main.go) — so this test reproduces the same
|
||||
// forced-503-then-success sequence on every run, including a re-run
|
||||
// against an already-up `make integration-keep` stack with no
|
||||
// mockopenai restart.
|
||||
func TestRetryFlakyRecovers(t *testing.T) {
|
||||
resetResp, resetBody := doJSON(t, http.MethodPost, traefik1URL+"/flaky/reset", aliceKey, nil)
|
||||
if resetResp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("reset flaky counter: status = %d, body=%#v", resetResp.StatusCode, resetBody)
|
||||
}
|
||||
|
||||
reqBody := map[string]any{
|
||||
"model": "flaky/flaky-mock",
|
||||
"messages": []map[string]any{{"role": "user", "content": "hi"}},
|
||||
@@ -605,6 +632,9 @@ func TestResponseCache(t *testing.T) {
|
||||
if content := firstChoiceContent(t, body1); content != "mock openai response" {
|
||||
t.Errorf("first request: content = %q, want %q", content, "mock openai response")
|
||||
}
|
||||
if keys := redisKeys(t, "llmgw:cache:*"); len(keys) == 0 {
|
||||
t.Error("redis holds no llmgw:cache:* key after a cache miss, want the entry the miss's SET (cache.go) should have written")
|
||||
}
|
||||
|
||||
tokensAfterMiss, _ := adminUsageEntry(t, "users", "alice")["tokensPerDay"].(float64)
|
||||
if tokensAfterMiss <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user