137 lines
4.7 KiB
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
|
|
}
|