|
1 | 1 | import { render, screen, waitFor } from "@testing-library/react"; |
2 | 2 | import EventsPage from "./page"; |
| 3 | +import { MAX_RENDERED_EVENTS } from "@/lib/events"; |
3 | 4 |
|
4 | 5 | describe("EventsPage", () => { |
5 | 6 | let originalFetch: typeof global.fetch; |
@@ -68,4 +69,76 @@ describe("EventsPage", () => { |
68 | 69 | }); |
69 | 70 | expect(document.querySelectorAll("[aria-live=polite]")).toHaveLength(1); |
70 | 71 | }); |
| 72 | + |
| 73 | + it("drops malformed event records instead of throwing during render", async () => { |
| 74 | + global.fetch = jest.fn().mockResolvedValueOnce({ |
| 75 | + ok: true, |
| 76 | + text: async () => |
| 77 | + JSON.stringify({ |
| 78 | + items: [ |
| 79 | + { id: "evt1", ts: 1_782_460_000_000, type: "pair.registered", payload: {} }, |
| 80 | + { ts: 1_782_460_000_001, type: "missing.id", payload: {} }, |
| 81 | + { id: "evt2", ts: "not-a-number", type: "bad.ts", payload: {} }, |
| 82 | + { id: "evt3", ts: 1_782_460_000_002, payload: {} }, |
| 83 | + { id: "evt4", ts: 1_782_460_000_003, type: "bad.payload" }, |
| 84 | + { id: "evt5", ts: 1_782_460_000_004, type: "string.payload", payload: "nope" }, |
| 85 | + ], |
| 86 | + }), |
| 87 | + } as unknown as Response); |
| 88 | + |
| 89 | + render(<EventsPage />); |
| 90 | + |
| 91 | + expect(await screen.findByText("pair.registered")).toBeInTheDocument(); |
| 92 | + expect(screen.queryByText("missing.id")).not.toBeInTheDocument(); |
| 93 | + expect(screen.queryByText("bad.ts")).not.toBeInTheDocument(); |
| 94 | + expect(screen.queryByText("bad.payload")).not.toBeInTheDocument(); |
| 95 | + expect(screen.queryByText("string.payload")).not.toBeInTheDocument(); |
| 96 | + }); |
| 97 | + |
| 98 | + it("bounds rendered records and surfaces a capped note", async () => { |
| 99 | + const events = Array.from({ length: MAX_RENDERED_EVENTS + 3 }, (_, index) => ({ |
| 100 | + id: `evt${index}`, |
| 101 | + ts: 1_782_460_000_000 + index, |
| 102 | + type: `event.${index}`, |
| 103 | + payload: { index }, |
| 104 | + })); |
| 105 | + global.fetch = jest.fn().mockResolvedValueOnce({ |
| 106 | + ok: true, |
| 107 | + text: async () => JSON.stringify({ items: events }), |
| 108 | + } as unknown as Response); |
| 109 | + |
| 110 | + render(<EventsPage />); |
| 111 | + |
| 112 | + expect( |
| 113 | + await screen.findByText( |
| 114 | + `Showing ${MAX_RENDERED_EVENTS} of ${MAX_RENDERED_EVENTS + 3} events (capped).`, |
| 115 | + ), |
| 116 | + ).toBeInTheDocument(); |
| 117 | + expect(screen.getByText("event.0")).toBeInTheDocument(); |
| 118 | + expect(screen.getByText(`event.${MAX_RENDERED_EVENTS - 1}`)).toBeInTheDocument(); |
| 119 | + expect(screen.queryByText(`event.${MAX_RENDERED_EVENTS}`)).not.toBeInTheDocument(); |
| 120 | + }); |
| 121 | + |
| 122 | + it("truncates oversized payload previews", async () => { |
| 123 | + global.fetch = jest.fn().mockResolvedValueOnce({ |
| 124 | + ok: true, |
| 125 | + text: async () => |
| 126 | + JSON.stringify({ |
| 127 | + items: [ |
| 128 | + { |
| 129 | + id: "evt-large", |
| 130 | + ts: 1_782_460_000_000, |
| 131 | + type: "payload.large", |
| 132 | + payload: { body: "x".repeat(5000) }, |
| 133 | + }, |
| 134 | + ], |
| 135 | + }), |
| 136 | + } as unknown as Response); |
| 137 | + |
| 138 | + render(<EventsPage />); |
| 139 | + |
| 140 | + expect(await screen.findByText("payload.large")).toBeInTheDocument(); |
| 141 | + expect(screen.getByText(/truncated/)).toBeInTheDocument(); |
| 142 | + }); |
| 143 | + |
71 | 144 | }); |
0 commit comments