From f37cc932c4f22ccb33e0db25870bb507125ab253 Mon Sep 17 00:00:00 2001 From: poslop Date: Wed, 9 Sep 2026 18:28:00 -0500 Subject: [PATCH] store: burn_after_reads>0 implies burn-after-read mode (#82) --- internal/api/burnreads_test.go | 27 +++++++++++++++++++++++++++ internal/store/store.go | 8 ++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/internal/api/burnreads_test.go b/internal/api/burnreads_test.go index bd74e69..07f578b 100644 --- a/internal/api/burnreads_test.go +++ b/internal/api/burnreads_test.go @@ -46,6 +46,33 @@ func getWithCookie(t *testing.T, h anyHandler, id, viewer string) *httptest.Resp return rec } +// #82: burn_after_reads > 0 alone must enable burn-after-read +// even without burn_after_read: true. +func TestBurnReadsImpliedByBurnAfterReads(t *testing.T) { + s := testServer(t) + h := s.routes() + req := httptest.NewRequest("POST", "/api/pastes", strings.NewReader(`{"content":"implied","burn_after_reads":2}`)) + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != 201 { + t.Fatalf("create burn_after_reads-only: %d %s", rec.Code, rec.Body.String()) + } + var created struct { + ID string `json:"id"` + } + json.Unmarshal(rec.Body.Bytes(), &created) + + if rec := getWithCookie(t, h, created.ID, "aaa"); rec.Code != 200 { + t.Fatalf("read 1: %d", rec.Code) + } + if rec := getWithCookie(t, h, created.ID, "bbb"); rec.Code != 200 { + t.Fatalf("read 2: %d", rec.Code) + } + if rec := getWithCookie(t, h, created.ID, "ccc"); rec.Code != 404 { + t.Fatalf("read 3 expected 404 (burned), got %d", rec.Code) + } +} + func TestBurnAfterNReadsDistinctViewers(t *testing.T) { s := testServer(t) h := s.routes() diff --git a/internal/store/store.go b/internal/store/store.go index e836e04..8755207 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -211,13 +211,17 @@ func (s *Store) CreatePaste(p *Paste) (*Paste, error) { } } - // #49: burn-after-read pastes carry a read budget (default 1 read) - if p.BurnAfterRead { + // #49/#82: burn-after-read pastes carry a read budget (default 1 read). + // burn_after_reads > 0 alone implies burn mode even without burn_after_read. + if p.BurnAfterRead || (p.BurnAfterReads != nil && *p.BurnAfterReads > 0) { limit := int64(1) if p.BurnAfterReads != nil && *p.BurnAfterReads > 0 { limit = int64(*p.BurnAfterReads) } p.readsLimit = &limit + if !p.BurnAfterRead { + p.BurnAfterRead = true + } } visibility := p.Visibility