Files
2026-05-12 18:49:52 +08:00

137 lines
3.5 KiB
Go

package admission
import (
"bytes"
"context"
"io"
"net/http"
"os"
"time"
)
// HTTPTestRunner implements TestRunner by making real HTTP requests
type HTTPTestRunner struct {
client *http.Client
now func() time.Time
}
// NewHTTPTestRunner creates a runner that makes real HTTP calls
func NewHTTPTestRunner() *HTTPTestRunner {
return &HTTPTestRunner{
client: &http.Client{
Timeout: 60 * time.Second,
},
now: func() time.Time { return time.Now().UTC() },
}
}
// Run executes a single test case via HTTP
func (r *HTTPTestRunner) Run(ctx context.Context, tc TestCase) TestCaseResult {
// Allow mock mode for local verification without real API keys
if os.Getenv("ADMISSION_TEST_MOCK") == "1" {
return TestCaseResult{Passed: true, StatusCode: 200, LatencyMs: 1}
}
var body io.Reader
if tc.Body != "" {
body = bytes.NewBufferString(tc.Body)
}
req, err := http.NewRequestWithContext(ctx, tc.Method, tc.Endpoint, body)
if err != nil {
return TestCaseResult{Error: err.Error()}
}
for k, v := range tc.Headers {
req.Header.Set(k, v)
}
if req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "application/json")
}
start := time.Now()
resp, err := r.client.Do(req)
latencyMs := int(time.Since(start).Milliseconds())
if err != nil {
return TestCaseResult{
Error: err.Error(),
LatencyMs: latencyMs,
}
}
defer resp.Body.Close()
// Read response (up to 4KB for validation)
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
passed := resp.StatusCode >= 200 && resp.StatusCode < 300
return TestCaseResult{
Passed: passed,
StatusCode: resp.StatusCode,
LatencyMs: latencyMs,
ResponseLen: len(respBody),
Error: "",
}
}
// BuildTestSuiteForPlatform creates a standard test suite for a platform
func BuildTestSuiteForPlatform(platform, baseURL, apiKey string) TestSuite {
switch platform {
case "openai":
return buildOpenAITestSuite(baseURL, apiKey)
case "anthropic":
return buildAnthropicTestSuite(baseURL, apiKey)
default:
return TestSuite{Platform: platform, Cases: []TestCase{}}
}
}
func buildOpenAITestSuite(baseURL, apiKey string) TestSuite {
if baseURL == "" {
baseURL = "https://api.openai.com"
}
endpoint := baseURL + "/v1/models"
return TestSuite{
Platform: "openai",
Cases: []TestCase{
{
ID: "openai-models-list",
Name: "List Models",
Endpoint: endpoint,
Method: http.MethodGet,
Headers: map[string]string{"Authorization": "Bearer " + apiKey},
TimeoutSecs: 30,
},
{
ID: "openai-chat-completion",
Name: "Chat Completion",
Endpoint: baseURL + "/v1/chat/completions",
Method: http.MethodPost,
Headers: map[string]string{"Authorization": "Bearer " + apiKey, "Content-Type": "application/json"},
Body: `{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}],"max_tokens":10}`,
TimeoutSecs: 30,
},
},
}
}
func buildAnthropicTestSuite(baseURL, apiKey string) TestSuite {
if baseURL == "" {
baseURL = "https://api.anthropic.com"
}
return TestSuite{
Platform: "anthropic",
Cases: []TestCase{
{
ID: "anthropic-messages",
Name: "Claude Messages",
Endpoint: baseURL + "/v1/messages",
Method: http.MethodPost,
Headers: map[string]string{"x-api-key": apiKey, "anthropic-version": "2023-06-01", "Content-Type": "application/json"},
Body: `{"model":"claude-3-5-haiku-20241022","messages":[{"role":"user","content":"hello"}],"max_tokens":10}`,
TimeoutSecs: 30,
},
},
}
}