Assemble HTTP smoke-test reports
This commit is contained in:
@@ -591,6 +591,13 @@ selection before dispatch, canceled-before-dispatch, timeout with live caller,
|
||||
and available evidence after cancellation. Use real internal components with
|
||||
controlled HTTP effects, not a new mock framework.
|
||||
|
||||
**Completed: 2026-10-13.** Added the internal synchronous HTTP report
|
||||
coordinator with captured selection, metadata applicability, timing, sanitized
|
||||
report targets, per-attempt transport facts, and caller-cancellation handling.
|
||||
HTTP probes bypass generation admission and inherited execution observation.
|
||||
Validation passed: `go test -race ./internal/usecase ./internal/llm`,
|
||||
`go vet ./...`, `go test ./...`, and `git diff --check`.
|
||||
|
||||
## Stage 15: Integrate Inference Diagnostics And Engine Assembly
|
||||
|
||||
Scope: inference orchestration in `internal/usecase` and `internal/engine` wiring.
|
||||
|
||||
@@ -31,6 +31,14 @@ func (p authenticationProbe) run(
|
||||
backend domain.Backend,
|
||||
target domain.SmokeTestTarget,
|
||||
) (authenticationProbeOutcome, error) {
|
||||
outcome, err := p.support(backend)
|
||||
if err != nil || !outcome.supported || (outcome.applicable != nil && !*outcome.applicable) {
|
||||
return outcome, err
|
||||
}
|
||||
return p.runOpenRouterKey(ctx, target)
|
||||
}
|
||||
|
||||
func (p authenticationProbe) support(backend domain.Backend) (authenticationProbeOutcome, error) {
|
||||
if backend.SmokeTests == nil || backend.SmokeTests.Authentication == nil {
|
||||
return authenticationProbeOutcome{reason: domain.SmokeTestReasonProbeNotConfigured}, nil
|
||||
}
|
||||
@@ -46,7 +54,10 @@ func (p authenticationProbe) run(
|
||||
reason: domain.SmokeTestReasonAuthenticationNotApplicable,
|
||||
}, nil
|
||||
case "openrouter_key":
|
||||
return p.runOpenRouterKey(ctx, target)
|
||||
if p.client == nil {
|
||||
return authenticationProbeOutcome{reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable}, nil
|
||||
}
|
||||
return authenticationProbeOutcome{supported: true, applicable: boolPointer(true)}, nil
|
||||
default:
|
||||
return authenticationProbeOutcome{}, fmt.Errorf("%w: unsupported authentication probe kind %q", ErrInvalidRequest, *authentication.Kind)
|
||||
}
|
||||
@@ -56,14 +67,6 @@ func (p authenticationProbe) runOpenRouterKey(
|
||||
ctx context.Context,
|
||||
target domain.SmokeTestTarget,
|
||||
) (authenticationProbeOutcome, error) {
|
||||
applicable := boolPointer(true)
|
||||
if p.client == nil {
|
||||
return authenticationProbeOutcome{
|
||||
applicable: applicable,
|
||||
reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
endpoint, err := endpointWithoutUserInfo(target.Endpoint)
|
||||
if err != nil {
|
||||
return authenticationProbeOutcome{}, fmt.Errorf("prepare authentication endpoint: %w", err)
|
||||
@@ -79,7 +82,7 @@ func (p authenticationProbe) runOpenRouterKey(
|
||||
}
|
||||
outcome := authenticationProbeOutcome{
|
||||
supported: true,
|
||||
applicable: applicable,
|
||||
applicable: boolPointer(true),
|
||||
evidence: evidence,
|
||||
}
|
||||
if evidence.ResponseReceived || response != nil {
|
||||
|
||||
@@ -38,10 +38,9 @@ func TestAuthenticationProbeApplicabilityAndHTTPAvailability(t *testing.T) {
|
||||
reason: domain.SmokeTestReasonAuthenticationNotApplicable,
|
||||
},
|
||||
{
|
||||
name: "HTTP adapter unavailable",
|
||||
backend: backendWithAuthenticationKind("openrouter_key"),
|
||||
applicable: boolPointer(true),
|
||||
reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable,
|
||||
name: "HTTP adapter unavailable",
|
||||
backend: backendWithAuthenticationKind("openrouter_key"),
|
||||
reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
264
internal/usecase/smoke_tester.go
Normal file
264
internal/usecase/smoke_tester.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/generation"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/llm"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/observation"
|
||||
)
|
||||
|
||||
// SmokeTester coordinates internal HTTP smoke-test operations over captured
|
||||
// selection collaborators. Inference operations are added separately.
|
||||
type SmokeTester struct {
|
||||
runner *Runner
|
||||
httpClient smokeHTTPGetter
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// NewSmokeTester creates an internal diagnostic coordinator. A nil HTTP client
|
||||
// leaves HTTP operations unsupported rather than falling back to another client.
|
||||
func NewSmokeTester(runner *Runner, httpClient *llm.OpenAICompatibleClient) *SmokeTester {
|
||||
var getter smokeHTTPGetter
|
||||
if httpClient != nil {
|
||||
getter = httpClient
|
||||
}
|
||||
return &SmokeTester{runner: runner, httpClient: getter, now: time.Now}
|
||||
}
|
||||
|
||||
func newSmokeTesterWithClock(runner *Runner, httpClient *llm.OpenAICompatibleClient, now func() time.Time) *SmokeTester {
|
||||
var getter smokeHTTPGetter
|
||||
if httpClient != nil {
|
||||
getter = httpClient
|
||||
}
|
||||
return &SmokeTester{runner: runner, httpClient: getter, now: now}
|
||||
}
|
||||
|
||||
// Run selects and executes one HTTP diagnostic, retaining only factual results.
|
||||
func (t *SmokeTester) Run(ctx context.Context, request domain.SmokeTestRequest) (*domain.SmokeTestReport, error) {
|
||||
if t == nil || t.runner == nil {
|
||||
return nil, fmt.Errorf("%w: smoke tester is not configured", ErrInvalidRequest)
|
||||
}
|
||||
ctx = observation.WithoutExecutionState(ctx)
|
||||
now := t.now
|
||||
if now == nil {
|
||||
now = time.Now
|
||||
}
|
||||
startedAt := now().UTC()
|
||||
report := &domain.SmokeTestReport{
|
||||
Kind: request.Kind,
|
||||
Timing: domain.SmokeTestTiming{
|
||||
StartedAt: startedAt,
|
||||
},
|
||||
}
|
||||
finish := func(err error) (*domain.SmokeTestReport, error) {
|
||||
report.Timing.Duration = nonnegativeDuration(now().UTC().Sub(startedAt))
|
||||
return report, err
|
||||
}
|
||||
|
||||
selection, err := t.runner.resolveSmokeTestTarget(ctx, request)
|
||||
if err != nil {
|
||||
if contextErr := ctx.Err(); contextErr != nil {
|
||||
report.Failure = generation.ClassifyFailure(contextErr)
|
||||
return finish(contextErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
report.Target, err = smokeReportTarget(selection.target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.setSupport(report, selection); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if contextErr := ctx.Err(); contextErr != nil {
|
||||
report.Failure = generation.ClassifyFailure(contextErr)
|
||||
return finish(contextErr)
|
||||
}
|
||||
if !report.Supported || (report.Applicable != nil && !*report.Applicable) {
|
||||
report.Completed = true
|
||||
return finish(nil)
|
||||
}
|
||||
|
||||
requestStartedAt := now().UTC()
|
||||
switch request.Kind {
|
||||
case domain.SmokeTestReachability:
|
||||
outcome, err := (reachabilityProbe{client: t.httpClient}).run(
|
||||
ctx,
|
||||
selection.target.Endpoint,
|
||||
selection.target.EffectiveSettings.TimeoutSeconds,
|
||||
)
|
||||
requestDuration := nonnegativeDuration(now().UTC().Sub(requestStartedAt))
|
||||
if err != nil {
|
||||
if contextErr := ctx.Err(); contextErr != nil {
|
||||
report.Failure = generation.ClassifyFailure(contextErr)
|
||||
return finish(contextErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
report.Supported = outcome.supported
|
||||
report.Reason = outcome.reason
|
||||
report.Failure = outcome.failure
|
||||
report.Facts = httpExecutionFacts(outcome.evidence)
|
||||
if outcome.reachable != nil {
|
||||
reachable := *outcome.reachable
|
||||
report.Facts.EndpointReachable = &reachable
|
||||
}
|
||||
t.recordHTTPAttempt(report, report.Target.Endpoint, outcome.evidence, requestStartedAt, requestDuration, outcome.failure, nil)
|
||||
return t.finishHTTP(ctx, report, finish)
|
||||
case domain.SmokeTestAuthentication:
|
||||
outcome, err := (authenticationProbe{client: t.httpClient}).run(ctx, selection.backend, selection.target)
|
||||
requestDuration := nonnegativeDuration(now().UTC().Sub(requestStartedAt))
|
||||
if err != nil {
|
||||
if contextErr := ctx.Err(); contextErr != nil {
|
||||
report.Failure = generation.ClassifyFailure(contextErr)
|
||||
return finish(contextErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
report.Supported = outcome.supported
|
||||
report.Applicable = cloneOptionalBool(outcome.applicable)
|
||||
report.Reason = outcome.reason
|
||||
report.Failure = outcome.failure
|
||||
report.Facts = httpExecutionFacts(outcome.evidence)
|
||||
report.Facts.CredentialsAccepted = cloneOptionalBool(outcome.credentialsAccepted)
|
||||
t.recordHTTPAttempt(report, report.Target.Endpoint, outcome.evidence, requestStartedAt, requestDuration, outcome.failure, outcome.credentialsAccepted)
|
||||
return t.finishHTTP(ctx, report, finish)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: HTTP smoke tester cannot run kind %q", ErrInvalidRequest, request.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SmokeTester) setSupport(report *domain.SmokeTestReport, selection *smokeTestSelection) error {
|
||||
switch report.Kind {
|
||||
case domain.SmokeTestReachability:
|
||||
if t.httpClient == nil {
|
||||
report.Reason = domain.SmokeTestReasonHTTPDiagnosticsUnavailable
|
||||
return nil
|
||||
}
|
||||
report.Supported = true
|
||||
report.Applicable = boolPointer(true)
|
||||
return nil
|
||||
case domain.SmokeTestAuthentication:
|
||||
outcome, err := (authenticationProbe{client: t.httpClient}).support(selection.backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
report.Supported = outcome.supported
|
||||
report.Applicable = cloneOptionalBool(outcome.applicable)
|
||||
report.Reason = outcome.reason
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("%w: HTTP smoke tester cannot run kind %q", ErrInvalidRequest, report.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SmokeTester) finishHTTP(
|
||||
ctx context.Context,
|
||||
report *domain.SmokeTestReport,
|
||||
finish func(error) (*domain.SmokeTestReport, error),
|
||||
) (*domain.SmokeTestReport, error) {
|
||||
if contextErr := ctx.Err(); contextErr != nil && isContextFailure(report.Failure) {
|
||||
report.Completed = false
|
||||
report.Failure = generation.ClassifyFailure(contextErr)
|
||||
return finish(contextErr)
|
||||
}
|
||||
report.Completed = true
|
||||
return finish(nil)
|
||||
}
|
||||
|
||||
func (t *SmokeTester) recordHTTPAttempt(
|
||||
report *domain.SmokeTestReport,
|
||||
initialEndpoint string,
|
||||
evidence llm.HTTPExecutionEvidence,
|
||||
startedAt time.Time,
|
||||
duration time.Duration,
|
||||
failure domain.ExecutionFailureKind,
|
||||
credentialsAccepted *bool,
|
||||
) {
|
||||
if !evidence.DispatchInvoked {
|
||||
return
|
||||
}
|
||||
reportDuration := duration
|
||||
if report.Timing.RequestDuration == nil {
|
||||
report.Timing.RequestDuration = &reportDuration
|
||||
} else {
|
||||
*report.Timing.RequestDuration += reportDuration
|
||||
}
|
||||
attempt := domain.SmokeTestAttempt{
|
||||
Dispatched: true,
|
||||
InitialEndpoint: initialEndpoint,
|
||||
Endpoint: evidence.Endpoint,
|
||||
Failure: failure,
|
||||
Usage: domain.ObservedTokenUsage{},
|
||||
Facts: httpExecutionFacts(evidence),
|
||||
Timing: domain.SmokeTestTiming{
|
||||
StartedAt: startedAt,
|
||||
Duration: duration,
|
||||
RequestDuration: &duration,
|
||||
},
|
||||
}
|
||||
if evidence.HTTPStatus != nil {
|
||||
status := *evidence.HTTPStatus
|
||||
attempt.HTTPStatus = &status
|
||||
}
|
||||
attempt.Facts.CredentialsAccepted = cloneOptionalBool(credentialsAccepted)
|
||||
report.Attempts = append(report.Attempts, attempt)
|
||||
}
|
||||
|
||||
func httpExecutionFacts(evidence llm.HTTPExecutionEvidence) domain.SmokeTestFacts {
|
||||
var facts domain.SmokeTestFacts
|
||||
if evidence.ResponseReceived || evidence.DispatchInvoked {
|
||||
responseReceived := evidence.ResponseReceived
|
||||
facts.ResponseReceived = &responseReceived
|
||||
}
|
||||
if evidence.DispatchInvoked {
|
||||
reachable := evidence.ResponseReceived
|
||||
facts.EndpointReachable = &reachable
|
||||
}
|
||||
if evidence.RequestObserved {
|
||||
credentialsSupplied := evidence.CredentialsSupplied
|
||||
facts.CredentialsSupplied = &credentialsSupplied
|
||||
}
|
||||
return facts
|
||||
}
|
||||
|
||||
func smokeReportTarget(target domain.SmokeTestTarget) (domain.SmokeTestTarget, error) {
|
||||
cloned, err := domain.CloneSmokeTestReport(domain.SmokeTestReport{Target: target})
|
||||
if err != nil {
|
||||
return domain.SmokeTestTarget{}, fmt.Errorf("clone smoke-test target settings: %w", err)
|
||||
}
|
||||
target = cloned.Target
|
||||
target.EffectiveSettings.APIKey = ""
|
||||
target.Endpoint, err = endpointWithoutUserInfo(target.Endpoint)
|
||||
if err != nil {
|
||||
return domain.SmokeTestTarget{}, fmt.Errorf("%w: invalid resolved diagnostic endpoint", ErrInvalidRequest)
|
||||
}
|
||||
target.EffectiveSettings.Endpoint, err = endpointWithoutUserInfo(target.EffectiveSettings.Endpoint)
|
||||
if err != nil {
|
||||
return domain.SmokeTestTarget{}, fmt.Errorf("%w: invalid resolved execution endpoint", ErrInvalidRequest)
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func cloneOptionalBool(value *bool) *bool {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
cloned := *value
|
||||
return &cloned
|
||||
}
|
||||
|
||||
func isContextFailure(failure domain.ExecutionFailureKind) bool {
|
||||
return failure == domain.ExecutionFailureCanceled || failure == domain.ExecutionFailureDeadline
|
||||
}
|
||||
|
||||
func nonnegativeDuration(duration time.Duration) time.Duration {
|
||||
if duration < 0 {
|
||||
return 0
|
||||
}
|
||||
return duration
|
||||
}
|
||||
332
internal/usecase/smoke_tester_test.go
Normal file
332
internal/usecase/smoke_tester_test.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/configuration"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/llm"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/observation"
|
||||
)
|
||||
|
||||
func TestSmokeTesterAssemblesReachabilityReportWithoutGenerationAdmission(t *testing.T) {
|
||||
values := smokeSelectionConfig()
|
||||
alpha := values["backends"].(map[string]any)["alpha"].(map[string]any)
|
||||
alphaSmoke := alpha["smoke_tests"].(map[string]any)
|
||||
alphaReachability := alphaSmoke["reachability"].(map[string]any)
|
||||
alphaReachability["endpoint"] = "https://operator:private@example.invalid/health?check=1"
|
||||
runner := smokeTesterRunner(t, values)
|
||||
admitter := &smokeAdmissionSpy{}
|
||||
runner.admitter = admitter
|
||||
|
||||
var observedURL string
|
||||
var authorization string
|
||||
body := &reachabilityProbeBody{}
|
||||
client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) {
|
||||
observedURL = request.URL.String()
|
||||
authorization = request.Header.Get("Authorization")
|
||||
return &http.Response{StatusCode: http.StatusUnauthorized, Header: make(http.Header), Body: body}, nil
|
||||
})
|
||||
var observedEvents []domain.ExecutionEvent
|
||||
recorder, err := observation.New(func(event domain.ExecutionEvent) { observedEvents = append(observedEvents, event) })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recorder.Start()
|
||||
ctx := observation.WithRecorder(context.Background(), recorder)
|
||||
|
||||
clock := smokeClock()
|
||||
report, err := newSmokeTesterWithClock(runner, client, clock).Run(ctx, domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestReachability,
|
||||
BackendID: "alpha",
|
||||
APIKey: "synthetic-secret",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !body.closed || observedURL != "https://example.invalid/health?check=1" || authorization != "" {
|
||||
t.Fatalf("request URL=%q authorization=%q body closed=%v", observedURL, authorization, body.closed)
|
||||
}
|
||||
if report.Kind != domain.SmokeTestReachability || !report.Supported || report.Applicable == nil || !*report.Applicable || !report.Completed || report.Failure != domain.ExecutionFailureNone {
|
||||
t.Fatalf("report outcome = %+v", report)
|
||||
}
|
||||
if report.Target.Endpoint != "https://example.invalid/health?check=1" ||
|
||||
report.Target.EffectiveSettings.Endpoint != "https://alpha.example/v1" ||
|
||||
report.Target.EffectiveSettings.APIKey != "" {
|
||||
t.Fatalf("report target = %+v", report.Target)
|
||||
}
|
||||
if report.Facts.EndpointReachable == nil || !*report.Facts.EndpointReachable ||
|
||||
report.Facts.ResponseReceived == nil || !*report.Facts.ResponseReceived ||
|
||||
report.Facts.CredentialsSupplied == nil || *report.Facts.CredentialsSupplied {
|
||||
t.Fatalf("report facts = %+v", report.Facts)
|
||||
}
|
||||
if len(report.Attempts) != 1 || !report.Attempts[0].Dispatched ||
|
||||
report.Attempts[0].InitialEndpoint != report.Target.Endpoint ||
|
||||
report.Attempts[0].Endpoint != report.Target.Endpoint ||
|
||||
report.Attempts[0].HTTPStatus == nil || *report.Attempts[0].HTTPStatus != http.StatusUnauthorized {
|
||||
t.Fatalf("attempts = %+v", report.Attempts)
|
||||
}
|
||||
if report.Timing.StartedAt.IsZero() || report.Timing.Duration <= 0 || report.Timing.RequestDuration == nil ||
|
||||
report.Attempts[0].Timing.RequestDuration == nil || report.Usage.PromptTokens.Known || report.Timing.CapacityWait != nil {
|
||||
t.Fatalf("report timing/usage = %+v", report)
|
||||
}
|
||||
if strings.Contains(fmt.Sprintf("%+v", report), "synthetic-secret") || strings.Contains(fmt.Sprintf("%+v", report), "private") {
|
||||
t.Fatalf("report retained endpoint credentials: %+v", report)
|
||||
}
|
||||
if len(observedEvents) != 1 || observedEvents[0].Kind != domain.ExecutionStarted {
|
||||
t.Fatalf("diagnostic traffic entered inherited observer: %+v", observedEvents)
|
||||
}
|
||||
if admitter.calls != 0 {
|
||||
t.Fatalf("HTTP diagnostic acquired generation capacity %d times", admitter.calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeTesterAssemblesAuthenticationRejectionReport(t *testing.T) {
|
||||
runner, _ := newSmokeSelectionRunner(t)
|
||||
var authorization string
|
||||
client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) {
|
||||
authorization = request.Header.Get("Authorization")
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusUnauthorized,
|
||||
Header: make(http.Header),
|
||||
Body: ioNopCloser("provider body is not retained"),
|
||||
}, nil
|
||||
})
|
||||
|
||||
report, err := NewSmokeTester(runner, client).Run(context.Background(), domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestAuthentication,
|
||||
BackendID: "beta",
|
||||
APIKey: "synthetic-secret",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if authorization != "Bearer synthetic-secret" {
|
||||
t.Fatalf("Authorization = %q", authorization)
|
||||
}
|
||||
if !report.Supported || report.Applicable == nil || !*report.Applicable || !report.Completed || report.Failure != domain.ExecutionFailureNone {
|
||||
t.Fatalf("report outcome = %+v", report)
|
||||
}
|
||||
if report.Facts.CredentialsSupplied == nil || !*report.Facts.CredentialsSupplied ||
|
||||
report.Facts.EndpointReachable == nil || !*report.Facts.EndpointReachable ||
|
||||
report.Facts.ResponseReceived == nil || !*report.Facts.ResponseReceived ||
|
||||
report.Facts.CredentialsAccepted == nil || *report.Facts.CredentialsAccepted {
|
||||
t.Fatalf("report facts = %+v", report.Facts)
|
||||
}
|
||||
if len(report.Attempts) != 1 || report.Attempts[0].HTTPStatus == nil || *report.Attempts[0].HTTPStatus != http.StatusUnauthorized ||
|
||||
report.Attempts[0].Endpoint != "https://beta.example/v1/key" {
|
||||
t.Fatalf("attempts = %+v", report.Attempts)
|
||||
}
|
||||
if report.Target.Endpoint != "https://beta.example/v1/key" || report.Target.EffectiveSettings.APIKey != "" ||
|
||||
strings.Contains(fmt.Sprintf("%+v", report), "synthetic-secret") ||
|
||||
strings.Contains(fmt.Sprintf("%+v", report), "provider body") {
|
||||
t.Fatalf("report retained sensitive values: %+v", report)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeTesterReportsUnsupportedAndInapplicableAuthentication(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
backend string
|
||||
client bool
|
||||
configureNone bool
|
||||
supported bool
|
||||
applicable *bool
|
||||
reason domain.SmokeTestReason
|
||||
}{
|
||||
{
|
||||
name: "unconfigured",
|
||||
backend: "epsilon",
|
||||
reason: domain.SmokeTestReasonProbeNotConfigured,
|
||||
applicable: nil,
|
||||
},
|
||||
{
|
||||
name: "HTTP support absent",
|
||||
backend: "beta",
|
||||
reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable,
|
||||
applicable: nil,
|
||||
},
|
||||
{
|
||||
name: "known inapplicable without HTTP support",
|
||||
backend: "epsilon",
|
||||
configureNone: true,
|
||||
supported: true,
|
||||
applicable: boolPointer(false),
|
||||
reason: domain.SmokeTestReasonAuthenticationNotApplicable,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
values := smokeSelectionConfig()
|
||||
if tc.configureNone {
|
||||
backend := values["backends"].(map[string]any)[tc.backend].(map[string]any)
|
||||
backend["smoke_tests"].(map[string]any)["authentication"] = map[string]any{"kind": "none"}
|
||||
}
|
||||
runner := smokeTesterRunner(t, values)
|
||||
var client *llm.OpenAICompatibleClient
|
||||
if tc.client {
|
||||
client = reachabilityClient(t, func(*http.Request) (*http.Response, error) {
|
||||
t.Fatal("unsupported or inapplicable authentication dispatched")
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
report, err := NewSmokeTester(runner, client).Run(context.Background(), domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestAuthentication, BackendID: tc.backend,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if report.Supported != tc.supported || report.Reason != tc.reason || !report.Completed ||
|
||||
!sameBool(report.Applicable, tc.applicable) || len(report.Attempts) != 0 || report.Facts != (domain.SmokeTestFacts{}) {
|
||||
t.Fatalf("report = %+v", report)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeTesterReportsReachabilityUnavailableWithoutHTTPClient(t *testing.T) {
|
||||
runner, _ := newSmokeSelectionRunner(t)
|
||||
report, err := NewSmokeTester(runner, nil).Run(context.Background(), domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestReachability, BackendID: "alpha",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if report.Supported || report.Applicable != nil || !report.Completed ||
|
||||
report.Reason != domain.SmokeTestReasonHTTPDiagnosticsUnavailable ||
|
||||
len(report.Attempts) != 0 || report.Facts != (domain.SmokeTestFacts{}) {
|
||||
t.Fatalf("unsupported reachability report = %+v", report)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeTesterSelectionAndCancellationDoNotDispatch(t *testing.T) {
|
||||
runner, _ := newSmokeSelectionRunner(t)
|
||||
calls := 0
|
||||
client := reachabilityClient(t, func(*http.Request) (*http.Response, error) {
|
||||
calls++
|
||||
return &http.Response{StatusCode: http.StatusNoContent, Header: make(http.Header), Body: &reachabilityProbeBody{}}, nil
|
||||
})
|
||||
tester := NewSmokeTester(runner, client)
|
||||
|
||||
if report, err := tester.Run(context.Background(), domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestReachability, BackendID: "missing",
|
||||
}); report != nil || !errors.Is(err, ErrProfileLoad) {
|
||||
t.Fatalf("invalid selection report=%+v error=%v", report, err)
|
||||
}
|
||||
if calls != 0 {
|
||||
t.Fatalf("invalid selection dispatched %d requests", calls)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
report, err := tester.Run(ctx, domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestReachability, BackendID: "alpha",
|
||||
})
|
||||
if !errors.Is(err, context.Canceled) || report == nil || report.Completed || report.Failure != domain.ExecutionFailureCanceled {
|
||||
t.Fatalf("canceled report=%+v error=%v", report, err)
|
||||
}
|
||||
if report.Facts != (domain.SmokeTestFacts{}) || len(report.Attempts) != 0 || calls != 0 {
|
||||
t.Fatalf("cancellation dispatched or invented facts: %+v calls=%d", report, calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeTesterConfiguredTimeoutConcludesWithReport(t *testing.T) {
|
||||
runner, _ := newSmokeSelectionRunner(t)
|
||||
timeout := 1
|
||||
client := reachabilityClient(t, func(request *http.Request) (*http.Response, error) {
|
||||
<-request.Context().Done()
|
||||
return nil, request.Context().Err()
|
||||
})
|
||||
report, err := NewSmokeTester(runner, client).Run(context.Background(), domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestReachability, BackendID: "alpha",
|
||||
Execution: &domain.ExecutionTargetOverride{TimeoutSeconds: &timeout},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !report.Completed || report.Failure != domain.ExecutionFailureDeadline ||
|
||||
report.Facts.EndpointReachable == nil || *report.Facts.EndpointReachable ||
|
||||
report.Facts.ResponseReceived == nil || *report.Facts.ResponseReceived ||
|
||||
report.Facts.CredentialsSupplied == nil || *report.Facts.CredentialsSupplied {
|
||||
t.Fatalf("configured timeout report = %+v", report)
|
||||
}
|
||||
if len(report.Attempts) != 1 || report.Attempts[0].Failure != domain.ExecutionFailureDeadline ||
|
||||
report.Attempts[0].HTTPStatus != nil || report.Timing.RequestDuration == nil {
|
||||
t.Fatalf("configured timeout attempt = %+v", report.Attempts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeTesterRetainsHTTPResponseEvidenceWhenCallerCancelsDuringBodyRead(t *testing.T) {
|
||||
runner, _ := newSmokeSelectionRunner(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
client := reachabilityClient(t, func(*http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Header: make(http.Header),
|
||||
Body: cancelingSmokeBody{cancel: cancel},
|
||||
}, nil
|
||||
})
|
||||
|
||||
report, err := NewSmokeTester(runner, client).Run(ctx, domain.SmokeTestRequest{
|
||||
Kind: domain.SmokeTestAuthentication, BackendID: "beta", APIKey: "synthetic-secret",
|
||||
})
|
||||
if !errors.Is(err, context.Canceled) || report == nil || report.Completed || report.Failure != domain.ExecutionFailureCanceled {
|
||||
t.Fatalf("partial report=%+v error=%v", report, err)
|
||||
}
|
||||
if report.Facts.ResponseReceived == nil || !*report.Facts.ResponseReceived ||
|
||||
report.Facts.EndpointReachable == nil || !*report.Facts.EndpointReachable ||
|
||||
report.Facts.CredentialsSupplied == nil || !*report.Facts.CredentialsSupplied ||
|
||||
report.Facts.CredentialsAccepted != nil || len(report.Attempts) != 1 ||
|
||||
report.Attempts[0].HTTPStatus == nil || *report.Attempts[0].HTTPStatus != http.StatusOK {
|
||||
t.Fatalf("partial HTTP evidence = %+v", report)
|
||||
}
|
||||
if strings.Contains(fmt.Sprintf("%+v", report), "synthetic-secret") {
|
||||
t.Fatalf("partial report retained credential: %+v", report)
|
||||
}
|
||||
}
|
||||
|
||||
func smokeTesterRunner(t *testing.T, values map[string]any) *Runner {
|
||||
t.Helper()
|
||||
loaded, err := configuration.Load(configkit.Input{Source: configkit.Mapping("smoke-test", values)}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return NewRunner(nil, loaded, nil, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func smokeClock() func() time.Time {
|
||||
current := time.Date(2026, time.October, 13, 12, 0, 0, 0, time.UTC)
|
||||
return func() time.Time {
|
||||
current = current.Add(time.Millisecond)
|
||||
return current
|
||||
}
|
||||
}
|
||||
|
||||
type smokeAdmissionSpy struct{ calls int }
|
||||
|
||||
func (a *smokeAdmissionSpy) Admit(context.Context, string) (func(), error) {
|
||||
a.calls++
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
type cancelingSmokeBody struct {
|
||||
cancel context.CancelFunc
|
||||
read bool
|
||||
}
|
||||
|
||||
func (b cancelingSmokeBody) Read(buffer []byte) (int, error) {
|
||||
if b.read {
|
||||
return 0, context.Canceled
|
||||
}
|
||||
b.read = true
|
||||
b.cancel()
|
||||
return copy(buffer, []byte(`{"data":{"label":"member","is_free_tier":false,"usage":1}}`)), context.Canceled
|
||||
}
|
||||
|
||||
func (cancelingSmokeBody) Close() error { return nil }
|
||||
Reference in New Issue
Block a user