package main import ( "context" "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "git.cin.su/m/Assistant/contracts/cognitive" ) type stubAdvisoryProvider struct { called bool request cognitive.AdvisoryRequest response cognitive.AdvisoryResponse err error } func (s *stubAdvisoryProvider) Advise(_ context.Context, request cognitive.AdvisoryRequest) (cognitive.AdvisoryResponse, error) { s.called = true s.request = request return s.response, s.err } func TestDeterministicAdvisoryRejectsUnknownJSONField(t *testing.T) { body := `{"Version":"creator-advisory-v1","Unexpected":"provider override"}` request := httptest.NewRequest(http.MethodPost, "/v1/advisories/deterministic", strings.NewReader(body)) response := httptest.NewRecorder() deterministicAdvisory(response, request) if response.Code != http.StatusBadRequest { t.Fatalf("status=%d, want 400", response.Code) } } func TestDeterministicAdvisoryReturnsUnavailableContract(t *testing.T) { body := `{"Version":"creator-advisory-v1","RequestID":"request-1","ActorID":"actor","PrincipalID":"principal","ScopeID":"scope","Message":"help","Context":[],"Locale":"en","MaxOutputBytes":4096,"ProviderBindingID":"fake-binding","ProviderBindingRevision":1}` request := httptest.NewRequest(http.MethodPost, "/v1/advisories/deterministic", strings.NewReader(body)) response := httptest.NewRecorder() deterministicAdvisory(response, request) if response.Code != http.StatusOK { t.Fatalf("status=%d body=%s", response.Code, response.Body.String()) } var advisory cognitive.AdvisoryResponse if err := json.NewDecoder(response.Body).Decode(&advisory); err != nil { t.Fatal(err) } if advisory.FinishReason != cognitive.FinishProviderUnavailable || advisory.AdvisoryText != "" { t.Fatalf("unexpected response: %+v", advisory) } } func TestOpenAIAdvisoryIsUnavailableBeforeBodyParsingWhenDisabled(t *testing.T) { request := httptest.NewRequest(http.MethodPost, "/v1/advisories/openai", strings.NewReader(`not-json`)) response := httptest.NewRecorder() openAIAdvisory(nil)(response, request) if response.Code != http.StatusServiceUnavailable { t.Fatalf("status=%d, want 503", response.Code) } } func TestOpenAIAdvisoryStrictlyDecodesBeforeProvider(t *testing.T) { provider := &stubAdvisoryProvider{} request := httptest.NewRequest(http.MethodPost, "/v1/advisories/openai", strings.NewReader(`{"Version":"creator-advisory-v1","Unexpected":"provider override"}`)) response := httptest.NewRecorder() openAIAdvisory(provider)(response, request) if response.Code != http.StatusBadRequest || provider.called { t.Fatalf("status=%d provider_called=%t", response.Code, provider.called) } } func TestOpenAIAdvisoryReturnsProviderNeutralResponse(t *testing.T) { provider := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{ Version: cognitive.CreatorAdvisoryV1, RequestID: "request-1", ProviderBindingID: "binding-under-test", ProviderBindingRevision: 1, AdvisoryText: "advice", FinishReason: cognitive.FinishCompleted, }} body := `{"Version":"creator-advisory-v1","RequestID":"request-1","ActorID":"actor","PrincipalID":"principal","ScopeID":"scope","Message":"help","Context":[],"Locale":"en","MaxOutputBytes":4096,"ProviderBindingID":"openai-responses-gpt-5.4-mini-2026-03-17","ProviderBindingRevision":1}` request := httptest.NewRequest(http.MethodPost, "/v1/advisories/openai", strings.NewReader(body)) response := httptest.NewRecorder() openAIAdvisory(provider)(response, request) if response.Code != http.StatusOK || !provider.called || provider.request.Message != "help" { t.Fatalf("status=%d provider_called=%t", response.Code, provider.called) } var advisory cognitive.AdvisoryResponse if err := json.NewDecoder(response.Body).Decode(&advisory); err != nil { t.Fatal(err) } if advisory.AdvisoryText != "advice" || advisory.FinishReason != cognitive.FinishCompleted { t.Fatalf("unexpected response: %+v", advisory) } } func TestLoadOpenAIAdvisoryProviderDefaultsDisabled(t *testing.T) { t.Setenv("OPENAI_API_KEY", "") provider, err := loadOpenAIAdvisoryProvider("") if err != nil || provider != nil { t.Fatalf("provider=%v error=%v", provider, err) } } func TestLoadOpenAIAdvisoryProviderRejectsDirectEnvironmentCredential(t *testing.T) { t.Setenv("OPENAI_API_KEY", "must-not-be-read") if _, err := loadOpenAIAdvisoryProvider(""); err == nil || strings.Contains(err.Error(), "must-not-be-read") { t.Fatalf("unexpected error: %v", err) } } func TestLoadOpenAIAdvisoryProviderUsesBoundedSecretFile(t *testing.T) { t.Setenv("OPENAI_API_KEY", "") directory := t.TempDir() validPath := filepath.Join(directory, "valid") if err := os.WriteFile(validPath, []byte("test-project-key\n"), 0o600); err != nil { t.Fatal(err) } provider, err := loadOpenAIAdvisoryProvider(validPath) if err != nil || provider == nil { t.Fatalf("provider=%v error=%v", provider, err) } for name, content := range map[string][]byte{ "empty": nil, "embedded whitespace": []byte("invalid key"), "oversized": []byte(strings.Repeat("x", maxOpenAISecretBytes+1)), } { t.Run(name, func(t *testing.T) { path := filepath.Join(directory, name) if err := os.WriteFile(path, content, 0o600); err != nil { t.Fatal(err) } if _, err := loadOpenAIAdvisoryProvider(path); err == nil { t.Fatal("invalid secret file was accepted") } }) } }