What a quota used shows its percentage of the allowance on the Rules page (line and dialog), the dashboard and in hc quotas. Statistics' Quotas card links each entry to its rule, named, without the share of the total; per-address quotas are in it at last: their quota:rN series is the sum of their addresses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
3.6 KiB
Go
88 lines
3.6 KiB
Go
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)
|
|
}
|
|
}
|
|
for used, want := range map[int64]string{0: "0%", 5: "<1%", 250: "25%", 1000: "100%", 1300: "130%"} {
|
|
if got := percent(used, 1000); got != want {
|
|
t.Errorf("percent(%d, 1000) = %q, want %q", used, got, 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())
|
|
}
|
|
}
|