test(12-02): add discussion unread coverage
This commit is contained in:
parent
6af4d70d51
commit
e3c8d51782
1 changed files with 158 additions and 0 deletions
|
|
@ -285,3 +285,161 @@ func TestDiscussionRequiresCSRF(t *testing.T) {
|
|||
t.Fatalf("POST discussion without CSRF succeeded; want rejection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTablosListDiscussionUnreadBadge(t *testing.T) {
|
||||
pool, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
q := sqlc.New(pool)
|
||||
store := auth.NewStore(q)
|
||||
router := newDiscussionTestRouter(q, store)
|
||||
|
||||
user := preInsertUser(t, ctx, q, "discussion-unread@example.com", "correct-horse-12")
|
||||
unreadTablo := insertDiscussionTestTablo(t, ctx, q, user, "Unread Tablo")
|
||||
quietTablo := insertDiscussionTestTablo(t, ctx, q, user, "Quiet Tablo")
|
||||
for i := 0; i < 3; i++ {
|
||||
insertDiscussionTestMessage(t, ctx, pool, q, unreadTablo.ID, user.ID, "Unread message", time.Date(2026, 5, 16, 9, i, 0, 0, time.UTC))
|
||||
}
|
||||
sessionCookie := sessionCookieForUser(t, ctx, store, user)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(sessionCookie)
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET / status = %d; want 200", rec.Code)
|
||||
}
|
||||
body := rec.Body.String()
|
||||
if !strings.Contains(body, `aria-label="3 unread discussion messages"`) {
|
||||
t.Fatalf("unread badge accessible label missing; body: %.1200s", body)
|
||||
}
|
||||
unreadCard := discussionTestTabloCardHTML(t, body, unreadTablo.ID)
|
||||
if !strings.Contains(unreadCard, ">3<") {
|
||||
t.Fatalf("unread tablo card missing numeric badge; card: %.800s", unreadCard)
|
||||
}
|
||||
quietCard := discussionTestTabloCardHTML(t, body, quietTablo.ID)
|
||||
if strings.Contains(quietCard, "unread discussion messages") {
|
||||
t.Fatalf("quiet tablo card unexpectedly contains unread badge; card: %.800s", quietCard)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTablosListDiscussionUnreadDoesNotLeakOtherUsers(t *testing.T) {
|
||||
pool, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
q := sqlc.New(pool)
|
||||
store := auth.NewStore(q)
|
||||
router := newDiscussionTestRouter(q, store)
|
||||
|
||||
user := preInsertUser(t, ctx, q, "discussion-unread-owner@example.com", "correct-horse-12")
|
||||
other := preInsertUser(t, ctx, q, "discussion-unread-other@example.com", "correct-horse-12")
|
||||
_ = insertDiscussionTestTablo(t, ctx, q, user, "Owner Quiet Tablo")
|
||||
otherTablo := insertDiscussionTestTablo(t, ctx, q, other, "Other Private Tablo")
|
||||
insertDiscussionTestMessage(t, ctx, pool, q, otherTablo.ID, other.ID, "Other user's message", time.Date(2026, 5, 16, 9, 0, 0, 0, time.UTC))
|
||||
sessionCookie := sessionCookieForUser(t, ctx, store, user)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(sessionCookie)
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET / status = %d; want 200", rec.Code)
|
||||
}
|
||||
body := rec.Body.String()
|
||||
if strings.Contains(body, "Other Private Tablo") || strings.Contains(body, "unread discussion messages") {
|
||||
t.Fatalf("dashboard leaked another user's unread state; body: %.1200s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscussionGetMarksMessagesRead(t *testing.T) {
|
||||
pool, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
q := sqlc.New(pool)
|
||||
store := auth.NewStore(q)
|
||||
router := newDiscussionTestRouter(q, store)
|
||||
|
||||
user := preInsertUser(t, ctx, q, "discussion-read@example.com", "correct-horse-12")
|
||||
tablo := insertDiscussionTestTablo(t, ctx, q, user, "Read State Tablo")
|
||||
insertDiscussionTestMessage(t, ctx, pool, q, tablo.ID, user.ID, "First unread", time.Date(2026, 5, 16, 9, 0, 0, 0, time.UTC))
|
||||
insertDiscussionTestMessage(t, ctx, pool, q, tablo.ID, user.ID, "Second unread", time.Date(2026, 5, 16, 10, 0, 0, 0, time.UTC))
|
||||
sessionCookie := sessionCookieForUser(t, ctx, store, user)
|
||||
|
||||
beforeReq := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
beforeReq.AddCookie(sessionCookie)
|
||||
beforeRec := httptest.NewRecorder()
|
||||
router.ServeHTTP(beforeRec, beforeReq)
|
||||
if !strings.Contains(beforeRec.Body.String(), `aria-label="2 unread discussion messages"`) {
|
||||
t.Fatalf("pre-read dashboard missing unread badge; body: %.1200s", beforeRec.Body.String())
|
||||
}
|
||||
|
||||
readReq := httptest.NewRequest(http.MethodGet, "/tablos/"+tablo.ID.String()+"/discussion", nil)
|
||||
readReq.AddCookie(sessionCookie)
|
||||
readRec := httptest.NewRecorder()
|
||||
router.ServeHTTP(readRec, readReq)
|
||||
if readRec.Code != http.StatusOK {
|
||||
t.Fatalf("GET discussion status = %d; want 200", readRec.Code)
|
||||
}
|
||||
|
||||
afterReq := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
afterReq.AddCookie(sessionCookie)
|
||||
afterRec := httptest.NewRecorder()
|
||||
router.ServeHTTP(afterRec, afterReq)
|
||||
if strings.Contains(afterRec.Body.String(), "unread discussion messages") {
|
||||
t.Fatalf("post-read dashboard still shows unread badge; body: %.1200s", afterRec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscussionPostMarksSenderRead(t *testing.T) {
|
||||
pool, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
q := sqlc.New(pool)
|
||||
store := auth.NewStore(q)
|
||||
router := newDiscussionTestRouter(q, store)
|
||||
|
||||
user := preInsertUser(t, ctx, q, "discussion-sender-read@example.com", "correct-horse-12")
|
||||
tablo := insertDiscussionTestTablo(t, ctx, q, user, "Sender Read Tablo")
|
||||
sessionCookie := sessionCookieForUser(t, ctx, store, user)
|
||||
csrfToken, csrfCookies := getCSRFToken(t, router, "/tablos/"+tablo.ID.String()+"/discussion", []*http.Cookie{sessionCookie})
|
||||
|
||||
form := url.Values{"body": {"Sender-created message"}, "_csrf": {csrfToken}}
|
||||
req := httptest.NewRequest(http.MethodPost, "/tablos/"+tablo.ID.String()+"/discussion/messages", strings.NewReader(form.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("HX-Request", "true")
|
||||
for _, c := range csrfCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("POST discussion message status = %d; want 200; body: %.500s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
dashboardReq := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
dashboardReq.AddCookie(sessionCookie)
|
||||
dashboardRec := httptest.NewRecorder()
|
||||
router.ServeHTTP(dashboardRec, dashboardReq)
|
||||
if strings.Contains(dashboardRec.Body.String(), "unread discussion messages") {
|
||||
t.Fatalf("sender's own message counted unread; body: %.1200s", dashboardRec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func discussionTestTabloCardHTML(t *testing.T, body string, id uuid.UUID) string {
|
||||
t.Helper()
|
||||
start := strings.Index(body, `id="tablo-`+id.String()+`"`)
|
||||
if start == -1 {
|
||||
t.Fatalf("tablo card %s not found; body: %.1200s", id, body)
|
||||
}
|
||||
next := strings.Index(body[start+1:], `id="tablo-`)
|
||||
if next == -1 {
|
||||
return body[start:]
|
||||
}
|
||||
return body[start : start+1+next]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue