package server import ( "context" "net/http" "net/url" "strings" "testing" "time" "gitea.lab.audasmedia.com.au/sam/email_agent/internal/store" ) func seedUnsubMessage(t *testing.T, srv *Server) int64 { t.Helper() id, err := srv.store.UpsertMessage(context.Background(), &store.Message{ GmailID: "unsub-1", FromAddr: "Heritage Fruit Trees ", Subject: "New workshops released", BodyText: "Hello, you have received a newsletter. Unsubscribe at https://heritagefruittrees.com.au/u?id=1.", SourceAddress: "samuelrolfe@gmail.com", ListUnsubscribe: ", ", DateSent: time.Now().UTC(), }) if err != nil { t.Fatalf("seed message: %v", err) } return id } func TestUnsubscribePageDetectsLinks(t *testing.T) { srv := newTestServer(t) seedUnsubMessage(t, srv) session, _, _ := login(t, srv) rec := getWithSession(t, srv, "/unsubscribe", session) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200", rec.Code) } body := rec.Body.String() for _, want := range []string{ "Detected links", "heritagefruittrees.com.au", "https://heritagefruittrees.com.au/u?id=1", "mailto:unsub@heritagefruittrees.com.au", "Ignore domain", "Mark for unsubscribe", "Ignore list", } { if !strings.Contains(body, want) { t.Errorf("unsubscribe page missing %q", want) } } } func TestUnsubscribeIgnoreAndUnignore(t *testing.T) { srv := newTestServer(t) seedUnsubMessage(t, srv) session, csrfCookie, csrf := login(t, srv) ctx := context.Background() rec := postForm(t, srv, "/unsubscribe/ignore", session, csrfCookie, url.Values{ "csrf": {csrf}, "domain": {"heritagefruittrees.com.au"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("ignore status = %d, want 303 (body %s)", rec.Code, rec.Body.String()) } rec2, ok, err := srv.store.UnsubscribeByDomain(ctx, "heritagefruittrees.com.au") if err != nil || !ok { t.Fatalf("UnsubscribeByDomain() = (_, %v, %v), want a hit", ok, err) } if rec2.Status != store.UnsubscribeStatusIgnored { t.Errorf("status = %q, want ignored", rec2.Status) } body := getWithSession(t, srv, "/unsubscribe", session).Body.String() if !strings.Contains(body, "ignored") || !strings.Contains(body, "Un-ignore") { t.Errorf("ignore list did not render the ignored domain") } rec = postForm(t, srv, "/unsubscribe/unignore", session, csrfCookie, url.Values{ "csrf": {csrf}, "domain": {"heritagefruittrees.com.au"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("unignore status = %d, want 303", rec.Code) } if _, ok, _ := srv.store.UnsubscribeByDomain(ctx, "heritagefruittrees.com.au"); ok { t.Error("domain still on the list after unignore") } } func TestUnsubscribeMarkRecordsWithoutFollowingLink(t *testing.T) { srv := newTestServer(t) seedUnsubMessage(t, srv) session, csrfCookie, csrf := login(t, srv) ctx := context.Background() // Without confirmation the handler refuses and records nothing. rec := postForm(t, srv, "/unsubscribe/mark", session, csrfCookie, url.Values{ "csrf": {csrf}, "domain": {"heritagefruittrees.com.au"}, "url": {"https://heritagefruittrees.com.au/u?id=1"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("mark without confirm status = %d, want 303", rec.Code) } if _, ok, _ := srv.store.UnsubscribeByDomain(ctx, "heritagefruittrees.com.au"); ok { t.Error("unconfirmed mark recorded a domain") } rec = postForm(t, srv, "/unsubscribe/mark", session, csrfCookie, url.Values{ "csrf": {csrf}, "domain": {"heritagefruittrees.com.au"}, "url": {"https://heritagefruittrees.com.au/u?id=1"}, "confirm": {"yes"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("mark status = %d, want 303", rec.Code) } rec2, ok, err := srv.store.UnsubscribeByDomain(ctx, "heritagefruittrees.com.au") if err != nil || !ok { t.Fatalf("UnsubscribeByDomain() = (_, %v, %v), want a hit", ok, err) } if rec2.Status != store.UnsubscribeStatusDone { t.Errorf("status = %q, want done", rec2.Status) } if rec2.URL != "https://heritagefruittrees.com.au/u?id=1" { t.Errorf("url = %q, want the recorded link", rec2.URL) } } func TestUnsubscribeMutationsRequireCSRF(t *testing.T) { srv := newTestServer(t) session, _, _ := login(t, srv) for _, path := range []string{"/unsubscribe/ignore", "/unsubscribe/unignore", "/unsubscribe/mark"} { rec := postForm(t, srv, path, session, nil, url.Values{"domain": {"x.example"}}) if rec.Code != http.StatusForbidden { t.Errorf("%s without CSRF status = %d, want 403", path, rec.Code) } } } func TestUnsubscribeMarkRejectsBadDomain(t *testing.T) { srv := newTestServer(t) session, csrfCookie, csrf := login(t, srv) rec := postForm(t, srv, "/unsubscribe/ignore", session, csrfCookie, url.Values{ "csrf": {csrf}, "domain": {"not a domain!"}, }) if rec.Code != http.StatusSeeOther { t.Fatalf("status = %d, want 303", rec.Code) } if _, ok, _ := srv.store.UnsubscribeByDomain(context.Background(), "not a domain!"); ok { t.Error("bad domain was stored") } } func TestMessageDetailShowsUnsubscribeLinks(t *testing.T) { srv := newTestServer(t) id := seedUnsubMessage(t, srv) session, _, _ := login(t, srv) rec := getWithSession(t, srv, "/partials/message?id="+itoa(id), session) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200", rec.Code) } body := rec.Body.String() if !strings.Contains(body, "Unsubscribe") || !strings.Contains(body, "heritagefruittrees.com.au/u?id=1") { t.Errorf("message detail did not show the unsubscribe link") } } func TestCleanDomain(t *testing.T) { cases := map[string]bool{ "brand.example": true, "Sub.Brand.example": true, "x-y.example": true, "": false, "nodot": false, "has space.example": false, "bad!char.example": false, } for in, want := range cases { if _, ok := cleanDomain(in); ok != want { t.Errorf("cleanDomain(%q) ok = %v, want %v", in, ok, want) } } }