// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2021-2024 Simon Ruderich package config import ( "fmt" "os" "path/filepath" "testing" "ruderich.org/simon/safcm/testutil" ) func TestLoadGroups(t *testing.T) { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } defer os.Chdir(cwd) //nolint:errcheck err = os.Chdir("../testdata/project") if err != nil { t.Fatal(err) } hosts, err := LoadHosts() if err != nil { t.Fatal(err) } err = os.Chdir(cwd) if err != nil { t.Fatal(err) } tests := []struct { path string cfg *Config hosts *Hosts exp map[string][]string expErr error }{ { "../testdata/project", &Config{ GroupPriority: []string{ "detected_linux", "detected_freebsd", }, }, hosts, map[string][]string{ "group": { "detected_linux", "detected_freebsd", "host1.example.org", }, "group:remove": { "host2", "detected_mips", }, "group2": { "all", }, "group2:remove": { "remove", }, "group3": { "host1.example.org", }, "group3:remove": { "host2", }, "all_except_some": { "all", }, "all_except_some:remove": { "host1.example.org", "group2", }, "remove": { "host1.example.org", "host2", "host3.example.net", }, "remove:remove": { "host2", }, }, nil, }, { "../testdata/project", &Config{ GroupPriority: []string{ "detected_freebsd", "does-not-exist", }, }, hosts, nil, fmt.Errorf("config.yaml: group_priority: group \"does-not-exist\" does not exist"), }, { "../testdata/project", &Config{ GroupPriority: []string{ "detected_freebsd", "special:group", }, }, hosts, nil, fmt.Errorf("config.yaml: group_priority: invalid group name \"special:group\""), }, { "../testdata/project", &Config{ GroupPriority: []string{ "detected_freebsd", "group:remove", }, }, hosts, nil, fmt.Errorf("config.yaml: group_priority: invalid group name \"group:remove\""), }, { "../testdata/group-invalid-all", &Config{}, hosts, nil, fmt.Errorf("groups.yaml: group \"all\": conflict with pre-defined group \"all\""), }, { "../testdata/group-invalid-all-remove", &Config{}, hosts, nil, fmt.Errorf("groups.yaml: group \"all:remove\": conflict with pre-defined group \"all:remove\""), }, { "../testdata/group-invalid-conflict", &Config{}, hosts, nil, fmt.Errorf("groups.yaml: group \"host2\": conflict with existing host"), }, { "../testdata/group-invalid-conflict-remove", &Config{}, hosts, nil, fmt.Errorf("groups.yaml: group \"host2:remove\": conflict with existing host"), }, { "../testdata/group-invalid-detected", &Config{}, &Hosts{}, nil, fmt.Errorf("groups.yaml: group \"detected_linux\": name must not start with \"detected\" (reserved for detected groups)"), }, { "../testdata/group-invalid-member", &Config{}, &Hosts{}, nil, fmt.Errorf("groups.yaml: group \"group1\": member \"special:member\" must not contain \":\""), }, { "../testdata/group-invalid-missing", &Config{}, &Hosts{}, nil, fmt.Errorf("groups.yaml: group \"group2\": member \"does-not-exist\" not found"), }, { "../testdata/group-invalid-name", &Config{}, &Hosts{}, nil, fmt.Errorf("groups.yaml: group \"invalid.group.name\": name contains invalid characters (must match ^[a-z0-9_-]+$)"), }, } for _, tc := range tests { t.Run(tc.path, func(t *testing.T) { err := os.Chdir(filepath.Join(cwd, tc.path)) if err != nil { t.Fatal(err) } res, err := LoadGroups(tc.cfg, tc.hosts) testutil.AssertEqual(t, "res", res, tc.exp) testutil.AssertErrorEqual(t, "err", err, tc.expErr) }) } } func TestResolveHostGroups(t *testing.T) { cwd, err := os.Getwd() if err != nil { t.Fatal(err) } defer os.Chdir(cwd) //nolint:errcheck err = os.Chdir("../testdata/project") if err != nil { t.Fatal(err) } allHosts, err := LoadHosts() if err != nil { t.Fatal(err) } allGroups, err := LoadGroups(&Config{}, allHosts) if err != nil { t.Fatal(err) } tests := []struct { name string host string detected []string exp []string expErr error }{ { "host1", "host1.example.org", nil, []string{ "all", "group", "group3", "host1.example.org", "remove", }, nil, }, { "host2", "host2", nil, []string{ "all", "group2", "host2", }, nil, }, { "host3", "host3.example.net", nil, []string{ "all", "all_except_some", "host3.example.net", "remove", }, nil, }, { "unknown host", "unknown", nil, []string{ "all", "group2", "unknown", }, nil, }, { "host1, detected_mips", "host1.example.org", []string{ "detected_mips", }, []string{ "all", "detected_mips", "group3", "host1.example.org", "remove", }, nil, }, { "host2, detected_linux", "host2", []string{ "detected_linux", }, []string{ "all", "detected_linux", "group2", "host2", }, nil, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { res, err := ResolveHostGroups(tc.host, allGroups, tc.detected) testutil.AssertEqual(t, "res", res, tc.exp) testutil.AssertErrorEqual(t, "err", err, tc.expErr) }) } } func TestTransitivelyDetectedGroups(t *testing.T) { tests := []struct { name string groups map[string][]string exp map[string]bool }{ { "no detected", map[string][]string{ "group-a": { "a", "b", "group-b", }, "group-a:remove": { "d", }, "group-b": { "c", "d", }, }, map[string]bool{}, }, { "detected as direct member", map[string][]string{ "group-a": { "a", "b", "detected_foo", }, "group-b": { "c", "d", }, }, map[string]bool{ "group-a": true, }, }, { "detected as direct :remove member", map[string][]string{ "group-a": { "a", "b", "group-b", }, "group-a:remove": { "d", "detected_foo", }, "group-b": { "c", "d", }, }, map[string]bool{ "group-a": true, }, }, { "detected as transitive member", map[string][]string{ "group-a": { "group-b", }, "group-b": { "group-c", }, "group-c": { "group-d", "detected_bar", }, "group-d": { "group-e", }, "group-e": { "detected_foo", }, "group-f": { "a", "b", }, }, map[string]bool{ "group-a": true, "group-b": true, "group-c": true, "group-d": true, "group-e": true, }, }, { "detected as transitive :remove member", map[string][]string{ "group-a": { "group-b", }, "group-b": { "group-c", }, "group-c": { "group-d", }, "group-d": { "group-e", }, "group-e": { "all", }, "group-e:remove": { "detected_foo", }, "group-f": { "a", "b", }, }, map[string]bool{ "group-a": true, "group-b": true, "group-c": true, "group-d": true, "group-e": true, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { res := TransitivelyDetectedGroups(tc.groups) testutil.AssertEqual(t, "res", res, tc.exp) }) } }