package engine import ( "errors" "testing" ) func TestFailureAttribution(t *testing.T) { base := errors.New("detail") tests := []struct { name string result string err error wantClass string wantReason string }{ {name: "success", result: "success", wantClass: "none", wantReason: "success"}, {name: "timeout", result: "timeout", err: ErrTimedOut, wantClass: "user", wantReason: "timeout"}, {name: "cancelled", result: "cancelled", err: ErrWorkflowCanceled, wantClass: "user", wantReason: "cancelled"}, { name: "user command", result: "failure", err: ClassifiedFailure(FailureClassUser, FailureReasonCommandFailed, base), wantClass: "user", wantReason: "command_failed", }, { name: "infrastructure", result: "failure", err: ClassifiedFailure(FailureClassInfrastructure, FailureReasonRuntimeFailed, base), wantClass: "infrastructure", wantReason: "runtime_failed", }, { name: "policy", result: "failure", err: ClassifiedFailure(FailureClassPolicy, FailureReasonQuotaDenied, base), wantClass: "policy", wantReason: "quota_denied", }, {name: "capacity", result: "failure", err: ErrNoWorkflowSlots, wantClass: "infrastructure", wantReason: "capacity_unavailable"}, {name: "unclassified", result: "failure", err: base, wantClass: "infrastructure", wantReason: "runtime_failed"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotClass, gotReason := FailureAttribution(tt.result, tt.err) if gotClass != tt.wantClass || gotReason != tt.wantReason { t.Fatalf("FailureAttribution() = (%q, %q), want (%q, %q)", gotClass, gotReason, tt.wantClass, tt.wantReason) } }) } } func TestClassifiedFailurePreservesCauseAndFirstClassification(t *testing.T) { cause := errors.New("cause") first := ClassifiedFailure(FailureClassUser, FailureReasonCommandFailed, cause) second := ClassifiedFailure(FailureClassInfrastructure, FailureReasonRuntimeFailed, first) if second != first { t.Fatal("nested classification replaced the original leaf classification") } if !errors.Is(second, cause) { t.Fatal("classified failure does not preserve errors.Is") } }