package main import ( "regexp" "strings" "testing" "housecarl/internal/matrix" "housecarl/internal/model" ) func TestDurations(t *testing.T) { for in, want := range map[string]int{"3600": 3600, "12h": 43200, "30m": 1800, "1d": 86400, "1d12h": 129600, "1h30m": 5400} { if got, err := parseSeconds(in); err != nil || got != want { t.Errorf("parseSeconds(%q) = %d, %v; want %d", in, got, err, want) } } for _, bad := range []string{"12x", "d", "1d2x"} { if _, err := parseSeconds(bad); err == nil { t.Errorf("parseSeconds(%q) accepted", bad) } } for n, want := range map[int]string{43200: "12h", 5400: "1h30m", 90061: "1d1h1m1s", 0: "-"} { if got := fmtSeconds(n); got != want { t.Errorf("fmtSeconds(%d) = %q, want %q", n, got, want) } } } func TestLimitAndDescribe(t *testing.T) { l, err := parseLimit("3/minutes,burst=5,over,per-source") if err != nil || l.Rate != 3 || l.Per != "minute" || l.Burst != 5 || !l.Over || !l.PerSource { t.Errorf("parseLimit: %+v %v", l, err) } if _, err := parseLimit("fast"); err == nil { t.Error("a bad limit accepted") } for _, c := range []struct { m model.Match chain string want string }{ {model.Match{InZones: []string{"wan"}, NotSources: true, Sources: []string{"trusted"}, Services: []string{"ssh", "tcp/8080"}}, "input", "wan → firewall from any but trusted ssh,tcp/8080"}, {model.Match{InZones: []string{"wan"}, NotIn: true, Destinations: []string{"trusted_ips"}}, "forward", "any but wan → any to trusted_ips"}, {model.Match{InZones: []string{"lan"}, OutZones: []string{"wan"}}, "forward", "lan → wan"}, {model.Match{OutZones: []string{"wan"}}, "output", "firewall → wan"}, {model.Match{OutZones: []string{"wan"}, Sources: []string{"10.0.0.0/8"}}, "", "any → wan from 10.0.0.0/8"}, {model.Match{Sources: []string{"trusted"}}, "input", "from trusted"}, {model.Match{}, "forward", "everything"}, } { if got := describe(c.m, c.chain); got != c.want { t.Errorf("describe(%+v, %s) = %q, want %q", c.m, c.chain, got, c.want) } } if got := editList([]string{"a", "b", "c"}, []string{"d"}, []string{"b"}); len(got) != 3 || got[1] != "c" || got[2] != "d" { t.Errorf("editList: %v", got) } } // TestMatrixAligned: the grid's columns line up in colour too (the // escape codes take no room on screen). func TestMatrixAligned(t *testing.T) { st := model.NewState() st.Zones = []*model.Zone{{ID: 1, Name: "lan", Interfaces: []string{"eth1"}}, {ID: 2, Name: "wan", Interfaces: []string{"wan"}, External: true}} st.Rules = []*model.Rule{{ID: 3, Chain: model.ChainForward, Position: 1, Action: model.ActionAccept, Match: model.Match{InZones: []string{"lan"}, Services: []string{"https"}}}} st.Settings.InputPolicy, st.Settings.ForwardPolicy = model.PolicyDrop, model.PolicyDrop var b strings.Builder writeMatrix(&b, matrix.Build(st), true) esc := regexp.MustCompile("\x1b\\[[0-9]+m") lines := strings.Split(esc.ReplaceAllString(b.String(), ""), "\n") col := strings.Index(lines[0], "wan") for _, l := range lines[1:4] { if r := []rune(l); len(r) <= col || r[col-1] != ' ' || r[col] == ' ' { t.Errorf("misaligned at column %d:\n%s", col, strings.Join(lines[:4], "\n")) break } } if !strings.Contains(b.String(), "\x1b[33mpartly +1") || !strings.Contains(b.String(), "lan → wan\thttps") { t.Errorf("no partly cell, or no list of what's open:\n%s", b.String()) } }