Return 400 for malformed bulk status JSON#461
Conversation
Greptile SummaryThis PR fixes the
Confidence Score: 4/5Safe to merge — the change is narrow and well-tested; the only callouts are stylistic. The implementation is correct and the regression test exercises exactly the broken path. The two comments are both non-blocking style points: the implicit union guard in the route and the incomplete Supabase mock stub in the test file that would only matter if more test cases are added later. No files require special attention; both changed files are small and self-contained. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant PUT as PUT /api/applications/bulk-status
participant Auth as getAuthContext
participant Parse as parseJsonBody
participant Supabase
Client->>PUT: PUT request (body)
PUT->>Auth: getAuthContext(request)
Auth-->>PUT: "null | { user, supabase }"
alt auth is null
PUT-->>Client: 401 Unauthorized
else auth ok
PUT->>Parse: parseJsonBody(request)
alt malformed JSON
Parse-->>PUT: "{ response: 400 Invalid JSON body }"
PUT-->>Client: 400 Invalid JSON body
else valid JSON
Parse-->>PUT: "{ body: parsed }"
PUT->>PUT: bulkStatusSchema.safeParse(body)
alt schema invalid
PUT-->>Client: 400 Validation error
else schema valid
PUT->>Supabase: SELECT applications WHERE id IN (...)
Supabase-->>PUT: applications[]
PUT->>Supabase: "UPDATE applications SET status=..."
Supabase-->>PUT: updatedApplications[]
PUT->>Supabase: INSERT notifications
PUT-->>Client: "200 { updated, applications }"
end
end
end
Reviews (1): Last reviewed commit: "Handle malformed bulk status JSON" | Re-trigger Greptile |
| if (parsed.response) return parsed.response; | ||
|
|
||
| const body = parsed.body; |
There was a problem hiding this comment.
Implicit discriminated-union access may obscure intent
parsed.response works at runtime because { body: any } never carries a response key, but TypeScript resolves this through an inferred union rather than an explicit discriminant — meaning future changes to parseJsonBody could silently break narrowing without a type error. Using 'response' in parsed as the guard makes the discriminant explicit and is also the idiomatic TypeScript way to narrow this union.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| })); | ||
|
|
||
| const mockFrom = vi.fn(); | ||
|
|
There was a problem hiding this comment.
mockFrom is declared but never given a return value
mockFrom is asserted to not be called in the malformed-JSON test, which is correct. However, vi.fn() returns undefined by default, so any future test case in this file that exercises the happy path will get an immediate crash when the route calls supabase.from(...).select(...). Consider adding a minimal chain stub (e.g., .mockReturnValue({ select: vi.fn(), in: vi.fn(), ... })) in beforeEach so the fixture is ready for follow-on tests.
|
CI is green for PR #461. Verification:
uGig invoice evidence has been sent for this PR. |
Closes #460.
Summary
PUT /api/applications/bulk-statusbodies with a small guarded JSON helper.400 Invalid JSON bodyfor malformed request JSON instead of the generic 500.Verification
corepack pnpm vitest run src/app/api/applications/bulk-status/route.test.tscorepack pnpm tsc --noEmit