Skip to content

Commit a675478

Browse files
committed
Add tests for complex multi-rule filter scenarios
Add two new test cases covering order-aware filtering with three rules: - `TestFilterUnits_SliceIncludeUnitIncludeUnitExclude`: Tests slice-include followed by unit-include to extend capture, then unit-exclude to remove a specific unit from the included slice. - `TestFilterUnits_SliceIncludeUnitIncludeSliceExclude`: Tests slice-include followed by broad unit-include pattern, then slice-exclude to override the unit-include for units in a specific slice. These tests verify that later rules correctly override earlier ones when combining slice and unit filters in various orders.
1 parent 9b66070 commit a675478

1 file changed

Lines changed: 129 additions & 0 deletions

File tree

systemd/systemd_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,3 +703,132 @@ func TestFilterUnits_SliceIncludeThenUnitInclude_ExtendsCapture(t *testing.T) {
703703
t.Errorf("extra.service should be included via unit-include rule")
704704
}
705705
}
706+
707+
// Test three-rule scenario: slice-include + unit-include (extra) + unit-exclude (from slice)
708+
func TestFilterUnits_SliceIncludeUnitIncludeUnitExclude(t *testing.T) {
709+
sliceMap := map[string]string{
710+
"app1.service": "myapp.slice",
711+
"app2.service": "myapp.slice",
712+
"excluded.service": "myapp.slice", // In the slice but will be excluded
713+
"extra.service": "system.slice", // NOT in myapp.slice, but will be included
714+
"other.service": "system.slice",
715+
}
716+
mockConn := createMockConn(sliceMap)
717+
718+
// Three-rule scenario:
719+
// 1. slice-include=myapp.slice (capture units in that slice)
720+
// 2. unit-include=^extra\.service$ (extend capture to this service)
721+
// 3. unit-exclude=^excluded\.service$ (remove this specific unit from slice)
722+
filterRules := []FilterRule{
723+
{Type: FilterTypeSlice, Action: FilterActionInclude, Pattern: "myapp.slice"},
724+
{Type: FilterTypeUnit, Action: FilterActionInclude, Pattern: regexp.MustCompile(`^(?:extra\.service)$`)},
725+
{Type: FilterTypeUnit, Action: FilterActionExclude, Pattern: regexp.MustCompile(`^(?:excluded\.service)$`)},
726+
}
727+
728+
collector := createTestCollector(filterRules)
729+
730+
testUnitsScenario := []dbus.UnitStatus{
731+
{Name: "app1.service", LoadState: "loaded"},
732+
{Name: "app2.service", LoadState: "loaded"},
733+
{Name: "excluded.service", LoadState: "loaded"},
734+
{Name: "extra.service", LoadState: "loaded"},
735+
{Name: "other.service", LoadState: "loaded"},
736+
}
737+
738+
filtered := collector.filterUnitsOrderAware(testUnitsScenario, mockConn)
739+
740+
// Expected:
741+
// - app1.service: in myapp.slice (rule 1) -> INCLUDED
742+
// - app2.service: in myapp.slice (rule 1) -> INCLUDED
743+
// - excluded.service: in myapp.slice (rule 1) then excluded (rule 3) -> EXCLUDED
744+
// - extra.service: not in myapp.slice, but matched by rule 2 -> INCLUDED
745+
// - other.service: not matched by any rule -> EXCLUDED (default for include-only)
746+
expectedUnits := map[string]bool{
747+
"app1.service": true,
748+
"app2.service": true,
749+
"extra.service": true,
750+
}
751+
752+
if len(filtered) != len(expectedUnits) {
753+
names := make([]string, 0, len(filtered))
754+
for _, u := range filtered {
755+
names = append(names, u.Name)
756+
}
757+
t.Errorf("Expected %d units, got %d: %v", len(expectedUnits), len(filtered), names)
758+
}
759+
760+
for _, unit := range filtered {
761+
if !expectedUnits[unit.Name] {
762+
t.Errorf("Unexpected unit in result: %s", unit.Name)
763+
}
764+
}
765+
766+
// Verify excluded.service is NOT in results
767+
for _, unit := range filtered {
768+
if unit.Name == "excluded.service" {
769+
t.Errorf("excluded.service should have been excluded by unit-exclude rule")
770+
}
771+
}
772+
}
773+
774+
// Test: slice-include + unit-include (broad) + slice-exclude
775+
// Three slices with an app in each. Include slice1, include all apps, exclude slice3.
776+
// Expected: app1 (from slice1) and app2 (from unit-include) but NOT app3 (slice-exclude overrides)
777+
func TestFilterUnits_SliceIncludeUnitIncludeSliceExclude(t *testing.T) {
778+
sliceMap := map[string]string{
779+
"app1.service": "slice1.slice",
780+
"app2.service": "slice2.slice",
781+
"app3.service": "slice3.slice",
782+
}
783+
mockConn := createMockConn(sliceMap)
784+
785+
// Rules:
786+
// 1. slice-include=slice1 (capture slice1)
787+
// 2. unit-include=^app.*\.service$ (extend to all app services)
788+
// 3. slice-exclude=slice3 (exclude slice3, overriding unit-include for app3)
789+
filterRules := []FilterRule{
790+
{Type: FilterTypeSlice, Action: FilterActionInclude, Pattern: "slice1"},
791+
{Type: FilterTypeUnit, Action: FilterActionInclude, Pattern: regexp.MustCompile(`^(?:app.*\.service)$`)},
792+
{Type: FilterTypeSlice, Action: FilterActionExclude, Pattern: "slice3"},
793+
}
794+
795+
collector := createTestCollector(filterRules)
796+
797+
testUnitsScenario := []dbus.UnitStatus{
798+
{Name: "app1.service", LoadState: "loaded"},
799+
{Name: "app2.service", LoadState: "loaded"},
800+
{Name: "app3.service", LoadState: "loaded"},
801+
}
802+
803+
filtered := collector.filterUnitsOrderAware(testUnitsScenario, mockConn)
804+
805+
// Expected:
806+
// - app1.service: in slice1 (rule 1) -> INCLUDED
807+
// - app2.service: matches app.* (rule 2) -> INCLUDED
808+
// - app3.service: matches app.* (rule 2), but in slice3 (rule 3) -> EXCLUDED
809+
expectedUnits := map[string]bool{
810+
"app1.service": true,
811+
"app2.service": true,
812+
}
813+
814+
if len(filtered) != len(expectedUnits) {
815+
names := make([]string, 0, len(filtered))
816+
for _, u := range filtered {
817+
names = append(names, u.Name)
818+
}
819+
t.Errorf("Expected %d units, got %d: %v", len(expectedUnits), len(filtered), names)
820+
}
821+
822+
for _, unit := range filtered {
823+
if !expectedUnits[unit.Name] {
824+
t.Errorf("Unexpected unit in result: %s", unit.Name)
825+
}
826+
}
827+
828+
// Verify app3.service is NOT in results
829+
for _, unit := range filtered {
830+
if unit.Name == "app3.service" {
831+
t.Errorf("app3.service should have been excluded by slice-exclude rule")
832+
}
833+
}
834+
}

0 commit comments

Comments
 (0)