Files
supply-intelligence/internal/probe/executor_test.go
2026-05-07 10:16:46 +08:00

220 lines
5.6 KiB
Go

package probe
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
// mockHTTPClient records requests and returns configurable responses
type mockHTTPClient struct {
Resp *http.Response
Err error
}
func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
// Simulate context cancellation: if the request context is done, return context error
select {
case <-req.Context().Done():
return nil, req.Context().Err()
default:
}
return m.Resp, m.Err
}
func TestProbeExecutor_ExecuteProbe_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
}))
defer server.Close()
executor := NewProbeExecutor(nil) // nil → uses real http.Client
outcome, err := executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 1,
Platform: "openai",
Endpoint: server.URL,
AuthHeader: "Bearer test-key",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if outcome.StatusCode != http.StatusOK {
t.Fatalf("expected 200, got: %d", outcome.StatusCode)
}
if outcome.LatencyMs < 0 {
t.Fatalf("expected latency >= 0, got: %d", outcome.LatencyMs)
}
if outcome.RequestID == "" {
t.Fatal("expected request_id to be set")
}
}
func TestProbeExecutor_ExecuteProbe_ExplicitFailure(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}))
defer server.Close()
executor := NewProbeExecutor(nil)
outcome, err := executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 2,
Platform: "openai",
Endpoint: server.URL,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if outcome.StatusCode != http.StatusUnauthorized {
t.Fatalf("expected 401, got: %d", outcome.StatusCode)
}
}
func TestProbeExecutor_ExecuteProbe_Inconclusive_429(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTooManyRequests)
}))
defer server.Close()
executor := NewProbeExecutor(nil)
outcome, err := executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 3,
Platform: "openai",
Endpoint: server.URL,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if outcome.StatusCode != http.StatusTooManyRequests {
t.Fatalf("expected 429, got: %d", outcome.StatusCode)
}
}
func TestProbeExecutor_ExecuteProbe_TransportError(t *testing.T) {
client := &mockHTTPClient{
Err: errors.New("connection refused"),
}
executor := NewProbeExecutor(client)
outcome, err := executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 4,
Platform: "openai",
Endpoint: "http://localhost:9999",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if outcome.TransportError == nil {
t.Fatal("expected transport error to be set")
}
if outcome.StatusCode != 0 {
t.Fatalf("expected status 0 on transport error, got: %d", outcome.StatusCode)
}
}
func TestProbeExecutor_ExecuteProbe_InvalidTarget(t *testing.T) {
executor := NewProbeExecutor(nil)
_, err := executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 5,
Platform: "openai",
Endpoint: "", // empty endpoint
})
if err == nil {
t.Fatal("expected error for empty endpoint")
}
}
func TestProbeExecutor_ExecuteProbe_ContextCanceled(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(5 * time.Second) // delay longer than context
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
executor := NewProbeExecutor(nil)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
outcome, err := executor.ExecuteProbe(ctx, ProbeTarget{
AccountID: 6,
Platform: "openai",
Endpoint: server.URL,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if outcome.TransportError == nil {
t.Fatal("expected context deadline exceeded transport error")
}
}
func TestProbeExecutor_ExecuteProbe_ResponseBodyTruncated(t *testing.T) {
largeBody := strings.Repeat("x", 10*1024) // 10KB
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(largeBody))
}))
defer server.Close()
executor := NewProbeExecutor(nil)
outcome, err := executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 7,
Platform: "openai",
Endpoint: server.URL,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(outcome.ResponseBody) > 1024 {
t.Fatalf("expected body truncated to <=1024, got: %d", len(outcome.ResponseBody))
}
}
func TestProbeExecutor_SetsUserAgentAndAcceptHeader(t *testing.T) {
var receivedHeaders http.Header
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedHeaders = r.Header.Clone()
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
executor := NewProbeExecutor(nil)
_, _ = executor.ExecuteProbe(context.Background(), ProbeTarget{
AccountID: 8,
Platform: "openai",
Endpoint: server.URL,
AuthHeader: "Bearer my-key",
})
if receivedHeaders == nil {
t.Fatal("server handler was not called — check test setup")
}
if receivedHeaders.Get("User-Agent") == "" {
t.Fatal("expected User-Agent header to be set")
}
if receivedHeaders.Get("Accept") != "application/json" {
t.Fatalf("expected Accept: application/json, got: %s", receivedHeaders.Get("Accept"))
}
if receivedHeaders.Get("Authorization") != "Bearer my-key" {
t.Fatalf("expected Authorization header to be set")
}
}