package usecase import ( "context" "errors" "fmt" "io" "net/http" "strings" "testing" "go.maximumdirect.net/promptkit/internal/domain" ) type measuredAuthenticationBody struct { reader io.Reader read int closed bool } func (b *measuredAuthenticationBody) Read(p []byte) (int, error) { n, err := b.reader.Read(p) b.read += n return n, err } func (b *measuredAuthenticationBody) Close() error { b.closed = true; return nil } func TestAuthenticationResponseRecognitionBound(t *testing.T) { const maxAuthenticationResponseBytes = 1 << 20 valid := `{"data":{"label":"member","is_free_tier":false,"usage":0}}` for _, test := range []struct { name string size int declared int64 status int overflow bool }{ {"exact limit", maxAuthenticationResponseBytes, -1, 200, false}, {"overflow unknown length", maxAuthenticationResponseBytes + 10, -1, 200, true}, {"overflow understated length", maxAuthenticationResponseBytes + 10, 1, 200, true}, {"unauthorized does not read", maxAuthenticationResponseBytes + 10, -1, 401, false}, } { t.Run(test.name, func(t *testing.T) { body := &measuredAuthenticationBody{reader: strings.NewReader(valid + strings.Repeat(" ", test.size-len(valid)))} client := reachabilityClient(t, func(*http.Request) (*http.Response, error) { return &http.Response{StatusCode: test.status, Header: make(http.Header), Body: body, ContentLength: test.declared}, nil }) outcome, err := (authenticationProbe{client: client}).run(context.Background(), backendWithAuthenticationKind("openrouter_key"), domain.SmokeTestTarget{Endpoint: "https://example.invalid/key"}) if err != nil || !body.closed { t.Fatalf("outcome = %+v, err = %v, closed = %v", outcome, err, body.closed) } if test.overflow { if outcome.reason != domain.SmokeTestReasonAuthenticationResponseLimitExceeded || outcome.failure != domain.ExecutionFailureOther || outcome.credentialsAccepted != nil || body.read != maxAuthenticationResponseBytes+1 { t.Fatalf("overflow = %+v, read = %d", outcome, body.read) } } else if test.status == 401 { if body.read != 0 || outcome.credentialsAccepted == nil || *outcome.credentialsAccepted { t.Fatalf("unauthorized = %+v, read = %d", outcome, body.read) } } else if outcome.credentialsAccepted == nil || !*outcome.credentialsAccepted { t.Fatalf("exact limit = %+v", outcome) } }) } } 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 _, tc := range []struct { body string accepted bool }{ {`{"data":{"label":"member","is_free_tier":false,"usage":1}} {}`, false}, {`{"data":[]}`, false}, {`{"data":{"label":"member","is_free_tier":false,"usage":null}}`, false}, {`{"data":{"label":"member","is_free_tier":0,"usage":1}}`, false}, {`{"data":{"is_free_tier":false,"usage":1}}`, false}, {`{"data":{"label":null,"is_free_tier":false,"usage":1}}`, false}, {`{"data":{"label":"","is_free_tier":false,"usage":1e2,"ignored":true},"ignored":true}`, true}, } { client := reachabilityClient(t, func(*http.Request) (*http.Response, error) { return &http.Response{StatusCode: http.StatusOK, Header: make(http.Header), Body: ioNopCloser(tc.body)}, nil }) outcome, err := (authenticationProbe{client: client}).run(context.Background(), backendWithAuthenticationKind("openrouter_key"), domain.SmokeTestTarget{Endpoint: "https://example.invalid/key"}) if err != nil || (outcome.credentialsAccepted != nil && *outcome.credentialsAccepted) != tc.accepted { t.Errorf("account response %s: outcome = %+v, error = %v", tc.body, outcome, err) } } } 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 }