Files
promptkit/internal/usecase/smoke_reachability_test.go

137 lines
4.7 KiB
Go

package usecase
import (
"context"
"errors"
"io"
"net/http"
"strings"
"testing"
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
"gitea.maximumdirect.net/eric/promptkit/internal/llm"
)
func TestReachabilityProbeTreatsEveryHTTPResponseAsReachable(t *testing.T) {
for _, status := range []int{
http.StatusNoContent,
http.StatusUnauthorized,
http.StatusNotFound,
http.StatusMethodNotAllowed,
} {
t.Run(http.StatusText(status), func(t *testing.T) {
calls := 0
body := &reachabilityProbeBody{}
client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) {
calls++
if request.Method != http.MethodGet {
t.Errorf("method = %q, want GET", request.Method)
}
return &http.Response{StatusCode: status, Header: make(http.Header), Body: body}, nil
})
result, err := (reachabilityProbe{client: client}).run(context.Background(), "https://example.invalid/health", 0)
if err != nil {
t.Fatal(err)
}
if calls != 1 || !body.closed {
t.Fatalf("calls=%d body closed=%v", calls, body.closed)
}
if !result.supported || result.reachable == nil || !*result.reachable || !result.evidence.ResponseReceived {
t.Fatalf("response evidence = %+v", result)
}
if result.evidence.HTTPStatus == nil || *result.evidence.HTTPStatus != status || result.failure != domain.ExecutionFailureNone {
t.Fatalf("status/failure = %v / %v", result.evidence.HTTPStatus, result.failure)
}
})
}
}
func TestReachabilityProbeRemovesURLCredentials(t *testing.T) {
var observed *http.Request
client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) {
observed = request
return &http.Response{StatusCode: http.StatusNoContent, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(""))}, nil
})
endpoint := "https://operator:secret@example.invalid/health?check=1"
result, err := (reachabilityProbe{client: client}).run(context.Background(), endpoint, 0)
if err != nil {
t.Fatal(err)
}
if observed == nil || observed.URL.User != nil || observed.Header.Get("Authorization") != "" {
t.Fatalf("probe request retained URL credentials: %+v", observed)
}
if result.initialEndpoint != "https://example.invalid/health?check=1" || result.evidence.CredentialsSupplied {
t.Fatalf("credential-free evidence = %+v", result)
}
}
func TestReachabilityProbeReportsTransportFailureWithoutResponse(t *testing.T) {
client := reachabilityClient(t, func(*http.Request) (*http.Response, error) {
return nil, errors.New("opaque network failure")
})
result, err := (reachabilityProbe{client: client}).run(context.Background(), "https://example.invalid/health", 0)
if err != nil {
t.Fatal(err)
}
if result.reachable == nil || *result.reachable || result.evidence.ResponseReceived || result.evidence.HTTPStatus != nil {
t.Fatalf("no-response evidence = %+v", result)
}
if !result.evidence.DispatchInvoked || !result.evidence.RequestObserved || result.failure != domain.ExecutionFailureTransport {
t.Fatalf("transport failure evidence = %+v", result)
}
}
func TestReachabilityProbeUsesTargetTimeout(t *testing.T) {
client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) {
<-request.Context().Done()
return nil, request.Context().Err()
})
result, err := (reachabilityProbe{client: client}).run(context.Background(), "https://example.invalid/health", 1)
if err != nil {
t.Fatal(err)
}
if result.reachable == nil || *result.reachable || result.failure != domain.ExecutionFailureDeadline {
t.Fatalf("timeout evidence = %+v", result)
}
}
func TestReachabilityProbeIsUnsupportedWithoutHTTPClient(t *testing.T) {
result, err := (reachabilityProbe{}).run(context.Background(), "https://example.invalid/health", 0)
if err != nil {
t.Fatal(err)
}
if result.supported || result.reason != domain.SmokeTestReasonHTTPDiagnosticsUnavailable || result.reachable != nil || result.evidence.DispatchInvoked {
t.Fatalf("unsupported outcome = %+v", result)
}
}
func reachabilityClient(t *testing.T, roundTrip func(*http.Request) (*http.Response, error)) *llm.OpenAICompatibleClient {
t.Helper()
client, err := llm.NewOpenAICompatibleClient(llm.OpenAICompatibleConfig{
HTTPClient: &http.Client{Transport: reachabilityProbeTransport(roundTrip)},
})
if err != nil {
t.Fatal(err)
}
return client
}
type reachabilityProbeTransport func(*http.Request) (*http.Response, error)
func (t reachabilityProbeTransport) RoundTrip(request *http.Request) (*http.Response, error) {
return t(request)
}
type reachabilityProbeBody struct{ closed bool }
func (*reachabilityProbeBody) Read([]byte) (int, error) { return 0, io.EOF }
func (b *reachabilityProbeBody) Close() error {
b.closed = true
return nil
}