diff --git a/.env.example b/.env.example index f4c78b1..1d17208 100644 --- a/.env.example +++ b/.env.example @@ -27,6 +27,13 @@ ADMIN_SECRET="" ADMIN_PASSWORD="" ADMIN_EMAIL="" +# ── Seed / test bypass ────────────────────────────────────────────────────── +# Guards POST /api/admin/seed (loads the official 774 LGAs) and lets test +# scripts create LGAs past the 774-total cap on POST /api/lga/register by +# sending it as an `x-seed-secret` header. Leave unset in production once +# real chairmen are registering — there's no legitimate reason to exceed 774. +SEED_SECRET="" + # ── File uploads (Cloudinary) ─────────────────────────────────────────────── # Create a free account at https://cloudinary.com and copy these from your # dashboard. Used for project images/video and press release attachments — @@ -54,4 +61,3 @@ CONTACT_EMAIL="" # ── Misc ───────────────────────────────────────────────────────────────────── NEXT_PUBLIC_APP_URL="http://localhost:3000" OPENAI_API_KEY="" -SEED_SECRET="" diff --git a/app/(admin)/admin/analytics/page.tsx b/app/(admin)/admin/analytics/page.tsx index c5b44ad..5d6c40d 100644 --- a/app/(admin)/admin/analytics/page.tsx +++ b/app/(admin)/admin/analytics/page.tsx @@ -43,24 +43,24 @@ function StatCard({ icon: Icon, label, value, sub, color = "text-green-600" }: { sub?: string; color?: string; }) { return ( -
+
-
+
-

{label}

+

{label}

-

{value}

- {sub &&

{sub}

} +

{value}

