diff --git a/apps/platformnotifications/src/platformnotifications.app.ts b/apps/platformnotifications/src/platformnotifications.app.ts index eb2f2c4..a707445 100644 --- a/apps/platformnotifications/src/platformnotifications.app.ts +++ b/apps/platformnotifications/src/platformnotifications.app.ts @@ -21,9 +21,9 @@ function unauthorized(c: Context) { } /** - * The notification categories `GET /config/categories` serves — a STUB standing in for the - * real list until something here actually defines categories and stores preferences against - * them. + * The notification categories `GET /config/categories` serves as its `Results` — a STUB + * standing in for the real list until something here actually defines categories and stores + * preferences against them. * * `CategoryId` is the client's own id for the category, `Importance` its ranking (0 being * the lowest observed), and `IsMuteable` whether the player may switch it off at all. The @@ -74,9 +74,15 @@ const app = new Hono() }) // The notification categories a player can be shown toggles for — the "what may we notify - // you about" list. A bare array of PascalCase categories, no envelope. No auth: the list - // is server-side config, the same for every player, and a caller's own preferences are - // the per-account routes above. + // you about" list. A `{ Results, TotalResults }` PAGE of PascalCase categories, the same + // envelope the other paged reads on this server use — not the bare array this once + // served. No auth: the list is server-side config, the same for every player, and a + // caller's own preferences are the per-account routes above. + // + // `TotalResults` counts the whole list rather than the page, and there is only ever one + // page here — nothing pages a list this short — so it is the array's own length. Counting + // it rather than writing the number keeps the two from disagreeing when a category is + // added. // // STUB. Nothing here defines categories or stores a preference against one, so this is // one hand-written entry standing in for the real list — the toggle it draws does @@ -84,7 +90,10 @@ const app = new Hono() // the field the client renders, so the stub is visible in-game instead of looking like // a real (and broken) setting. `Name` is left clean in case the client keys off it. .get('/config/categories', async (c) => { - return c.json(NOTIFICATION_CATEGORIES) + return c.json({ + Results: NOTIFICATION_CATEGORIES, + TotalResults: NOTIFICATION_CATEGORIES.length, + }) }) // The caller's CRM configuration — the campaign/messaging settings the client fetches on diff --git a/apps/platformnotifications/src/test/integration/api.test.ts b/apps/platformnotifications/src/test/integration/api.test.ts index 25b22e9..b6b33b5 100644 --- a/apps/platformnotifications/src/test/integration/api.test.ts +++ b/apps/platformnotifications/src/test/integration/api.test.ts @@ -75,27 +75,33 @@ it('401s the gameplay-invites check without a bearer token', async () => { }) it('serves the stub notification categories', async () => { - // A bare array of PascalCase categories, and no auth — the list is server-side config - // rather than anything per-player. + // A `{ Results, TotalResults }` page of PascalCase categories — not a bare array — and no + // auth: the list is server-side config rather than anything per-player. const res = await SELF.fetch(`${ORIGIN}/config/categories`) expect(res.status).toBe(200) - const categories = (await res.json()) as Array<{ - CategoryId: number - Importance: number - Name: string - Description: string - IsMuteable: boolean - }> - expect(categories).toHaveLength(1) - expect(categories[0]).toMatchObject({ + const page = (await res.json()) as { + Results: Array<{ + CategoryId: number + Importance: number + Name: string + Description: string + IsMuteable: boolean + }> + TotalResults: number + } + expect(page.Results).toHaveLength(1) + expect(page.Results[0]).toMatchObject({ CategoryId: 2, Importance: 0, Name: 'Friends', IsMuteable: true, }) + // The total counts the whole list, and this list is served in one page — so it has to + // agree with what came back rather than being a number of its own. + expect(page.TotalResults).toBe(page.Results.length) // The stub marker is in the DISPLAYED text, so a category that does nothing says so // in-game rather than looking like a real setting. Keep it there while this is a stub. - expect(categories[0].Description).toContain('STUB') + expect(page.Results[0].Description).toContain('STUB') }) it('serves the caller’s notification preferences', async () => {