- internal/ai/types.go: IntakeRequest, IntakeResult, TierConfig, AIConfig domain types - internal/ai/client.go: AIClient interface + TierClient (go-openai, BaseURL tier-routing) - internal/ai/mock.go: MockAIClient test double with HighConfidenceResult/LowConfidenceResult fixtures - internal/ai/prompts/intake.go: BuildIntakePrompt() JSON-extraction prompt template - internal/config/config.go: Config.AI AIConfig field, tier defaults, env bindings, ai_config.json merge - ai_config.json: template config with placeholder Tier2 API key - .gitignore: add ai_config.local.json pattern for real keys (T-02-01 mitigation) - All tests pass: TestMockAIClient, TestMockAIClientError, TestTierClientConstruction, TestAIConfigDefaults
65 lines
1.5 KiB
Go
65 lines
1.5 KiB
Go
package ai_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.georgsen.dk/hwlab/internal/ai"
|
|
)
|
|
|
|
func TestMockAIClient(t *testing.T) {
|
|
mock := &ai.MockAIClient{
|
|
FixedResult: ai.HighConfidenceResult(),
|
|
}
|
|
|
|
result, err := mock.AnalyzePhotos(context.Background(), ai.IntakeRequest{
|
|
PhotosBase64: []string{"data:image/jpeg;base64,AAAA"},
|
|
JobID: "test-job-1",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result.Confidence != 0.95 {
|
|
t.Errorf("expected confidence 0.95, got %f", result.Confidence)
|
|
}
|
|
if len(mock.Calls) != 1 {
|
|
t.Errorf("expected 1 call recorded, got %d", len(mock.Calls))
|
|
}
|
|
}
|
|
|
|
func TestMockAIClientError(t *testing.T) {
|
|
mock := &ai.MockAIClient{
|
|
FixedError: errors.New("timeout"),
|
|
}
|
|
|
|
result, err := mock.AnalyzePhotos(context.Background(), ai.IntakeRequest{})
|
|
if err == nil {
|
|
t.Error("expected error, got nil")
|
|
}
|
|
if result != nil {
|
|
t.Errorf("expected nil result on error, got %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestTierClientConstruction(t *testing.T) {
|
|
cfg := ai.TierConfig{
|
|
BaseURL: "http://localhost:9999/v1",
|
|
APIKey: "x",
|
|
Model: "m",
|
|
TimeoutSeconds: 1,
|
|
}
|
|
client := ai.NewTierClient(cfg)
|
|
if client == nil {
|
|
t.Fatal("expected non-nil TierClient")
|
|
}
|
|
|
|
// AnalyzePhotos should return a non-nil error (connection refused) — not panic
|
|
_, err := client.AnalyzePhotos(context.Background(), ai.IntakeRequest{
|
|
PhotosBase64: []string{"data:image/jpeg;base64,AAAA"},
|
|
JobID: "test-job-2",
|
|
})
|
|
if err == nil {
|
|
t.Error("expected connection refused error, got nil")
|
|
}
|
|
}
|