package usecase import ( "context" "errors" "fmt" "net/http" "strings" "testing" "gitea.maximumdirect.net/eric/promptkit/internal/domain" ) func TestAuthenticationProbeApplicabilityAndHTTPAvailability(t *testing.T) { client := reachabilityClient(t, func(*http.Request) (*http.Response, error) { t.Fatal("authentication applicability unexpectedly dispatched") return nil, nil }) for _, tc := range []struct { name string backend domain.Backend client bool supported bool applicable *bool reason domain.SmokeTestReason }{ { name: "not configured", backend: domain.Backend{}, reason: domain.SmokeTestReasonProbeNotConfigured, }, { name: "none works with injected client", backend: backendWithAuthenticationKind("none"), supported: true, applicable: boolPointer(false), reason: domain.SmokeTestReasonAuthenticationNotApplicable, }, { name: "HTTP adapter unavailable", backend: backendWithAuthenticationKind("openrouter_key"), reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable, }, } { t.Run(tc.name, func(t *testing.T) { probe := authenticationProbe{} if tc.client { probe.client = client } outcome, err := probe.run(context.Background(), tc.backend, domain.SmokeTestTarget{}) if err != nil { t.Fatal(err) } if outcome.supported != tc.supported || outcome.reason != tc.reason || outcome.evidence.DispatchInvoked { t.Fatalf("outcome = %+v", outcome) } if !sameBool(outcome.applicable, tc.applicable) { t.Fatalf("applicability = %v, want %v", outcome.applicable, tc.applicable) } }) } } func TestAuthenticationProbeInterpretsOpenRouterResponses(t *testing.T) { validResponse := `{"data":{"label":"member","is_free_tier":false,"usage":0,"limit":12,"extra":{"ignored":true}},"other":"ignored"}` for _, tc := range []struct { name string status int body string key string wantAccepted *bool wantFailure domain.ExecutionFailureKind wantReason domain.SmokeTestReason wantCredentials bool }{ { name: "recognized success", status: http.StatusOK, body: validResponse, key: "synthetic-secret", wantAccepted: boolPointer(true), wantCredentials: true, }, { name: "anonymous unauthorized", status: http.StatusUnauthorized, wantAccepted: boolPointer(false), }, { name: "credential unauthorized", status: http.StatusUnauthorized, key: "synthetic-secret", wantAccepted: boolPointer(false), wantCredentials: true, }, { name: "unrecognized success", status: http.StatusOK, body: `{"data":{"label":"member","is_free_tier":false,"usage":"0"}}`, key: "synthetic-secret", wantFailure: domain.ExecutionFailureMalformedResponse, wantReason: domain.SmokeTestReasonAuthenticationResponseUnrecognized, wantCredentials: true, }, { name: "other status", status: http.StatusNotFound, key: "synthetic-secret", wantFailure: domain.ExecutionFailureProviderStatus, wantCredentials: true, }, } { t.Run(tc.name, func(t *testing.T) { var authorization string client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) { if request.Method != http.MethodGet { t.Errorf("method = %q, want GET", request.Method) } authorization = request.Header.Get("Authorization") return &http.Response{ StatusCode: tc.status, Header: make(http.Header), Body: ioNopCloser(tc.body), }, nil }) backend := backendWithAuthenticationKind("openrouter_key") settings := domain.ExecutionTarget{APIKey: tc.key, APIKeyRequired: true} target := domain.SmokeTestTarget{ Endpoint: "https://endpoint-user:endpoint-pass@example.invalid/v1/key", EffectiveSettings: settings, } outcome, err := (authenticationProbe{client: client}).run(context.Background(), backend, target) if err != nil { t.Fatal(err) } if outcome.credentialsAccepted == nil && tc.wantAccepted != nil || outcome.credentialsAccepted != nil && (tc.wantAccepted == nil || *outcome.credentialsAccepted != *tc.wantAccepted) { t.Fatalf("accepted = %v, want %v", outcome.credentialsAccepted, tc.wantAccepted) } if outcome.failure != tc.wantFailure || outcome.reason != tc.wantReason { t.Fatalf("failure/reason = %v/%q", outcome.failure, outcome.reason) } if !outcome.supported || outcome.applicable == nil || !*outcome.applicable || !outcome.evidence.ResponseReceived || outcome.evidence.HTTPStatus == nil || *outcome.evidence.HTTPStatus != tc.status { t.Fatalf("response evidence = %+v", outcome) } if outcome.evidence.CredentialsSupplied != tc.wantCredentials || (authorization != "") != tc.wantCredentials { t.Fatalf("credential evidence=%+v authorization=%q", outcome.evidence, authorization) } if strings.Contains(fmt.Sprintf("%+v", outcome), "synthetic-secret") || strings.Contains(outcome.evidence.Endpoint, "endpoint-pass") { t.Fatalf("sensitive value escaped into outcome: %+v", outcome) } }) } } func TestAuthenticationProbeRecognizesOnlyOneCompleteJSONObject(t *testing.T) { for _, body := range []string{ `{"data":{"label":"member","is_free_tier":false,"usage":1}} {}`, `{"data":[]}`, `{"data":{"label":"member","is_free_tier":false,"usage":null}}`, `{"data":{"label":"member","is_free_tier":0,"usage":1}}`, `{"data":{"is_free_tier":false,"usage":1}}`, `{"data":{"label":null,"is_free_tier":false,"usage":1}}`, } { if recognized, err := recognizedOpenRouterKeyResponse(strings.NewReader(body)); err != nil || recognized { t.Errorf("recognized invalid account response %s", body) } } if recognized, err := recognizedOpenRouterKeyResponse(strings.NewReader(`{"data":{"label":"","is_free_tier":false,"usage":1e2,"ignored":true},"ignored":true}`)); err != nil || !recognized { t.Fatal("valid minimal identifying response was rejected") } } func TestAuthenticationProbeTreatsInterruptedResponseBodyAsTransportFailure(t *testing.T) { client := reachabilityClient(t, func(*http.Request) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusOK, Header: make(http.Header), Body: interruptedReadCloser{err: context.DeadlineExceeded}, }, nil }) outcome, err := (authenticationProbe{client: client}).run( context.Background(), backendWithAuthenticationKind("openrouter_key"), domain.SmokeTestTarget{Endpoint: "https://example.invalid/key"}, ) if err != nil { t.Fatal(err) } if outcome.credentialsAccepted != nil || outcome.failure != domain.ExecutionFailureDeadline || !outcome.evidence.ResponseReceived || outcome.evidence.HTTPStatus == nil || *outcome.evidence.HTTPStatus != http.StatusOK { t.Fatalf("interrupted response outcome = %+v", outcome) } } func TestAuthenticationProbeReportsTransportFailureAndAllowsMissingRequiredKey(t *testing.T) { t.Setenv("PROMPTKIT_AUTH_REQUIRED_KEY", "") client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) { if request.Header.Get("Authorization") != "" { t.Errorf("Authorization = %q, want absent", request.Header.Get("Authorization")) } return nil, errors.New("opaque transport failure") }) target := domain.SmokeTestTarget{ Endpoint: "https://example.invalid/key", EffectiveSettings: domain.ExecutionTarget{ APIKeyEnv: "PROMPTKIT_AUTH_REQUIRED_KEY", APIKeyRequired: true, }, } outcome, err := (authenticationProbe{client: client}).run(context.Background(), backendWithAuthenticationKind("openrouter_key"), target) if err != nil { t.Fatal(err) } if outcome.credentialsAccepted != nil || outcome.failure != domain.ExecutionFailureTransport || !outcome.evidence.DispatchInvoked || !outcome.evidence.RequestObserved || outcome.evidence.CredentialsSupplied { t.Fatalf("transport outcome = %+v", outcome) } } func backendWithAuthenticationKind(kind string) domain.Backend { return domain.Backend{SmokeTests: &domain.BackendSmokeTestConfig{ Authentication: &domain.AuthenticationProbeConfig{Kind: &kind}, }} } func sameBool(got, want *bool) bool { if got == nil || want == nil { return got == nil && want == nil } return *got == *want } type stringReadCloser struct{ *strings.Reader } func ioNopCloser(value string) stringReadCloser { return stringReadCloser{Reader: strings.NewReader(value)} } func (stringReadCloser) Close() error { return nil } type interruptedReadCloser struct{ err error } func (r interruptedReadCloser) Read([]byte) (int, error) { return 0, r.err } func (interruptedReadCloser) Close() error { return nil }