package server import ( "context" "net/http" "net/url" "strings" "testing" ) func TestRulesPageRenders(t *testing.T) { srv := newTestServer(t) session, _, _ := login(t, srv) rec := getWithSession(t, srv, "/rules", session) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200", rec.Code) } body := rec.Body.String() for _, want := range []string{"Rules editor", "Save new version", "Versions", "Back to inbox", "Unsubscribe"} { if !strings.Contains(body, want) { t.Errorf("rules page missing %q", want) } } } func TestRulesSaveCreatesVersion(t *testing.T) { srv := newTestServer(t) session, csrfCookie, csrf := login(t, srv) rule := `from contains "heritagefruittrees.com.au" => category=Action, importance=high, action=file` rec := postForm(t, srv, "/rules/save", session, csrfCookie, url.Values{ "csrf": {csrf}, "text": {rule}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("save status = %d, want 303 (body %s)", rec.Code, rec.Body.String()) } text, version, err := srv.store.CurrentRules(context.Background()) if err != nil { t.Fatalf("CurrentRules() error = %v", err) } if text != rule { t.Errorf("CurrentRules() text = %q, want the saved rule", text) } if version != 2 { t.Errorf("CurrentRules() version = %d, want 2", version) } // The editor shows the new version and active rule count. body := getWithSession(t, srv, "/rules", session).Body.String() if !strings.Contains(body, "version 2") || !strings.Contains(body, "1 active rule") { t.Errorf("rules page did not reflect the saved version; body:\n%s", body) } } func TestRulesSaveRequiresCSRF(t *testing.T) { srv := newTestServer(t) session, _, _ := login(t, srv) rec := postForm(t, srv, "/rules/save", session, nil, url.Values{"text": {"anything"}}) if rec.Code != http.StatusForbidden { t.Fatalf("status = %d, want 403", rec.Code) } } func TestRulesPageShowsParseErrors(t *testing.T) { srv := newTestServer(t) session, csrfCookie, csrf := login(t, srv) rec := postForm(t, srv, "/rules/save", session, csrfCookie, url.Values{ "csrf": {csrf}, "text": {"this line has no arrow"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("save status = %d, want 303", rec.Code) } body := getWithSession(t, srv, "/rules", session).Body.String() if !strings.Contains(body, "could not be parsed") || !strings.Contains(body, "missing =>") { t.Errorf("rules page did not surface the parse error; body:\n%s", body) } } func TestRulesRestoreCreatesNewVersion(t *testing.T) { srv := newTestServer(t) session, csrfCookie, csrf := login(t, srv) ctx := context.Background() first := `from contains "a" => category=Info` second := `from contains "b" => category=Action` for _, text := range []string{first, second} { rec := postForm(t, srv, "/rules/save", session, csrfCookie, url.Values{"csrf": {csrf}, "text": {text}}) if rec.Code != http.StatusSeeOther { t.Fatalf("save %q status = %d, want 303", text, rec.Code) } } // Restore version 2 (the first saved rule) as a new version. rec := postForm(t, srv, "/rules/restore", session, csrfCookie, url.Values{ "csrf": {csrf}, "version": {"2"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("restore status = %d, want 303", rec.Code) } text, version, err := srv.store.CurrentRules(ctx) if err != nil { t.Fatalf("CurrentRules() error = %v", err) } if text != first { t.Errorf("restored text = %q, want %q", text, first) } if version != 4 { t.Errorf("restored version = %d, want 4 (append-only)", version) } } func TestDashboardLinksRulesAndUnsubscribe(t *testing.T) { srv := newTestServer(t) session, _, _ := login(t, srv) body := getWithSession(t, srv, "/", session).Body.String() for _, want := range []string{`href="/rules"`, `href="/unsubscribe"`} { if !strings.Contains(body, want) { t.Errorf("dashboard missing %q", want) } } } func TestRulesCountsLines(t *testing.T) { text := "# comment\n\nfrom contains \"a\" => category=Info\nfrom contains \"b\" => category=Action" if got := countRuleLines(text); got != 2 { t.Errorf("countRuleLines() = %d, want 2", got) } if got := firstRuleLine(text); got != `from contains "a" => category=Info` { t.Errorf("firstRuleLine() = %q", got) } if got := firstRuleLine("# only a comment"); got != "(comments only)" { t.Errorf("firstRuleLine(comment only) = %q", got) } } func TestRulesReclassifyWithoutJEVRedirects(t *testing.T) { srv := newTestServer(t) session, csrfCookie, csrf := login(t, srv) rec := postForm(t, srv, "/rules/reclassify", session, csrfCookie, url.Values{"csrf": {csrf}}) if rec.Code != http.StatusSeeOther { t.Fatalf("status = %d, want 303 (body %s)", rec.Code, rec.Body.String()) } if loc := rec.Header().Get("Location"); !strings.Contains(loc, "JEV_API_KEY") { t.Errorf("Location = %q, want a JEV-not-configured note", loc) } }