package llm import ( "context" "encoding/json" "git.sundynix.cn/Blizzard/Veyra/internal/model" "net/http" "net/http/httptest" "strings" "testing" ) func TestProtocolsAndFailureIsolation(t *testing.T) { output := `{"schema_version":"candidate_analysis.v1","symbol":"000042","exchange":"SZSE","decision":"APPROVE","summary":"均线交叉观察","risks":"未复权日线","evidence_indices":[0],"missing_information":["基本面未提供"]}` for _, protocol := range []string{"responses", "chat_completions"} { t.Run(protocol, func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "Bearer fixture-secret" { t.Error("missing bearer authentication") } var body map[string]any if json.NewDecoder(r.Body).Decode(&body) != nil { t.Error("invalid request") } if body["store"] != false || body["model"] != "fixture-model" { t.Error("model/storage config not inherited") } if protocol == "responses" { if r.URL.Path != "/v1/responses" || body["text"] == nil { t.Error("invalid responses request") } _ = json.NewEncoder(w).Encode(map[string]any{"status": "completed", "output": []any{map[string]any{"type": "message", "content": []any{map[string]string{"type": "output_text", "text": output}}}}}) } else { if r.URL.Path != "/v1/chat/completions" || body["response_format"] == nil { t.Error("invalid chat request") } _ = json.NewEncoder(w).Encode(map[string]any{"choices": []any{map[string]any{"finish_reason": "stop", "message": map[string]string{"content": output}}}}) } })) defer server.Close() p := model.ProviderConfig{Enabled: true, ModelName: "fixture-model", BaseURL: server.URL + "/v1", WireAPI: protocol, TimeoutSeconds: 2, RequiresOpenAIAuth: true, DisableResponseStorage: true} a, err := New().Analyze(context.Background(), p, "fixture-secret", json.RawMessage(`{}`)) if err != nil || a.Decision != "APPROVE" { t.Fatalf("call failed: %v", err) } }) } for _, response := range []string{`{"status":"incomplete","output":[]}`, `{"status":"completed","output":[{"content":[{"type":"refusal"}]}]}`, `{"status":"completed","output":[{"content":[{"type":"output_text","text":"{} {}"}]}]}`} { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(response)) })) _, err := New().Analyze(context.Background(), model.ProviderConfig{Enabled: true, ModelName: "x", BaseURL: server.URL, WireAPI: "responses", TimeoutSeconds: 2}, "", json.RawMessage(`{}`)) server.Close() if err == nil { t.Fatal("invalid response accepted") } } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "fixture-secret upstream internal detail", 503) })) defer server.Close() _, err := New().Analyze(context.Background(), model.ProviderConfig{Enabled: true, ModelName: "x", BaseURL: server.URL, WireAPI: "responses", TimeoutSeconds: 2}, "", json.RawMessage(`{}`)) if err == nil || strings.Contains(err.Error(), "secret") || err.Error() != "MODEL_HTTP_503" { t.Fatal("unsafe provider error") } }