209 lines
6.6 KiB
Go
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))
|
|
}
|
|
}
|