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

209 lines
6.6 KiB
Go

package admission
import (
"context"
"errors"
"testing"
"time"
)
type mockCandidateRepo struct {
candidates map[string]Candidate
}
func (r *mockCandidateRepo) GetCandidateByIDContext(ctx context.Context, candidateID string) (Candidate, bool) {
c, ok := r.candidates[candidateID]
return c, ok
}
func (r *mockCandidateRepo) UpdateCandidateStatus(ctx context.Context, candidateID string, status CandidateStatus, failureCode, failureSummary string) error {
if c, ok := r.candidates[candidateID]; ok {
c.Status = status
c.ReasonCode = failureCode
c.UpdatedAt = time.Now().UTC()
r.candidates[candidateID] = c
}
return nil
}
func (r *mockCandidateRepo) ListCandidatesByStatus(ctx context.Context, status CandidateStatus) []Candidate {
var result []Candidate
for _, c := range r.candidates {
if status == "" || c.Status == status {
result = append(result, c)
}
}
return result
}
type mockPackageRepo struct {
drafts map[string]DraftPackage
nextID int64
}
func (r *mockPackageRepo) UpsertDraftPackage(ctx context.Context, platform, model, source string) (int64, error) {
r.nextID++
id := r.nextID
r.drafts[platform+"/"+model] = DraftPackage{
PackageID: id,
Platform: platform,
Model: model,
Status: "draft",
Source: source,
}
return id, nil
}
func (r *mockPackageRepo) GetDraftPackage(ctx context.Context, platform, model string) (DraftPackage, bool) {
d, ok := r.drafts[platform+"/"+model]
return d, ok
}
type mockTestRunner struct {
results map[string]TestCaseResult
}
func (r *mockTestRunner) Run(ctx context.Context, tc TestCase) TestCaseResult {
if res, ok := r.results[tc.ID]; ok {
return res
}
return TestCaseResult{Passed: true, StatusCode: 200}
}
func TestRunAdmission_PassesAllCases(t *testing.T) {
candidateRepo := &mockCandidateRepo{candidates: map[string]Candidate{
"cand-1": {CandidateID: "cand-1", Platform: "openai", Model: "gpt-4", Status: CandidateStatusDiscovered},
}}
packageRepo := &mockPackageRepo{drafts: map[string]DraftPackage{}}
runner := &mockTestRunner{results: map[string]TestCaseResult{}}
suites := []TestSuite{{
Platform: "openai",
Cases: []TestCase{
{ID: "case-1", Name: "models endpoint", Endpoint: "/v1/models", Method: "GET", TimeoutSecs: 30},
},
}}
svc := NewService(candidateRepo, packageRepo, suites, runner, nil)
result, err := svc.RunAdmission(context.Background(), "cand-1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !result.Passed {
t.Fatalf("expected pass, got failed: %+v", result)
}
if result.Status != CandidateStatusTestPassed {
t.Fatalf("expected test_passed status, got: %s", result.Status)
}
if len(packageRepo.drafts) != 1 {
t.Fatalf("expected 1 draft package, got %d", len(packageRepo.drafts))
}
}
func TestRunAdmission_FailsOneCase(t *testing.T) {
candidateRepo := &mockCandidateRepo{candidates: map[string]Candidate{
"cand-2": {CandidateID: "cand-2", Platform: "openai", Model: "gpt-4", Status: CandidateStatusDiscovered},
}}
packageRepo := &mockPackageRepo{drafts: map[string]DraftPackage{}}
runner := &mockTestRunner{results: map[string]TestCaseResult{
"case-1": {Passed: false, StatusCode: 500, Error: ""},
}}
suites := []TestSuite{{
Platform: "openai",
Cases: []TestCase{
{ID: "case-1", Name: "models endpoint", Endpoint: "/v1/models", Method: "GET", TimeoutSecs: 30},
},
}}
svc := NewService(candidateRepo, packageRepo, suites, runner, nil)
result, err := svc.RunAdmission(context.Background(), "cand-2")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Passed {
t.Fatalf("expected failure, got pass")
}
if result.Status != CandidateStatusTestFailed {
t.Fatalf("expected test_failed status, got: %s", result.Status)
}
if result.FailureCode == "" {
t.Fatalf("expected failure code to be set")
}
if len(packageRepo.drafts) != 0 {
t.Fatalf("expected 0 draft packages on failure, got %d", len(packageRepo.drafts))
}
}
func TestRunAdmission_CandidateNotFound(t *testing.T) {
candidateRepo := &mockCandidateRepo{candidates: map[string]Candidate{}}
packageRepo := &mockPackageRepo{drafts: map[string]DraftPackage{}}
runner := &mockTestRunner{results: map[string]TestCaseResult{}}
svc := NewService(candidateRepo, packageRepo, []TestSuite{}, runner, nil)
_, err := svc.RunAdmission(context.Background(), "nonexistent")
if !errors.Is(err, ErrCandidateNotFound) {
t.Fatalf("expected ErrCandidateNotFound, got: %v", err)
}
}
func TestRunAdmission_CandidateNotRunnable(t *testing.T) {
candidateRepo := &mockCandidateRepo{candidates: map[string]Candidate{
"cand-3": {CandidateID: "cand-3", Platform: "openai", Model: "gpt-4", Status: CandidateStatusTestPassed},
}}
packageRepo := &mockPackageRepo{drafts: map[string]DraftPackage{}}
runner := &mockTestRunner{results: map[string]TestCaseResult{}}
svc := NewService(candidateRepo, packageRepo, []TestSuite{}, runner, nil)
_, err := svc.RunAdmission(context.Background(), "cand-3")
if !errors.Is(err, ErrCandidateNotRunnable) {
t.Fatalf("expected ErrCandidateNotRunnable, got: %v", err)
}
}
func TestRunAdmission_NoTestSuite_FailsClosed(t *testing.T) {
candidateRepo := &mockCandidateRepo{candidates: map[string]Candidate{
"cand-4": {CandidateID: "cand-4", Platform: "unknown-platform", Model: "some-model", Status: CandidateStatusDiscovered},
}}
packageRepo := &mockPackageRepo{drafts: map[string]DraftPackage{}}
runner := &mockTestRunner{results: map[string]TestCaseResult{}}
svc := NewService(candidateRepo, packageRepo, []TestSuite{}, runner, nil)
result, err := svc.RunAdmission(context.Background(), "cand-4")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Passed {
t.Fatalf("expected fail-closed for unknown platform, got: %+v", result)
}
if result.Status != CandidateStatusTestFailed {
t.Fatalf("expected test_failed status, got: %s", result.Status)
}
if result.FailureCode != "test_suite_missing" {
t.Fatalf("expected test_suite_missing, got: %s", result.FailureCode)
}
}
func TestGetRunnableCandidates(t *testing.T) {
candidateRepo := &mockCandidateRepo{candidates: map[string]Candidate{
"cand-1": {CandidateID: "cand-1", Status: CandidateStatusDiscovered},
"cand-2": {CandidateID: "cand-2", Status: CandidateStatusTestPassed},
"cand-3": {CandidateID: "cand-3", Status: CandidateStatusRetryPending},
"cand-4": {CandidateID: "cand-4", Status: CandidateStatusTesting},
}}
packageRepo := &mockPackageRepo{drafts: map[string]DraftPackage{}}
runner := &mockTestRunner{}
svc := NewService(candidateRepo, packageRepo, []TestSuite{}, runner, nil)
candidates := svc.GetRunnableCandidates(context.Background())
if len(candidates) != 2 {
t.Fatalf("expected 2 runnable candidates, got %d", len(candidates))
}
}