mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-18 05:47:38 +00:00
test(yaegi-check): wait for every probe before asserting
The warmup waited for one target while the same sweep probed the others concurrently. A late probe could overwrite another target's traffic sourced record and fail a later assertion. Wait for every configured MCP target to report the probe source, and correct the comment that claimed a guarantee the code did not give. Claude-Session: https://claude.ai/code/session_01EebV5jyWvYocByNQ3oss1w
This commit is contained in:
+43
-23
@@ -1896,31 +1896,51 @@ func readTargetHealth(handler http.Handler, name string) (targetHealthProbeView,
|
|||||||
// used to make TARGETHEALTH-2, below, racy under Yaegi's interpreter
|
// used to make TARGETHEALTH-2, below, racy under Yaegi's interpreter
|
||||||
// overhead).
|
// overhead).
|
||||||
//
|
//
|
||||||
// targetHealthProbeOnlyServerName is dedicated to this assertion
|
// G5, feat/target-health review round 2: that guarantee only holds once
|
||||||
// specifically because it is NEVER proxied or federated through by any
|
// EVERY configured MCP server's own probe observation has actually
|
||||||
// other exercise in this harness — its health entry can only ever be
|
// landed, not merely targetHealthProbeOnlyServerName's. sweepTargetHealth
|
||||||
// written by the probe, so source == "probe" here is not just likely,
|
// probes every configured server CONCURRENTLY, on its own goroutines —
|
||||||
// it is the only value possible. Polling (pollTargetHealth) rather than
|
// waiting for one target's record() to land says nothing about whether
|
||||||
// asserting on a single read: the sweep itself still runs on its own
|
// mcpProbeServerName's own probe goroutine has finished yet. A version of
|
||||||
// spawned goroutine even though this call triggered it.
|
// this function that returned as soon as targetHealthProbeOnlyServerName
|
||||||
|
// alone changed could return while mcpProbeServerName's probe write was
|
||||||
|
// still in flight; that write could then land AFTER
|
||||||
|
// exerciseFederatedTooLarge's later traffic write, silently reverting
|
||||||
|
// mcpProbeServerName's source back to "probe" and corrupting
|
||||||
|
// exerciseTargetHealth's own TARGETHEALTH-2 assertion (source ==
|
||||||
|
// "traffic"). Polling every configured MCP server for its own source ==
|
||||||
|
// "probe" closes that gap: this function cannot return until the whole
|
||||||
|
// sweep — not just the one target it happens to check last — has
|
||||||
|
// finished writing.
|
||||||
|
//
|
||||||
|
// targetHealthProbeOnlyServerName is additionally asserted to the FULL
|
||||||
|
// "healthy" state, not just source == "probe": it is NEVER proxied or
|
||||||
|
// federated through by any other exercise in this harness, so its health
|
||||||
|
// entry can only ever be written by the probe — recover's own swallow
|
||||||
|
// (sweepTargetHealth's defer, target_health.go) means an interpreted
|
||||||
|
// panic mid-probe would otherwise degrade to a silently-stuck "unknown"
|
||||||
|
// rather than a loud failure, and asserting the full success shape here
|
||||||
|
// is what makes a Yaegi incompatibility in the initialize/close handshake
|
||||||
|
// path visible instead of passing silently.
|
||||||
func exerciseTargetHealthProbeWarmup(handler http.Handler) error {
|
func exerciseTargetHealthProbeWarmup(handler http.Handler) error {
|
||||||
health, err := pollTargetHealth(handler, targetHealthProbeOnlyServerName, func(h targetHealthProbeView) bool {
|
warmupServerNames := []string{mcpProbeServerName, targetHealthDownServerName, targetHealthProbeOnlyServerName}
|
||||||
return h.State != "unknown"
|
var probeOnlyHealth targetHealthProbeView
|
||||||
})
|
for _, name := range warmupServerNames {
|
||||||
if err != nil {
|
health, err := pollTargetHealth(handler, name, func(h targetHealthProbeView) bool {
|
||||||
return err
|
return h.Source == "probe"
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if health.Source != "probe" {
|
||||||
|
return fmt.Errorf("TARGETHEALTH-PROBE-1: GET /admin/api/targets: %q health.source = %q, want %q (the warmup sweep must probe every configured MCP server before any later exercise runs)", name, health.Source, "probe")
|
||||||
|
}
|
||||||
|
if name == targetHealthProbeOnlyServerName {
|
||||||
|
probeOnlyHealth = health
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if health.Source != "probe" {
|
if probeOnlyHealth.State != "healthy" {
|
||||||
return fmt.Errorf("TARGETHEALTH-PROBE-1: GET /admin/api/targets: %q health.source = %q, want %q (only the active probe ever touches this target)", targetHealthProbeOnlyServerName, health.Source, "probe")
|
return fmt.Errorf("TARGETHEALTH-PROBE-2: GET /admin/api/targets: %q health.state = %q, want healthy (targetHealthProbeOnlyUpstream always answers initialize successfully)", targetHealthProbeOnlyServerName, probeOnlyHealth.State)
|
||||||
}
|
|
||||||
// Recover's own swallow (sweepTargetHealth's defer, target_health.go)
|
|
||||||
// means an interpreted panic mid-probe would otherwise degrade to a
|
|
||||||
// silently-stuck "unknown" rather than a loud failure — asserting the
|
|
||||||
// FULL success shape here (not merely "state != unknown") is what
|
|
||||||
// makes a Yaegi incompatibility in the initialize/close handshake
|
|
||||||
// path visible instead of passing silently.
|
|
||||||
if health.State != "healthy" {
|
|
||||||
return fmt.Errorf("TARGETHEALTH-PROBE-2: GET /admin/api/targets: %q health.state = %q, want healthy (targetHealthProbeOnlyUpstream always answers initialize successfully)", targetHealthProbeOnlyServerName, health.State)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("yaegi-check: target health active probe sweep ran and recorded correctly interpreted")
|
fmt.Println("yaegi-check: target health active probe sweep ran and recorded correctly interpreted")
|
||||||
|
|||||||
Reference in New Issue
Block a user