import fs from "node:fs"; import path from "node:path"; function assert(condition: unknown, message: string) { if (!condition) throw new Error(message); } const EXPECTED_RECEIVER = "0x351a1bca5f49dd0046a7cf0bafa7e12fa6441c3a"; export function runTests() { const projectRoot = process.cwd(); const receiverModulePath = path.join(projectRoot, "lib", "payment-receiver.ts"); const backendAuthPath = path.join(projectRoot, "lib", "backend-auth.ts"); const middlewarePath = path.join(projectRoot, "middleware.ts"); const accountCenterPath = path.join( projectRoot, "components", "account", "AccountCenter.tsx", ); const paymentRoutes = [ "app/api/payments/wallets/challenge/route.ts", "app/api/payments/wallets/verify/route.ts", "app/api/payments/wallets/route.ts", "app/api/payments/intents/route.ts", "app/api/payments/intents/[intentId]/submit/route.ts", "app/api/payments/intents/[intentId]/confirm/route.ts", "app/api/payments/intents/[intentId]/validate/route.ts", "app/api/payments/reconcile-latest/route.ts", ]; assert( fs.existsSync(receiverModulePath), "payment receiver guard module must exist", ); const receiverSource = fs.readFileSync(receiverModulePath, "utf8"); assert( receiverSource.includes(EXPECTED_RECEIVER), "payment receiver guard must pin the production receiver address", ); assert( receiverSource.includes("assertExpectedPaymentReceiver"), "payment receiver guard must expose an assertion helper", ); const accountCenterSource = fs.readFileSync(accountCenterPath, "utf8"); const hookPath = path.join( projectRoot, "components", "account", "useAccountPayment.ts", ); const hookSource = fs.existsSync(hookPath) ? fs.readFileSync(hookPath, "utf8") : ""; const paymentFlowPath = path.join( projectRoot, "components", "account", "usePaymentFlow.ts", ); const paymentFlowSource = fs.existsSync(paymentFlowPath) ? fs.readFileSync(paymentFlowPath, "utf8") : ""; // The receiver validation now lives in the extracted hook file (called // from createManualPaymentIntent and createIntentAndPay). assert( accountCenterSource.includes("assertExpectedPaymentReceiver") || hookSource.includes("assertExpectedPaymentReceiver") || paymentFlowSource.includes("assertExpectedPaymentReceiver"), "AccountCenter must validate backend-returned manual payment receiver before displaying it", ); assert( /!\(\s*txValidation\.checked\s*&&\s*txValidation\.valid === true\s*\)/.test( accountCenterSource, ), "manual payment submit button must require checked && valid === true", ); assert( paymentFlowSource.includes("validateTxHash") && paymentFlowSource.includes("/validate"), "manual payment flow must validate tx hashes with the backend before submission", ); assert( paymentFlowSource.includes("await waitForReceipt(txHashNorm, eth)") && paymentFlowSource.indexOf("await waitForReceipt(txHashNorm, eth)") < paymentFlowSource.indexOf("const submitRes = await fetch(`/api/payments/intents/${intentId}/submit`"), "wallet payment flow must wait for the payment tx receipt before submitting tx hash to the backend", ); assert( accountCenterSource.includes("EXPECTED_PAYMENT_RECEIVER_ADDRESS") || hookSource.includes("EXPECTED_PAYMENT_RECEIVER_ADDRESS") || paymentFlowSource.includes("EXPECTED_PAYMENT_RECEIVER_ADDRESS"), "AccountCenter must show the pinned payment receiver address in its payment guard", ); const backendAuthSource = fs.readFileSync(backendAuthPath, "utf8"); assert( backendAuthSource.includes("requireBackendAuthUser"), "backend auth helper must expose a real-user requirement for payment mutations", ); assert( backendAuthSource.includes("requireBackendPaymentAuth") && backendAuthSource.includes("hasBearerAuth"), "backend auth helper must allow bearer-backed payment mutations to reach the backend verifier", ); const middlewareSource = fs.readFileSync(middlewarePath, "utf8"); assert( !middlewareSource.includes("/^bearer\\s+\\S+/i.test(authHeader)") && !middlewareSource.includes("return NextResponse.next();\n }\n }\n\n const response = NextResponse.next"), "middleware must not treat the mere presence of a bearer token as authenticated", ); assert( middlewareSource.includes("function hasSupabaseSessionCookie") && middlewareSource.includes("request.cookies.getAll()") && middlewareSource.includes("hasSupabaseSessionCookieValues"), "middleware must detect non-empty Supabase session cookies locally via the shared helper before refreshing auth", ); assert( middlewareSource.includes("redirectToLogin(request, pathname)") && middlewareSource.indexOf("if (!hasSupabaseSessionCookie(request))") < middlewareSource.indexOf("await refreshMiddlewareSession(request)"), "middleware must redirect no-cookie page requests without calling Supabase auth", ); const terminalGateSource = middlewareSource.slice( middlewareSource.indexOf("async function handleTerminalGate"), middlewareSource.indexOf("async function handleSupabaseAuthGate"), ); assert( terminalGateSource.includes("return response;") && !terminalGateSource.includes("return redirectToLogin(request, pathname);\n}"), "terminal middleware must not navigate long-lived dashboards away when a session cookie exists but claims refresh is transiently unavailable", ); assert( middlewareSource.includes("unauthorizedSupabaseSessionResponse()"), "middleware must reject no-cookie protected API requests without calling Supabase auth", ); const authMeRouteSource = fs.readFileSync( path.join(projectRoot, "app", "api", "auth", "me", "route.ts"), "utf8", ); assert( authMeRouteSource.includes("/auth/v1/user") && authMeRouteSource.includes("getVerifiedBearerIdentity") && authMeRouteSource.includes("degraded_auth_profile: true"), "/api/auth/me must verify bearer tokens directly and return a degraded authenticated profile when the backend auth profile is transiently unavailable", ); assert( authMeRouteSource.includes("const identity = await getBearerIdentityOnce();") && !authMeRouteSource.includes("return buildProxyExceptionResponse(error"), "/api/auth/me must try bearer identity fallback before returning a proxy exception", ); assert( authMeRouteSource.includes("exception_snapshot") && authMeRouteSource.includes("unauthenticatedAuthProfileResponse") && !authMeRouteSource.includes("return buildProxyExceptionResponse(error"), "/api/auth/me must serve a snapshot or anonymous auth profile before surfacing proxy exceptions to long-lived clients", ); for (const route of [ "app/api/ops/analytics/funnel/route.ts", "app/api/ops/config/route.ts", "app/api/ops/health-check/route.ts", "app/api/ops/leaderboard/weekly/route.ts", "app/api/ops/memberships/route.ts", "app/api/ops/memberships/growth/route.ts", "app/api/ops/memberships/overview/route.ts", "app/api/ops/online-users/route.ts", "app/api/ops/payments/incidents/route.ts", "app/api/ops/payments/incidents/[eventId]/resolve/route.ts", "app/api/ops/subscriptions/extend/route.ts", "app/api/ops/subscriptions/grant/route.ts", "app/api/ops/telegram/members-audit/route.ts", "app/api/ops/training/accuracy/route.ts", "app/api/ops/truth-history/route.ts", "app/api/ops/users/route.ts", "app/api/ops/users/grant-points/route.ts", "app/api/ops/view-logs/route.ts", ]) { const routeSource = fs.readFileSync(path.join(projectRoot, route), "utf8"); assert( routeSource.includes("requireOpsProxyAuth(req, auth)") && routeSource.indexOf("requireOpsProxyAuth(req, auth)") > routeSource.indexOf("buildBackendRequestHeaders(req"), `${route} must reject requests without Supabase identity before forwarding the backend entitlement token`, ); } assert( middlewareSource.includes('pathname === "/api/payments/config"'), "middleware must treat public payment config as public API so cached config requests do not refresh Supabase sessions", ); const optionalRefreshFunction = middlewareSource.slice( middlewareSource.indexOf("function shouldRefreshOptionalSupabaseSession"), middlewareSource.indexOf("function hasSupabaseSessionCookie"), ); assert( !optionalRefreshFunction.includes('pathname.startsWith("/api/ops/")') && !optionalRefreshFunction.includes('pathname.startsWith("/api/payments/")'), "optional Supabase middleware refresh must not pre-read sessions for API routes that already build backend auth headers", ); const optionalRefreshIndex = middlewareSource.indexOf( "function shouldRefreshOptionalSupabaseSession", ); const systemStatusPublicIndex = middlewareSource.indexOf( 'pathname === "/api/system/status"', ); assert( systemStatusPublicIndex >= 0 && optionalRefreshIndex >= 0 && systemStatusPublicIndex < optionalRefreshIndex, "middleware must treat public system status as public API instead of optional Supabase session refresh", ); for (const route of paymentRoutes) { const routeSource = fs.readFileSync(path.join(projectRoot, route), "utf8"); assert( routeSource.includes("requireBackendPaymentAuth") && !routeSource.includes("requireBackendAuthUser(auth)"), `${route} must allow bearer-backed payment mutations while still rejecting requests with no auth context`, ); } }