package config import ( "bufio" "os" "path/filepath" "strings" "testing" "time" "guard/internal/match" ) // TestPresets runs each preset over its sample lines in testdata/presets: // what it must find, and what it must leave alone. func TestPresets(t *testing.T) { for name, p := range Presets() { if p.Source != "" { continue } m, err := match.Compile(p.Match, p.Ignore) if err != nil { t.Fatalf("preset %s: %v", name, err) } f, err := os.Open(filepath.Join("..", "..", "testdata", "presets", name+".txt")) if err != nil { t.Errorf("preset %s has no sample lines: %v", name, err) continue } sc := bufio.NewScanner(f) n := 0 for sc.Scan() { want, line, ok := strings.Cut(sc.Text(), "\t") if !ok || strings.HasPrefix(want, "#") { continue } n++ a, matched := m.Match(line) switch { case want == "-" && matched: t.Errorf("%s: matched %s in %q", name, a, line) case want != "-" && !matched: t.Errorf("%s: no match in %q", name, line) case want != "-" && a.String() != want: t.Errorf("%s: %s, want %s, in %q", name, a, want, line) } } f.Close() if n == 0 { t.Errorf("preset %s: no sample lines", name) } } } func TestExampleParses(t *testing.T) { c, err := Parse([]byte(Example())) if err != nil { t.Fatal(err) } w := c.Watch[0] if w.Kind() != "journal" || w.Ban.List != "web" || w.Hits != 5 || w.Within.D() != 10*time.Minute || len(w.Match) == 0 { t.Errorf("the ssh watch: %+v", w) } if c.Defaults.Repeat.Max.D() != 7*24*time.Hour { t.Errorf("repeat %+v", c.Defaults.Repeat) } } func TestErrors(t *testing.T) { base := "housecarl: {url: https://fw:8070, list: web}\n" cases := []struct{ yaml, want string }{ {"", "give url"}, {base + "watch: [{name: a, preset: nope}]", `no preset "nope"`}, {base + "watch: [{name: a, file: /x, match: ['no address']}]", "exactly one"}, {base + "watch: [{name: a, file: /x, journal: {unit: x}, match: ['']}]", "one source"}, {base + "watch: [{name: a, file: /x}]", "no match patterns"}, {base + "watch: [{name: a, source: housecarl-drops, match: ['']}]", "no patterns"}, {base + "watch: [{name: a, preset: sshd, ban: {protocol: tcp}}]", "without ban.port"}, {base + "watch: [{name: a, preset: sshd, within: 10 minutes}]", "isn't a length of time"}, {base + "watch: [{name: a, preset: sshd}, {name: A, preset: sshd}]", "used twice"}, {base + "never_ban: ['not an address!']\nwatch: [{name: a, preset: sshd}]", "never_ban"}, {base + "bogus: 1\nwatch: [{name: a, preset: sshd}]", "bogus"}, {"housecarl: {url: https://fw:8070}\nwatch: [{name: a, preset: sshd}]", "which ban list"}, } for _, c := range cases { _, err := Parse([]byte(c.yaml)) if err == nil || !strings.Contains(err.Error(), c.want) { t.Errorf("%q: %v, want %q", c.yaml, err, c.want) } } } func TestPresetSourceAndOwnPatterns(t *testing.T) { c, err := Parse([]byte(`housecarl: {socket: /run/housecarl/housecarl.sock, list: router} watch: - {name: ssh, preset: sshd, file: /var/log/auth.log, match: ['custom line']} - {name: scans, preset: housecarl-drops} `)) if err != nil { t.Fatal(err) } ssh := c.Watch[0] if ssh.Kind() != "file" || ssh.File != "/var/log/auth.log" || ssh.Match[len(ssh.Match)-1] != "custom line" || len(ssh.Match) < 2 { t.Errorf("own file and pattern beside the preset's: %+v", ssh) } if s := c.Watch[1]; s.Kind() != SourceHousecarlDrops || s.Protocol != "tcp" { t.Errorf("housecarl-drops: %+v", s) } } func TestDuration(t *testing.T) { for in, want := range map[string]time.Duration{"30s": 30 * time.Second, "1d12h": 36 * time.Hour, "1w": 7 * 24 * time.Hour} { if d, err := ParseDuration(in); err != nil || d != want { t.Errorf("%s: %v %v", in, d, err) } if got := FormatDuration(want); got != in { t.Errorf("format %v: %s, want %s", want, got, in) } } }