package service import ( "strings" "testing" ) func dependency(id, from, to string, confidence float64, confirmed bool) Dependency { return Dependency{ID: id, ServiceID: from, DependsOnServiceID: to, RelationType: RelationDependsOn, Confidence: confidence, Confirmed: confirmed} } func TestValidateDependencyGraphRejectsCyclesDeterministically(t *testing.T) { first := []Dependency{dependency("b-a", "b", "a", 1, true), dependency("a-b", "a", "b", 1, true)} second := []Dependency{first[1], first[0]} err1 := ValidateDependencyGraph(first) err2 := ValidateDependencyGraph(second) if err1 == nil || err2 == nil || err1.Error() != err2.Error() || !strings.Contains(err1.Error(), "a") { t.Fatalf("cycle results were not deterministic: %v / %v", err1, err2) } } func TestBuildSuppressionInputsPreservesConfidenceAndManualConfirmation(t *testing.T) { items, err := BuildSuppressionInputs([]Dependency{ dependency("inferred", "app", "db", .8, false), dependency("manual", "worker", "db", .4, true), {ID: "backs", ServiceID: "db", DependsOnServiceID: "app", RelationType: RelationBacks, Confidence: 1, Confirmed: true}, }, map[string]string{"db": StateDown}) if err != nil { t.Fatal(err) } if len(items) != 2 || items[0].DependencyID != "inferred" || !items[0].Suppress || items[0].Reason != "inferred_dependency_failure" { t.Fatalf("unexpected inferred input: %+v", items) } if items[1].DependencyID != "manual" || !items[1].Confirmed || items[1].Confidence != .4 || !items[1].Suppress { t.Fatalf("unexpected manual input: %+v", items) } } func TestValidateDependencyGraphRejectsDuplicateEdges(t *testing.T) { items := []Dependency{dependency("one", "app", "db", 1, true), dependency("two", "app", "db", 1, false)} if err := ValidateDependencyGraph(items); err == nil || !strings.Contains(err.Error(), "duplicate") { t.Fatalf("expected duplicate edge error, got %v", err) } }