package emitter import ( "strings" "testing" ) func TestDriftPathUnit_RenderUnits(t *testing.T) { specs := []DriftPathSpec{ {Pattern: "/etc/traefik/dynamic/orca.yml", SystemdPathUnit: true}, {Pattern: "/etc/nftables.d/orca.nft", SystemdPathUnit: true}, } files, err := (DriftPathUnit{}).RenderDriftUnits(specs) if err != nil { t.Fatalf("RenderDriftUnits: %v", err) } if len(files) != 4 { t.Fatalf("got %d files, want 4 (2 .path + 2 .service)", len(files)) } var pathUnits, svcUnits int for _, f := range files { if strings.HasSuffix(f.Path, ".path") { pathUnits++ } if strings.HasSuffix(f.Path, ".service") { svcUnits++ } } if pathUnits != 2 || svcUnits != 2 { t.Errorf("path=%d service=%d, want 2 and 2", pathUnits, svcUnits) } } func TestDriftPathUnit_PathUnitContent(t *testing.T) { files, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{ {Pattern: "/etc/traefik/dynamic/orca.yml", SystemdPathUnit: true}, }) if err != nil { t.Fatalf("Render: %v", err) } var pathFile *File for i := range files { if strings.HasSuffix(files[i].Path, ".path") { pathFile = &files[i] } } if pathFile == nil { t.Fatal("no .path unit emitted") } c := pathFile.Content for _, want := range []string{ "PathChanged=/etc/traefik/dynamic/orca.yml", "RateLimitIntervalSec=1s", "RateLimitBurst=5", "WantedBy=multi-user.target", } { if !strings.Contains(c, want) { t.Errorf("path unit missing %q:\n%s", want, c) } } } func TestDriftPathUnit_ServiceUnitSecurity(t *testing.T) { files, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{ {Pattern: "/etc/nftables.d/orca.nft", SystemdPathUnit: true}, }) if err != nil { t.Fatalf("Render: %v", err) } var svcFile *File for i := range files { if strings.HasSuffix(files[i].Path, ".service") { svcFile = &files[i] } } if svcFile == nil { t.Fatal("no .service unit emitted") } c := svcFile.Content for _, want := range []string{ "Type=oneshot", "ExecStart=/usr/local/bin/orca-drift-notify.sh %f", "User=orca", "Group=orca", "NoNewPrivileges=yes", "ProtectSystem=strict", "ReadWritePaths=/etc/orca/state/drift-events", "ProtectHome=yes", } { if !strings.Contains(c, want) { t.Errorf("service unit missing %q:\n%s", want, c) } } } func TestDriftPathUnit_UnitNameSlug(t *testing.T) { cases := []struct { pattern string want string }{ {"/etc/traefik/dynamic/orca.yml", "orca-drift-etc-traefik-dynamic-orca-yml"}, {"/etc/systemd/system/orca-alloc-*.service", "orca-drift-etc-systemd-system-orca-alloc-service"}, {"/etc/orca/actual/*/etc/sudoers.d/orca-*", "orca-drift-etc-orca-actual-etc-sudoers-d-orca"}, } for _, c := range cases { got := driftUnitName(c.pattern) if got != c.want { t.Errorf("driftUnitName(%q) = %q, want %q", c.pattern, got, c.want) } } } func TestDriftPathUnit_EmptyPatternErrors(t *testing.T) { _, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{{Pattern: ""}}) if err == nil { t.Fatal("expected error for empty pattern") } } func TestDriftPathUnit_NoSpecsNoFiles(t *testing.T) { files, err := (DriftPathUnit{}).RenderDriftUnits(nil) if err != nil { t.Fatalf("RenderDriftUnits nil: %v", err) } if files != nil { t.Errorf("got %d files, want nil", len(files)) } } func TestDriftPathUnit_DedupesSameSlug(t *testing.T) { // Two patterns that produce the same slug (after non-alnum collapse) // should dedupe to a single .path + .service pair. files, err := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{ {Pattern: "/etc/x/a.yml", SystemdPathUnit: true}, {Pattern: "/etc/x/a.yml", SystemdPathUnit: true}, }) if err != nil { t.Fatalf("Render: %v", err) } if len(files) != 2 { t.Errorf("got %d files, want 2 (deduped identical patterns)", len(files)) } } func TestDriftPathUnit_ServiceAfterPath(t *testing.T) { files, _ := (DriftPathUnit{}).RenderDriftUnits([]DriftPathSpec{ {Pattern: "/etc/traefik/dynamic/orca.yml", SystemdPathUnit: true}, }) for _, f := range files { if strings.HasSuffix(f.Path, ".service") { if !strings.Contains(f.Content, "After=orca-drift-etc-traefik-dynamic-orca-yml.path") { t.Errorf("service missing After=.path:\n%s", f.Content) } return } } t.Fatal("no service file found") }