185 lines
5.0 KiB
Go
185 lines
5.0 KiB
Go
package apps
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestTopoSort_SimpleChain(t *testing.T) {
|
|
manifests := map[string]AppManifest{
|
|
"metallb": {Name: "metallb"},
|
|
"traefik": {Name: "traefik", Requires: []AppDependency{{Name: "metallb"}}},
|
|
"cert-manager": {Name: "cert-manager", Requires: []AppDependency{{Name: "traefik"}}},
|
|
}
|
|
|
|
sorted, err := topoSort(manifests)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if len(sorted) != 3 {
|
|
t.Fatalf("expected 3 packages, got %d", len(sorted))
|
|
}
|
|
|
|
// metallb must come before traefik, traefik before cert-manager
|
|
indexOf := map[string]int{}
|
|
for i, m := range sorted {
|
|
indexOf[m.Name] = i
|
|
}
|
|
|
|
if indexOf["metallb"] >= indexOf["traefik"] {
|
|
t.Errorf("metallb (at %d) should come before traefik (at %d)", indexOf["metallb"], indexOf["traefik"])
|
|
}
|
|
if indexOf["traefik"] >= indexOf["cert-manager"] {
|
|
t.Errorf("traefik (at %d) should come before cert-manager (at %d)", indexOf["traefik"], indexOf["cert-manager"])
|
|
}
|
|
}
|
|
|
|
func TestTopoSort_MultipleDeps(t *testing.T) {
|
|
manifests := map[string]AppManifest{
|
|
"metallb": {Name: "metallb"},
|
|
"nfs": {Name: "nfs"},
|
|
"traefik": {Name: "traefik", Requires: []AppDependency{{Name: "metallb"}}},
|
|
"longhorn": {Name: "longhorn", Requires: []AppDependency{{Name: "traefik"}, {Name: "nfs"}}},
|
|
}
|
|
|
|
sorted, err := topoSort(manifests)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
indexOf := map[string]int{}
|
|
for i, m := range sorted {
|
|
indexOf[m.Name] = i
|
|
}
|
|
|
|
// longhorn must come after both traefik and nfs
|
|
if indexOf["traefik"] >= indexOf["longhorn"] {
|
|
t.Errorf("traefik should come before longhorn")
|
|
}
|
|
if indexOf["nfs"] >= indexOf["longhorn"] {
|
|
t.Errorf("nfs should come before longhorn")
|
|
}
|
|
// metallb must come before traefik
|
|
if indexOf["metallb"] >= indexOf["traefik"] {
|
|
t.Errorf("metallb should come before traefik")
|
|
}
|
|
}
|
|
|
|
func TestTopoSort_NoDeps(t *testing.T) {
|
|
manifests := map[string]AppManifest{
|
|
"a": {Name: "a"},
|
|
"b": {Name: "b"},
|
|
"c": {Name: "c"},
|
|
}
|
|
|
|
sorted, err := topoSort(manifests)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if len(sorted) != 3 {
|
|
t.Fatalf("expected 3 packages, got %d", len(sorted))
|
|
}
|
|
}
|
|
|
|
func TestTopoSort_CircularDependency(t *testing.T) {
|
|
manifests := map[string]AppManifest{
|
|
"a": {Name: "a", Requires: []AppDependency{{Name: "b"}}},
|
|
"b": {Name: "b", Requires: []AppDependency{{Name: "a"}}},
|
|
}
|
|
|
|
_, err := topoSort(manifests)
|
|
if err == nil {
|
|
t.Fatal("expected circular dependency error")
|
|
}
|
|
}
|
|
|
|
func TestTopoSort_IgnoresNonInfraDeps(t *testing.T) {
|
|
// If a package requires something not in the manifests map, it should be ignored
|
|
manifests := map[string]AppManifest{
|
|
"traefik": {Name: "traefik", Requires: []AppDependency{{Name: "metallb"}}},
|
|
"metallb": {Name: "metallb"},
|
|
}
|
|
|
|
// traefik requires metallb which IS in the map — should work
|
|
sorted, err := topoSort(manifests)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if sorted[0].Name != "metallb" {
|
|
t.Errorf("expected metallb first, got %s", sorted[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestTopoSort_IgnoresExternalDeps(t *testing.T) {
|
|
// A package that requires something NOT in the map (e.g., a non-infra app)
|
|
manifests := map[string]AppManifest{
|
|
"myservice": {Name: "myservice", Requires: []AppDependency{{Name: "postgres"}}},
|
|
}
|
|
|
|
sorted, err := topoSort(manifests)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if len(sorted) != 1 || sorted[0].Name != "myservice" {
|
|
t.Errorf("expected [myservice], got %v", sorted)
|
|
}
|
|
}
|
|
|
|
func TestInfrastructureOrder(t *testing.T) {
|
|
// Create a temp apps directory with test manifests
|
|
tmpDir := t.TempDir()
|
|
|
|
writeManifest := func(name, category string, requires []string) {
|
|
t.Helper()
|
|
dir := filepath.Join(tmpDir, name)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
content := "name: " + name + "\ncategory: " + category + "\n"
|
|
if len(requires) > 0 {
|
|
content += "requires:\n"
|
|
for _, r := range requires {
|
|
content += " - name: " + r + "\n"
|
|
}
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "manifest.yaml"), []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Create infrastructure packages
|
|
writeManifest("metallb", "services", nil)
|
|
writeManifest("traefik", "services", []string{"metallb"})
|
|
writeManifest("cert-manager", "services", []string{"traefik"})
|
|
|
|
// Create a non-infrastructure package (should be excluded)
|
|
writeManifest("postgres", "database", nil)
|
|
|
|
mgr := NewManager(t.TempDir(), tmpDir)
|
|
sorted, err := mgr.InfrastructureOrder()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if len(sorted) != 3 {
|
|
t.Fatalf("expected 3 infrastructure packages, got %d", len(sorted))
|
|
}
|
|
|
|
// Verify order: metallb -> traefik -> cert-manager
|
|
if sorted[0].Name != "metallb" {
|
|
t.Errorf("expected metallb first, got %s", sorted[0].Name)
|
|
}
|
|
if sorted[1].Name != "traefik" {
|
|
t.Errorf("expected traefik second, got %s", sorted[1].Name)
|
|
}
|
|
if sorted[2].Name != "cert-manager" {
|
|
t.Errorf("expected cert-manager third, got %s", sorted[2].Name)
|
|
}
|
|
}
|