mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-21 05:57:53 +00:00
test: integration suite with real Traefik, redis-shared limits, streaming assertions
Adds integration/ (separate module): docker compose stack with real Traefik v3.5 + localPlugins, mock openai/anthropic/gemini/tool providers, shared Redis, and a raw-TCP streaming timing probe. 6/7 tests pass; streaming incrementality fails on a confirmed external yaegi/Traefik bug (see task-15-report.md), not a plugin defect. Fixes 4 real Yaegi-interpretation bugs surfaced by running the plugin under real Traefik for the first time (none caught by go test or yaegi-check, which never exercises chat/embeddings/streaming/Redis): - .traefik.yml: add basePkg (Traefik derives the yaegi binding identifier from the module path, not the package's own declared name; without this override the plugin never loads at all). - registry.go: resolveAgainst's multi-value return panicked yaegi on an inline string concatenation; assign to a local first. - routes_unified.go: errors.As panicked yaegi on interpreted pointer error types; use plain type assertions (both types are always returned bare, never wrapped). - sse.go: readSSE's dispatch closure hit the same multi-value-assignment panic as registry.go; split into separate statements. - resp.go: respErr as a defined string type was indistinguishable from a plain string in a yaegi type assertion, misreading every successful Redis SELECT reply as failed and silently disabling rate limiting; changed to a struct.
This commit is contained in:
@@ -51,10 +51,19 @@ const respMaxLineLen = 64 << 10
|
||||
// respErr is a RESP2 error reply ("-message\r\n"). It implements error so a
|
||||
// caller inspecting a decoded reply can type-assert for it the same way as
|
||||
// any other error.
|
||||
type respErr string
|
||||
//
|
||||
// A struct wrapping the message, not a defined string type ("type respErr
|
||||
// string"): yaegi v0.16.1 conflates a defined string type with plain
|
||||
// string in a type assertion — v.(respErr) on an any holding a genuine
|
||||
// plain string (e.g. decodeReply's "+OK" success reply, itself a string)
|
||||
// incorrectly reports ok=true, so every successful handshakeLocked call
|
||||
// (AUTH/SELECT) was misread as a failed one under real Traefik. A struct
|
||||
// has no such ambiguity: verified empirically under real Traefik (Task
|
||||
// 15's integration suite, cross-replica rate limiting via Redis).
|
||||
type respErr struct{ msg string }
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e respErr) Error() string { return string(e) }
|
||||
func (e respErr) Error() string { return e.msg }
|
||||
|
||||
// respClient is a minimal stdlib-only RESP2 client for a single Redis-
|
||||
// compatible server: one mutex-guarded TCP connection, lazily dialled on
|
||||
@@ -305,7 +314,7 @@ func decodeReply(r *bufio.Reader, depth int) (any, error) {
|
||||
case '+':
|
||||
return rest, nil
|
||||
case '-':
|
||||
return respErr(rest), nil
|
||||
return respErr{msg: rest}, nil
|
||||
case ':':
|
||||
n, err := strconv.ParseInt(rest, 10, 64)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user