package runner import ( "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/clients" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestAttemptMetricsIncludesCacheAffinityCoefficient(t *testing.T) { metrics := attemptMetrics(store.RuntimeModelCandidate{ ModelName: "deepseek-chat", ModelType: "text_generate", PlatformID: "deepseek-platform", PlatformPriority: 100, CacheAffinity: store.RuntimeCandidateCacheAffinity{ Key: "prompt_lcp_v2:test", RequestCount: 2, CachedInputTokens: 7296, EMAHitRatio: 0.91, LastHitRatio: 0.92, Confidence: 1, Score: 1.74, Boost: 20, AdjustedPriority: 80, MatchedPrefixDepth: 4, CandidateCount: 2, OverrideReason: "different_effective_priority_tier", Applied: true, }, }, 1, false) if metrics["cacheAffinityScore"] != 1.74 { t.Fatalf("expected cache affinity score in attempt metrics, got %+v", metrics) } if metrics["cacheAffinityCachedInputTokens"] != 7296 { t.Fatalf("expected cached input tokens in attempt metrics, got %+v", metrics) } if metrics["cacheAffinityHitRatio"] != 0.91 || metrics["cacheAffinityLastHitRatio"] != 0.92 { t.Fatalf("expected hit ratios in attempt metrics, got %+v", metrics) } if metrics["cacheAffinityMatched"] != true || metrics["cacheAffinityMatchedPrefixDepth"] != 4 || metrics["cacheAffinityCandidateCount"] != 2 || metrics["cacheAffinityOverrideReason"] != "different_effective_priority_tier" { t.Fatalf("expected cache affinity routing diagnostics in attempt metrics, got %+v", metrics) } } func TestFailureRecordIncludesStructuredClientErrorDetails(t *testing.T) { cause := &clients.ClientError{ Code: "gemini_image_output_missing", Message: "Gemini image response contains no extractable image: prompt blocked", Details: map[string]any{ "provider": "gemini", "reason": "prompt_blocked", "promptBlockReason": "SAFETY", }, } _, metrics, _, _, _ := failureMetrics(cause, false) details, _ := metrics["errorDetails"].(map[string]any) if details["reason"] != "prompt_blocked" || details["promptBlockReason"] != "SAFETY" { t.Fatalf("failure metrics lost client error details: %+v", metrics) } result := buildFailureResult(cause.Code, cause.Message, "", cause) errorPayload, _ := result["error"].(map[string]any) resultDetails, _ := errorPayload["details"].(map[string]any) if resultDetails["provider"] != "gemini" || resultDetails["reason"] != "prompt_blocked" { t.Fatalf("failure result lost client error details: %+v", result) } }