From 4e1888ae8ea6c408babffd4d6b7b7ccbacf16764 Mon Sep 17 00:00:00 2001 From: Jennie Robinson Faber Date: Mon, 20 Apr 2026 19:25:24 +0100 Subject: [PATCH] fix(events): read allowIndividualEventTickets from series.tickets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The series-pass gate in register.post.js was checking `series.allowIndividualEventTickets` at the top level, but the field lives under `series.tickets.allowIndividualEventTickets` per the Series schema. Top-level access was always undefined, so `!undefined` always fired the pass check — blocking drop-in registration even when an admin enabled `(requiresSeriesTicket=true, allowIndividualEventTickets=true)`. The bug failed closed (overprotective), so no bypass was possible. The existing test mirrored the bug by mocking the field at the top level; updated the three mocks to nest it under `tickets` so the test shape matches the real schema. --- server/api/events/[id]/register.post.js | 2 +- tests/server/api/events/register-series.test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/api/events/[id]/register.post.js b/server/api/events/[id]/register.post.js index 41d7a3a..db13f50 100644 --- a/server/api/events/[id]/register.post.js +++ b/server/api/events/[id]/register.post.js @@ -34,7 +34,7 @@ export default defineEventHandler(async (event) => { statusMessage: "Series referenced by this event could not be found", }); } - if (!series.allowIndividualEventTickets) { + if (!series.tickets?.allowIndividualEventTickets) { const { hasPass } = checkUserSeriesPass(series, body.email); if (!hasPass) { throw createError({ diff --git a/tests/server/api/events/register-series.test.js b/tests/server/api/events/register-series.test.js index 892ef3b..f203d1d 100644 --- a/tests/server/api/events/register-series.test.js +++ b/tests/server/api/events/register-series.test.js @@ -109,7 +109,7 @@ describe('POST /api/events/[id]/register — series-pass enforcement', () => { mockSeriesFindById.mockResolvedValue({ _id: seriesId, slug: 'series-slug', - allowIndividualEventTickets: false, + tickets: { allowIndividualEventTickets: false }, registrations: [] }) mockCheckUserSeriesPass.mockReturnValue({ hasPass: false, registration: null }) @@ -138,7 +138,7 @@ describe('POST /api/events/[id]/register — series-pass enforcement', () => { mockSeriesFindById.mockResolvedValue({ _id: seriesId, slug: 'series-slug', - allowIndividualEventTickets: false, + tickets: { allowIndividualEventTickets: false }, registrations: [] }) mockCheckUserSeriesPass.mockReturnValue({ @@ -166,7 +166,7 @@ describe('POST /api/events/[id]/register — series-pass enforcement', () => { mockSeriesFindById.mockResolvedValue({ _id: seriesId, slug: 'series-dropin', - allowIndividualEventTickets: true, + tickets: { allowIndividualEventTickets: true }, registrations: [] }) mockCheckUserSeriesPass.mockReturnValue({ hasPass: false, registration: null })