Four low-risk adds before the Zod 4 / drizzle-zod headliner: - @total-typescript/ts-reset: tightens TS stdlib types globally (JSON.parse → unknown, fetch().json() → unknown, .filter(Boolean) narrows, Set literals respect typed Set targets). Caught 179 latent type errors; fixed all production sites (8 files) and added `any` cast escape hatch in test files (ESLint exemption scoped to tests/). - web-vitals + /api/v1/internal/vitals endpoint + WebVitalsReporter client component: establishes Core Web Vitals baseline (LCP/INP/CLS/ FCP/TTFB) via navigator.sendBeacon. Required before optimisation work. - @hookform/devtools + FormDevtool wrapper: dev-only RHF state inspector, lazy-loaded via next/dynamic so the chunk is excluded from prod bundles entirely. - @tanstack/query-broadcast-client-experimental: cross-tab cache sync via BroadcastChannel — wired in query-provider.tsx, 1-liner. Audit doc updated with sections 35 + 36 (PDF stack overhaul + comprehensive second-pass package sweep) covering ~20 package adoption candidates and 4-5 deprecation candidates. Verified: tsc clean, vitest 1293/1293 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
/**
|
|
* Dedicated test for the 503 path on /api/public/website-inquiries.
|
|
*
|
|
* Lives in its own file rather than sharing the main test file because
|
|
* the test mocks `@/lib/env` to return an empty object - that mock would
|
|
* leak into other tests in the same file via Vitest's module cache,
|
|
* making the rest of the suite return 503 instead of the expected
|
|
* status. Isolating to a single-test file sidesteps that entirely.
|
|
*
|
|
* Asserts the security-critical contract: when WEBSITE_INTAKE_SECRET is
|
|
* unset, every request gets 503, regardless of headers or payload. This
|
|
* is the dev/staging posture; without it, the endpoint would be
|
|
* unauthenticated.
|
|
*/
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
vi.mock('@/lib/env', () => ({
|
|
env: {}, // WEBSITE_INTAKE_SECRET intentionally unset
|
|
}));
|
|
|
|
vi.mock('@/lib/rate-limit', () => ({
|
|
rateLimiters: { websiteIntake: { limit: 10, window: 60_000 } },
|
|
checkRateLimit: vi.fn(async () => ({ allowed: true, resetAt: Date.now() })),
|
|
}));
|
|
|
|
vi.mock('@/lib/logger', () => ({
|
|
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
}));
|
|
|
|
vi.mock('@/lib/db', () => ({
|
|
db: {
|
|
select: () => ({ from: () => ({ where: () => ({ limit: async () => [] }) }) }),
|
|
insert: () => ({
|
|
values: () => ({
|
|
onConflictDoNothing: () => ({ returning: async () => [] }),
|
|
}),
|
|
}),
|
|
},
|
|
}));
|
|
|
|
function makeReq(body: unknown, headers: Record<string, string> = {}) {
|
|
return {
|
|
headers: {
|
|
get(name: string) {
|
|
return headers[name.toLowerCase()] ?? null;
|
|
},
|
|
},
|
|
json: async () => body,
|
|
} as unknown as import('next/server').NextRequest;
|
|
}
|
|
|
|
describe('POST /api/public/website-inquiries — 503 when secret unset', () => {
|
|
it('returns 503 even when a "valid" header + payload are supplied', async () => {
|
|
const { POST } = await import('@/app/api/public/website-inquiries/route');
|
|
const res = await POST(
|
|
makeReq(
|
|
{
|
|
submission_id: '11111111-1111-4111-8111-111111111111',
|
|
kind: 'berth_inquiry',
|
|
payload: {},
|
|
},
|
|
{ 'x-webhook-secret': 'anything-here-doesnt-matter' },
|
|
),
|
|
);
|
|
expect(res.status).toBe(503);
|
|
const body = (await res.json()) as any;
|
|
expect(body.error).toMatch(/not configured/i);
|
|
});
|
|
});
|