package server import ( "strings" "testing" udiff "github.com/aymanbagabas/go-udiff" "housecarl/internal/api" "housecarl/internal/model" ) // TestBlockHeaders: each hunk says which chain it is in, even when the // chain's own line is far above it. func TestBlockHeaders(t *testing.T) { var b strings.Builder b.WriteString("table inet housecarl {\n\tset lan_v4 { type ipv4_addr; }\n\tchain input {\n") for i := 0; i < 20; i++ { b.WriteString("\t\tcounter comment \"in\"\n") } b.WriteString("\t}\n\tchain forward {\n") for i := 0; i < 20; i++ { b.WriteString("\t\tcounter comment \"fw\"\n") } b.WriteString("\t}\n}\n") now := b.String() then := strings.Replace(now, "\t\tcounter comment \"in\"\n\t}", "\t\tcounter comment \"in\"\n\t\taccept comment \"new in\"\n\t}", 1) then = strings.Replace(then, "\t\tcounter comment \"fw\"\n\t}", "\t\taccept comment \"new fw\"\n\t}", 1) diff := blockHeaders(udiff.Unified("a", "b", now, then), now, then) var heads []string for _, l := range strings.Split(diff, "\n") { if strings.HasPrefix(l, "@@") { heads = append(heads, l) } } if len(heads) != 2 || !strings.HasSuffix(heads[0], "@@ chain input") || !strings.HasSuffix(heads[1], "@@ chain forward") { t.Fatalf("hunk headers %q, want chain input then chain forward\n%s", heads, diff) } if blockHeaders("", now, then) != "" { t.Fatal("no diff should stay empty") } } // TestHistoryDiff: an entry's diff is its own change, rebuilt from the // current configuration however many entries came after it. func TestHistoryDiff(t *testing.T) { e := newEnv(t) var mr api.MutationResponse e.call(e.token, "POST", "/zones", model.Zone{Name: "lan", Interfaces: []string{"eth1"}}, nil) e.call(e.token, "POST", "/rules", model.Rule{Chain: "input", Match: model.Match{Services: []string{"ssh"}}, Comment: "first"}, &mr) first := mr.HistoryID e.call(e.token, "POST", "/rules", model.Rule{Chain: "forward", Match: model.Match{Services: []string{"http"}}, Comment: "second"}, nil) e.call(e.token, "PATCH", "/settings", map[string]any{"builtins": map[string]any{"drop_invalid": true}}, nil) var out struct{ Diff string } if code := e.call(e.token, "GET", "/history/"+itoa(first)+"/diff", nil, &out); code != 200 { t.Fatalf("diff: %d", code) } var added []string for _, l := range strings.Split(out.Diff, "\n") { if strings.HasPrefix(l, "+") && !strings.HasPrefix(l, "+++") { added = append(added, l) } } if len(added) == 0 || !strings.Contains(strings.Join(added, "\n"), ` first"`) || strings.Contains(out.Diff, ` second"`) || !strings.Contains(out.Diff, "@@ chain input") { t.Errorf("entry %d's diff:\n%s", first, out.Diff) } if code := e.call(e.token, "GET", "/history/9999/diff", nil, nil); code != 404 { t.Errorf("unknown entry: %d", code) } }