package usecase import ( "strings" "testing" ) func TestValidateCandidateCommandAcceptsEmptyMatchedBatch(t *testing.T) { command := validAIExecutionCandidateCommand() command.Candidates = nil command.Recommendation = nil if err := validateCandidateCommand(command); err != nil { t.Fatalf("validate empty matched batch: %v", err) } } func TestValidateCandidateCommandAcceptsMatchedColorAndSize(t *testing.T) { command := validAIExecutionCandidateCommand() if err := validateCandidateCommand(command); err != nil { t.Fatalf("validate matched candidate: %v", err) } } func TestValidateCandidateCommandRejectsUnknownHardConstraint(t *testing.T) { command := validAIExecutionCandidateCommand() command.Candidates[0].Evaluation.HardConstraints[1].Status = "UNKNOWN" command.Candidates[0].Evaluation.Decision = "REJECT" command.Candidates[0].Evaluation.RejectionReasons = []string{"尺码无法确认"} command.Recommendation = nil if err := validateCandidateCommand(command); err != nil { t.Fatalf("validate observed unknown hard constraint: %v", err) } } func TestValidateCandidateCommandRejectsV2CandidateWithoutHardConstraints(t *testing.T) { command := validAIExecutionCandidateCommand() command.Candidates[0].Evaluation.HardConstraints = nil if err := validateCandidateCommand(command); err == nil { t.Fatal("expected missing v2 hard constraints to be rejected") } } func TestValidateCandidateCommandRejectsWeakV2Candidate(t *testing.T) { command := validAIExecutionCandidateCommand() command.Candidates[0].Evaluation.Score = 0.74 if err := validateCandidateCommand(command); err == nil { t.Fatal("expected weak v2 candidate to be rejected") } } func TestValidateCandidateCommandAcceptsV2RecommendationUsingOriginalOrdinal(t *testing.T) { command := validAIExecutionCandidateCommand() second := command.Candidates[0] second.Ordinal = 2 second.Title = "拼多多图片候选 2" command.Candidates = append(command.Candidates, second) command.Recommendation.CandidateOrdinal = 2 if err := validateCandidateCommand(command); err != nil { t.Fatalf("validate recommendation using original ordinal: %v", err) } } func TestValidateCandidateCommandRejectsV2RecommendationForRejectedCandidate(t *testing.T) { command := validAIExecutionCandidateCommand() second := command.Candidates[0] second.Ordinal = 2 second.Title = "拼多多图片候选 2" second.Evaluation = &CandidateEvaluation{ Decision: "REJECT", Score: 0.2, Confidence: 0.9, RejectionReasons: []string{"颜色不匹配"}, } command.Candidates = append(command.Candidates, second) command.Recommendation.CandidateOrdinal = 2 if err := validateCandidateCommand(command); err == nil { t.Fatal("expected recommendation for rejected candidate to be rejected") } } func validAIExecutionCandidateCommand() StoreExecutionCandidatesCommand { return StoreExecutionCandidatesCommand{ TaskContentSHA256: strings.Repeat("a", 64), ExecutionMode: aiAssistedMode, SearchQuery: "PDD_IMAGE_SEARCH", Provenance: &ExecutionProvenance{ ProviderID: "openai-compatible", Model: "test-model", PromptVersion: "candidate-evaluation-v2", SchemaVersion: 2, }, Candidates: []ExecutionCandidate{ { Ordinal: 1, Title: "拼多多图片候选 1", CardSignature: strings.Repeat("b", 64), DetailSignature: strings.Repeat("c", 64), DetailEvidenceSHA256: strings.Repeat("d", 64), SpecificationEvidenceSHA256: strings.Repeat("e", 64), EvidenceAssetIDs: []string{ "00000000-0000-4000-8000-000000000001", "00000000-0000-4000-8000-000000000002", }, Evaluation: &CandidateEvaluation{ Decision: "REVIEW", Score: 0.9, Matched: []string{"图片相似"}, Confidence: 0.9, HardConstraints: []CandidateHardConstraintEvaluation{ { Kind: "COLOR", Expected: "BLACK", Status: "MATCH", Evidence: "截图显示黑色", }, { Kind: "SIZE", Expected: "L", Status: "MATCH", Evidence: "截图显示 L 码", }, }, }, }, }, Recommendation: &CandidateRecommendation{ CandidateOrdinal: 1, PolicyVersion: "sku-hard-constraints-v1", Reasons: []string{"SKU 颜色和尺码硬约束均匹配"}, }, } } func TestStoreCandidatesRejectsDuplicateEvidenceReferences(t *testing.T) { command := validAIExecutionCandidateCommand() command.Candidates[0].EvidenceAssetIDs[1] = command.Candidates[0].EvidenceAssetIDs[0] if err := validateCandidateCommand(command); err == nil { t.Fatal("expected duplicate evidence references to be rejected") } }