package dag import "testing" // TestCircularDependency reproduces the original "A -> B -> C -> A" loop and // ensures DetectCycles rejects the offending edge while keeping the Graph // in a valid state (edge is rolled back). func TestCircularDependency(t *testing.T) { g := NewGraph() if err := g.AddDependency("gcc", "glibc", BuildDep, nil); err != nil { t.Fatalf("first edge should succeed: %v", err) } if err := g.AddDependency("glibc", "linux-headers", BuildDep, nil); err != nil { t.Fatalf("second edge should succeed: %v", err) } // Closing the loop must fail. if err := g.AddDependency("linux-headers", "gcc", BuildDep, nil); err == nil { t.Fatalf("expected cycle error, got nil") } else { t.Logf("✓ correctly caught loop: %v", err) } // Graph must still be intact (rolled back). if n := g.Nodes["linux-headers"]; n != nil && len(n.Edges) != 0 { t.Fatalf("linux-headers should have no edges after rollback, got %d", len(n.Edges)) } } func TestTopologicalSort(t *testing.T) { g := NewGraph() _ = g.AddDependency("wget", "openssl", RuntimeDep, nil) _ = g.AddDependency("wget", "glibc", RuntimeDep, nil) _ = g.AddDependency("openssl", "glibc", RuntimeDep, nil) order, err := g.TopologicalSort() if err != nil { t.Fatalf("topo sort: %v", err) } pos := make(map[string]int) for i, n := range order { pos[n.Name] = i } if pos["glibc"] >= pos["openssl"] { t.Fatalf("glibc must come before openssl") } if pos["glibc"] >= pos["wget"] { t.Fatalf("glibc must come before wget") } if pos["openssl"] >= pos["wget"] { t.Fatalf("openssl must come before wget") } } func TestPruneSkipsOptional(t *testing.T) { g := NewGraph() _ = g.AddDependency("app", "libcore", RuntimeDep, nil) _ = g.AddDependency("app", "libx11", OptionalDep, nil) _ = g.AddDependency("libx11", "libxext", RuntimeDep, nil) g.Prune("app", false) if !g.Nodes["libcore"].IsRequired { t.Fatalf("libcore must be required") } if g.Nodes["libx11"].IsRequired { t.Fatalf("libx11 (optional, not enabled) must be pruned") } if g.Nodes["libxext"].IsRequired { t.Fatalf("libxext must be pruned along with libx11") } } func TestSolverTriggersReForge(t *testing.T) { g := NewGraph() _ = g.AddDependency("wget", "openssl", RuntimeDep, []string{"ssl3"}) // Pretend the existing openssl Essence does NOT have ssl3 enabled. s := &Solver{Lookup: func(spell, feature string) (bool, error) { return false, nil }} reforges, err := s.Solve("wget", g) if err != nil { t.Fatalf("solve: %v", err) } if len(reforges) == 0 { t.Fatalf("expected a re-forge recommendation for openssl+ssl3") } }