+ {sub &&

{sub}

}
); } function nairaK(koboStr: string) { const naira = (koboStr && koboStr !== "0" ? Number(BigInt(koboStr)) : 0) / 100; - if (naira >= 1_000_000) return `₦${(naira / 1_000_000).toFixed(1)}M`; - if (naira >= 1_000) return `₦${(naira / 1_000).toFixed(1)}K`; - return `₦${naira.toFixed(2)}`; + if (naira >= 1_000_000) return `₦${(naira / 1_000_000).toFixed(1)}M`; + if (naira >= 1_000) return `₦${(naira / 1_000).toFixed(1)}K`; + return `₦${naira.toFixed(2)}`; } export default function AdminAnalyticsPage() { @@ -78,14 +78,14 @@ export default function AdminAnalyticsPage() { if (loading) { return (
- {[0,1,2].map((i) =>
)} + {[0,1,2].map((i) =>
)}
); } if (!data) { return ( -
+
Failed to load analytics.
); @@ -102,10 +102,10 @@ export default function AdminAnalyticsPage() {
- -

Platform Analytics

+ +

Platform Analytics

-

Real-time overview of platform activity

+

Real-time overview of platform activity

{/* Key metrics */} @@ -123,22 +123,22 @@ export default function AdminAnalyticsPage() { {/* Charts */}
{/* Monthly revenue bar chart */} -
-

Monthly Revenue (₦)

+
+

Monthly Revenue (₦)

{revenueChartData.length === 0 ? ( -
+
No revenue data yet
) : ( - - - `₦${(Number(v) / 1000).toFixed(0)}K`} /> + + + `₦${(Number(v) / 1000).toFixed(0)}K`} /> [`₦${Number(v).toLocaleString()}`, "Revenue"]} + contentStyle={{ background: "#fff", border: "1px solid #e2e8f0", borderRadius: 12, color: "#0f172a" }} + formatter={(v) => [`₦${Number(v).toLocaleString()}`, "Revenue"]} /> @@ -147,8 +147,8 @@ export default function AdminAnalyticsPage() {
{/* LGA status pie */} -
-

LGA Status Breakdown

+
+

LGA Status Breakdown

[v, name]} /> @@ -167,37 +167,37 @@ export default function AdminAnalyticsPage() {
{/* User role breakdown */} -
-

User Breakdown by Role

+
+

User Breakdown by Role

{userRoleData.map(({ name, value }) => ( -
-

{value.toLocaleString()}

-

{name.replace("_", " ")}

+
+

{value.toLocaleString()}

+

{name.replace("_", " ")}

))}
{/* Top LGAs by posts */} -
-
- -

Top LGAs by Content

+
+
+ +

Top LGAs by Content

{data.topLgasByPosts.length === 0 ? ( -
No data yet
+
No data yet
) : ( -
+
{data.topLgasByPosts.map((lga, idx) => (
- {idx + 1} + {idx + 1}
-

{lga.lgaName} LGA

-

{lga.state} · {lga.status}

+

{lga.lgaName} LGA

+

{lga.state} · {lga.status}

-
- +
+ {lga._count.posts}
diff --git a/app/(admin)/admin/audit-reports/page.tsx b/app/(admin)/admin/audit-reports/page.tsx index 6da7a43..93d53d4 100644 --- a/app/(admin)/admin/audit-reports/page.tsx +++ b/app/(admin)/admin/audit-reports/page.tsx @@ -119,10 +119,10 @@ export default function AdminAuditReportsPage() {
- -

Audit Reports

+ +

Audit Reports

-

{total.toLocaleString()} audit report records

+

{total.toLocaleString()} audit report records

@@ -192,13 +192,13 @@ export default function AdminAuditReportsPage() { {/* Pagination */} {Math.ceil(total / PAGE) > 1 && ( -
-

Showing {page * PAGE + 1}–{Math.min((page + 1) * PAGE, total)} of {total}

+
+

Showing {page * PAGE + 1}–{Math.min((page + 1) * PAGE, total)} of {total}

+ className="px-4 py-2 rounded-xl border border-slate-200 text-slate-600 text-sm disabled:opacity-40 hover:bg-slate-50">← Prev + className="px-4 py-2 rounded-xl border border-slate-200 text-slate-600 text-sm disabled:opacity-40 hover:bg-slate-50">Next →
)} diff --git a/app/(admin)/admin/lgas/page.tsx b/app/(admin)/admin/lgas/page.tsx index f47ac93..2932408 100644 --- a/app/(admin)/admin/lgas/page.tsx +++ b/app/(admin)/admin/lgas/page.tsx @@ -637,7 +637,7 @@ export default function AdminLGAsPage() {
))}
@@ -265,13 +265,13 @@ export default function AdminPressReleasesPage() { {/* Pagination */} {Math.ceil(total / PAGE) > 1 && ( -
-

Showing {page * PAGE + 1}–{Math.min((page + 1) * PAGE, total)} of {total}

+
+

Showing {page * PAGE + 1}–{Math.min((page + 1) * PAGE, total)} of {total}

+ className="px-4 py-2 rounded-xl border border-slate-200 text-slate-600 text-sm disabled:opacity-40 hover:bg-slate-50">← Prev + className="px-4 py-2 rounded-xl border border-slate-200 text-slate-600 text-sm disabled:opacity-40 hover:bg-slate-50">Next →
)} diff --git a/app/(admin)/admin/procurement/page.tsx b/app/(admin)/admin/procurement/page.tsx index 7f7da0e..115cf25 100644 --- a/app/(admin)/admin/procurement/page.tsx +++ b/app/(admin)/admin/procurement/page.tsx @@ -124,10 +124,10 @@ export default function AdminProcurementPage() {
- -

Procurement Contracts

+ +

Procurement Contracts

-

{total.toLocaleString()} contract records

+

{total.toLocaleString()} contract records

@@ -192,13 +192,13 @@ export default function AdminProcurementPage() { {/* Pagination */} {Math.ceil(total / PAGE) > 1 && ( -
-

Showing {page * PAGE + 1}–{Math.min((page + 1) * PAGE, total)} of {total}

+
+

Showing {page * PAGE + 1}–{Math.min((page + 1) * PAGE, total)} of {total}

+ className="px-4 py-2 rounded-xl border border-slate-200 text-slate-600 text-sm disabled:opacity-40 hover:bg-slate-50">← Prev + className="px-4 py-2 rounded-xl border border-slate-200 text-slate-600 text-sm disabled:opacity-40 hover:bg-slate-50">Next →
)} @@ -254,7 +254,7 @@ export default function AdminProcurementPage() { {[ { key: "title", label: "Contract Title *", placeholder: "e.g. Construction of Ward 3 Road" }, { key: "contractor", label: "Contractor Name *", placeholder: "e.g. ABC Construction Ltd" }, - { key: "value", label: "Contract Value (₦) *", placeholder: "e.g. 15000000" }, + { key: "value", label: "Contract Value (₦) *", placeholder: "e.g. 15000000" }, ].map(({ key, label, placeholder }) => (
diff --git a/app/(admin)/admin/revenue/page.tsx b/app/(admin)/admin/revenue/page.tsx index 3337047..07455b3 100644 --- a/app/(admin)/admin/revenue/page.tsx +++ b/app/(admin)/admin/revenue/page.tsx @@ -166,7 +166,7 @@ export default function AdminRevenuePage() { - `₦${(v / 1000).toFixed(0)}k`} /> + `₦${(v / 1000).toFixed(0)}k`} /> new Intl.NumberFormat("en-NG", { diff --git a/app/(admin)/admin/users/page.tsx b/app/(admin)/admin/users/page.tsx index 4cce163..4bc0578 100644 --- a/app/(admin)/admin/users/page.tsx +++ b/app/(admin)/admin/users/page.tsx @@ -132,18 +132,18 @@ export default function AdminUsersPage() {
- -

User Management

+ +

User Management

-

{total.toLocaleString()} total accounts

+

{total.toLocaleString()} total accounts

{/* Role tabs */} -
+
{ROLE_TABS.map(({ label, value }) => ( @@ -157,15 +157,15 @@ export default function AdminUsersPage() { setSearch(e.target.value)} - className="w-full pl-9 pr-4 py-2.5 rounded-xl border border-white/20 bg-white/10 text-white placeholder:text-green-200/40 text-sm focus:outline-none focus:border-green-400" + className="w-full pl-9 pr-4 py-2.5 rounded-xl border border-slate-200 bg-white text-slate-800 placeholder:text-slate-400 text-sm focus:outline-none focus:border-green-400" />
@@ -192,27 +192,29 @@ export default function AdminUsersPage() {
{users.map((user) => (
-
- {/* Avatar */} -
- {user.name.charAt(0).toUpperCase()} -
+
+
+ {/* Avatar */} +
+ {user.name.charAt(0).toUpperCase()} +
-
-
- {user.name} - - {user.isBanned && ( - BANNED - )} - {!user.isBanned && user.suspendedUntil && new Date(user.suspendedUntil) > new Date() && ( - SUSPENDED - )} +
+
+ {user.name} + + {user.isBanned && ( + BANNED + )} + {!user.isBanned && user.suspendedUntil && new Date(user.suspendedUntil) > new Date() && ( + SUSPENDED + )} +
+

{user.email}

-

{user.email}

-
+
{user.isBanned ? (
diff --git a/app/api/lga/register/route.ts b/app/api/lga/register/route.ts index c770a98..e671306 100644 --- a/app/api/lga/register/route.ts +++ b/app/api/lga/register/route.ts @@ -6,6 +6,16 @@ import { lgaSignUpSchema } from "@/lib/validations"; import { generateToken, sanitizeInput } from "@/lib/utils"; import { sendLGAVerificationEmail } from "@/lib/email"; +// Nigeria has exactly 774 Local Government Areas. Once /api/admin/seed has +// loaded the official 774 (see prisma/seeds/nigeria-lgas.ts), every real +// registration matches a seeded row by name+state and goes through the +// "claim" path below (an update, not a create) — so this only ever blocks +// the "fresh create" branch, which is where anything that isn't one of the +// 774 official LGAs would otherwise slip in. Test/seed scripts can bypass +// this with the same secret that guards /api/admin/seed. +const TOTAL_LGA_CAP = 774; +const SEED_SECRET = process.env.SEED_SECRET ?? ""; + export async function POST(request: Request) { // Rate limit: 3 per hour per IP const ip = getClientIP(request); @@ -108,6 +118,20 @@ export async function POST(request: Request) { include: { chairman: { select: { id: true } } }, }); } else { + // No seed record — this would create an LGA that isn't one of + // Nigeria's official 774. Blocked unless the request carries the + // seed-secret test bypass. + const isTestBypass = Boolean(SEED_SECRET) && request.headers.get("x-seed-secret") === SEED_SECRET; + if (!isTestBypass) { + const totalLgas = await db.lGA.count(); + if (totalLgas >= TOTAL_LGA_CAP) { + return NextResponse.json( + { error: "Nigeria has 774 Local Government Areas and that limit has been reached. If your LGA isn't listed, contact support." }, + { status: 409 } + ); + } + } + // No seed record — create fresh lga = await db.lGA.create({ data: { diff --git a/playwright.config.ts b/playwright.config.ts index 35c0ae6..101a65b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,4 +1,9 @@ import { defineConfig, devices } from "@playwright/test"; +import { loadEnvConfig } from "@next/env"; + +// Load .env.local the same way `next dev` does, so tests can read +// SEED_SECRET (and anything else app code reads from process.env). +loadEnvConfig(process.cwd()); export default defineConfig({ testDir: "./tests/e2e", diff --git a/tests/e2e/admin-browser.spec.ts b/tests/e2e/admin-browser.spec.ts index c041571..35bc8ac 100644 --- a/tests/e2e/admin-browser.spec.ts +++ b/tests/e2e/admin-browser.spec.ts @@ -65,7 +65,7 @@ async function seedPendingLGA(request: APIRequestContext, ip: string): Promise<{ const email = `chairman_adm_${suffix}@example.com`; const reg = await request.post("/api/lga/register", { - headers: { "x-forwarded-for": ip }, + headers: { "x-forwarded-for": ip, "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName, state: "Kano", chairmanName: "Alhaji Admin", email, phone: "08011223344", officeAddress: "3 Council Close, Kano", diff --git a/tests/e2e/admin-lga-ward-e2e.spec.ts b/tests/e2e/admin-lga-ward-e2e.spec.ts index d4488d0..e61b210 100644 --- a/tests/e2e/admin-lga-ward-e2e.spec.ts +++ b/tests/e2e/admin-lga-ward-e2e.spec.ts @@ -53,6 +53,7 @@ async function seedApprovedLGA(ip: string): Promise<{ id: string; email: string; const ctx = await apiRequest.newContext({ baseURL: BASE, extraHTTPHeaders: { "x-forwarded-for": ip } }); const reg = await ctx.post("/api/lga/register", { + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName, state, chairmanName: "Chief Record", email, phone: "08012345678", officeAddress: "1 Record Road, Ikeja", sectors: ["Health"], diff --git a/tests/e2e/auth-e2e.spec.ts b/tests/e2e/auth-e2e.spec.ts index 08c487a..4df06c7 100644 --- a/tests/e2e/auth-e2e.spec.ts +++ b/tests/e2e/auth-e2e.spec.ts @@ -263,7 +263,10 @@ test.describe("Auth E2E — LGA Chairman", () => { test("registration → 201 with lgaId", async () => { const ctx = await ctxForIp(IP); - const res = await ctx.post("/api/lga/register", { data: registrationPayload }); + const res = await ctx.post("/api/lga/register", { + data: registrationPayload, + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, + }); expect(res.status()).toBe(201); const body = await res.json(); expect(body.success).toBe(true); @@ -309,6 +312,7 @@ test.describe("Auth E2E — LGA Chairman", () => { const ctx = await ctxForIp(IP); const res = await ctx.post("/api/lga/register", { data: { ...registrationPayload, lgaName: `${lgaName} Two` }, + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, }); expect(res.status()).toBe(409); }); diff --git a/tests/e2e/chairman-crud-e2e.spec.ts b/tests/e2e/chairman-crud-e2e.spec.ts index 438f92e..56015b6 100644 --- a/tests/e2e/chairman-crud-e2e.spec.ts +++ b/tests/e2e/chairman-crud-e2e.spec.ts @@ -64,6 +64,7 @@ async function authedLGA(ip: string): Promise<{ ctx: APIRequestContext; lgaId: s const ctx = await ctxForIp(ip); const reg = await ctx.post("/api/lga/register", { + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName: `Governville ${suffix}`, state: "Lagos", chairmanName: "Chief Govern", email, phone: "08012345678", officeAddress: "1 Council Road, Ikeja", diff --git a/tests/e2e/citizen-lga-e2e.spec.ts b/tests/e2e/citizen-lga-e2e.spec.ts index af256cb..3836fc9 100644 --- a/tests/e2e/citizen-lga-e2e.spec.ts +++ b/tests/e2e/citizen-lga-e2e.spec.ts @@ -120,6 +120,7 @@ async function authedLGA(ip: string): Promise<{ ctx: APIRequestContext; lgaId: s const ctx = await ctxForIp(ip); const reg = await ctx.post("/api/lga/register", { + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName, state: "Lagos", chairmanName: "Chief Portal", email, phone: "08012345678", officeAddress: "1 Council Road, Ikeja", sectors: ["Health", "Education"], diff --git a/tests/e2e/fr01-fr03.spec.ts b/tests/e2e/fr01-fr03.spec.ts index 4a04b9c..8f5d81e 100644 --- a/tests/e2e/fr01-fr03.spec.ts +++ b/tests/e2e/fr01-fr03.spec.ts @@ -10,8 +10,11 @@ */ import { test, expect, request as apiRequest } from "@playwright/test"; +import { Pool } from "pg"; const BASE = "http://localhost:3000"; +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); +test.afterAll(async () => { await pool.end(); }); async function apiPost( url: string, @@ -37,6 +40,10 @@ async function apiGet(url: string, headers: Record = {}) { const ADMIN = { "x-admin-secret": process.env.NEXT_PUBLIC_ADMIN_SECRET ?? "4a0423d4888f73e76fbbb5655ac5458c09be34d5d4eaa9522f943b9cc3d80666" }; +// Bypasses the 774-LGA total cap on POST /api/lga/register (see route.ts) — +// the local test DB accumulates far more than 774 rows across test runs. +const SEED_HEADER = { "x-seed-secret": process.env.SEED_SECRET ?? "" }; + // ─── FR-01-01: Citizen Registration ───────────────────────────────────────── test.describe("FR-01-01: Citizen Registration — API", () => { @@ -272,7 +279,7 @@ test.describe("FR-02-01: LGA Registration — API contracts", () => { password: "Secure@123", confirmPassword: "Secure@123", terms: true, - }); + }, SEED_HEADER); expect([201, 429]).toContain(status); if (status === 201) { expect(body.success).toBe(true); @@ -294,11 +301,11 @@ test.describe("FR-02-01: LGA Registration — API contracts", () => { confirmPassword: "Secure@123", terms: true, }; - await apiPost("/api/lga/register", { ...base, email: `lga1+${ts}@mailinator.com` }); + await apiPost("/api/lga/register", { ...base, email: `lga1+${ts}@mailinator.com` }, SEED_HEADER); const { status } = await apiPost("/api/lga/register", { ...base, email: `lga2+${ts}@mailinator.com`, - }); + }, SEED_HEADER); // 409 on dup, or 429 if rate-limited (3 per hour) expect([409, 429]).toContain(status); }); @@ -314,12 +321,79 @@ test.describe("FR-02-01: LGA Registration — API contracts", () => { password: "Secure@123", confirmPassword: "Secure@123", terms: true, - }); + }, SEED_HEADER); expect([400, 429]).toContain(status); if (status === 400) expect(typeof body.error).toBe("string"); }); }); +// ─── 774-LGA total cap guard ──────────────────────────────────────────────── + +test.describe("774-LGA cap guard on POST /api/lga/register", () => { + test("a fresh LGA create is blocked once the total reaches 774, but the seed-secret bypass still works", async () => { + const { rows } = await pool.query("SELECT count(*)::int AS count FROM lgas"); + test.skip(rows[0].count < 774, "local DB has fewer than 774 LGAs; the cap hasn't kicked in yet"); + + const ctx = await apiRequest.newContext({ + baseURL: BASE, + extraHTTPHeaders: { "x-forwarded-for": `198.30.${Math.floor(Math.random() * 254) + 1}.1` }, + }); + const ts = Date.now(); + const payload = (suffix: string) => ({ + lgaName: `Cap Guard Test ${ts}${suffix}`, + state: "Lagos", + chairmanName: "Cap Guard Tester", + email: `cap-guard-${ts}${suffix}@mailinator.com`, + phone: "08012345678", + officeAddress: "1 Cap Guard Road", + sectors: ["Health"], + password: "Secure@123", + confirmPassword: "Secure@123", + terms: true, + }); + + const blocked = await ctx.post("/api/lga/register", { data: payload("a") }); + expect(blocked.status()).toBe(409); + expect((await blocked.json()).error).toMatch(/774/); + + const bypassed = await ctx.post("/api/lga/register", { + data: payload("b"), + headers: SEED_HEADER, + }); + expect(bypassed.status()).toBe(201); + }); + + test("claiming an already-seeded LGA (no chairman yet) is never blocked by the cap", async () => { + const suffix = `${Date.now()}${Math.floor(Math.random() * 1000)}`; + const lgaName = `Seed Claim Test ${suffix}`; + await pool.query( + `INSERT INTO lgas (id, "lgaName", state, "chairmanName", email, phone, "officeAddress", status, "isVerified", sectors, "createdAt", "updatedAt") + VALUES ($1, $2, 'Ogun', 'Vacant', $3, '08000000000', 'N/A', 'APPROVED', true, ARRAY[]::text[], now(), now())`, + [`captest${suffix}`.slice(0, 25), lgaName, `vacant-${suffix}@lga.gov.ng`] + ); + + const ctx = await apiRequest.newContext({ + baseURL: BASE, + extraHTTPHeaders: { "x-forwarded-for": `198.31.${Math.floor(Math.random() * 254) + 1}.1` }, + }); + const claim = await ctx.post("/api/lga/register", { + data: { + lgaName, + state: "Ogun", + chairmanName: "Real Chairman", + email: `real-chairman-${suffix}@mailinator.com`, + phone: "08012345678", + officeAddress: "1 Real Street", + sectors: ["Health"], + password: "Secure@123", + confirmPassword: "Secure@123", + terms: true, + }, + }); + expect(claim.status()).toBe(201); + }); +}); + // ─── FR-02-03: Admin LGA Approval ─────────────────────────────────────────── test.describe("FR-02-03: Admin Approval — API contracts", () => { diff --git a/tests/e2e/investor-browser.spec.ts b/tests/e2e/investor-browser.spec.ts index 6d5c3f7..2831185 100644 --- a/tests/e2e/investor-browser.spec.ts +++ b/tests/e2e/investor-browser.spec.ts @@ -68,7 +68,7 @@ async function seedApprovedLGAWithEndowment( // 1. Register const reg = await request.post("/api/lga/register", { - headers: { "x-forwarded-for": ip }, + headers: { "x-forwarded-for": ip, "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName, state: "Lagos", chairmanName: "Chief Investor", email, phone: "08012345678", officeAddress: "2 Investment Road, Ikeja", diff --git a/tests/e2e/investor-lga-e2e.spec.ts b/tests/e2e/investor-lga-e2e.spec.ts index b5c71dd..72ec814 100644 --- a/tests/e2e/investor-lga-e2e.spec.ts +++ b/tests/e2e/investor-lga-e2e.spec.ts @@ -69,6 +69,7 @@ async function authedLGA(ip: string): Promise<{ ctx: APIRequestContext; lgaId: s const ctx = await ctxForIp(ip); const reg = await ctx.post("/api/lga/register", { + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName: `Endowville ${suffix}`, state: "Lagos", chairmanName: "Chief Invest", email, phone: "08012345678", officeAddress: "1 Council Road, Ikeja", diff --git a/tests/e2e/lga-dashboard-browser.spec.ts b/tests/e2e/lga-dashboard-browser.spec.ts index 92343db..e97666c 100644 --- a/tests/e2e/lga-dashboard-browser.spec.ts +++ b/tests/e2e/lga-dashboard-browser.spec.ts @@ -147,7 +147,7 @@ async function seedVerifiedLGA( const suffix = uniq(); const email = `chairman_${suffix}@example.com`; const reg = await request.post("/api/lga/register", { - headers: { "x-forwarded-for": ip }, + headers: { "x-forwarded-for": ip, "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName: `Browserville ${suffix}`, state: "Lagos", chairmanName: "Chief Browser", email, phone: "08012345678", officeAddress: "1 Council Road, Ikeja", diff --git a/tests/e2e/lga-portal-auth-e2e.spec.ts b/tests/e2e/lga-portal-auth-e2e.spec.ts index dd0c82e..b2f840d 100644 --- a/tests/e2e/lga-portal-auth-e2e.spec.ts +++ b/tests/e2e/lga-portal-auth-e2e.spec.ts @@ -64,6 +64,7 @@ async function authedLGA(ip: string): Promise<{ ctx: APIRequestContext; lgaId: s const ctx = await ctxForIp(ip); const reg = await ctx.post("/api/lga/register", { + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName: `Portalville ${suffix}`, state: "Lagos", chairmanName: "Chief Portal", email, phone: "08012345678", officeAddress: "1 Council Road, Ikeja", diff --git a/tests/e2e/payments-e2e.spec.ts b/tests/e2e/payments-e2e.spec.ts index 90a8aec..bc41e43 100644 --- a/tests/e2e/payments-e2e.spec.ts +++ b/tests/e2e/payments-e2e.spec.ts @@ -64,6 +64,7 @@ async function authedLGA(ip: string): Promise<{ ctx: APIRequestContext; lgaId: s const c = await apiRequest.newContext({ baseURL: BASE, extraHTTPHeaders: { "x-forwarded-for": ip } }); const reg = await c.post("/api/lga/register", { + headers: { "x-seed-secret": process.env.SEED_SECRET ?? "" }, data: { lgaName: `Payville ${suffix}`, state: "Lagos", chairmanName: "Chief Pay", email, phone: "08012345678", officeAddress: "1 Pay Road, Ikeja",