Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/__tests__/components/MaintenanceBypass.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { render } from "@testing-library/react";
import MaintenanceBypass from "@/components/MaintenanceBypass";
import { useWallet } from "@/components/WalletContext";
import { BYPASS_COOKIE_MAX_AGE, MAINTENANCE_COOKIE } from "@/lib/maintenanceConfig";

jest.mock("@/components/WalletContext", () => ({
useWallet: jest.fn(),
}));

const mockUseWallet = useWallet as jest.MockedFunction<typeof useWallet>;

function mockWallet(publicKey: string | null) {
mockUseWallet.mockReturnValue({ publicKey } as unknown as ReturnType<typeof useWallet>);
}

// Reproduces the cookie string MaintenanceBypass.tsx builds on the client. Testing this
// pure logic directly (rather than the full env-derived-allowlist component render) keeps
// the test environment simple, matching the existing convention in maintenanceMode.test.ts.
function buildBypassCookie(publicKey: string, allowlist: string[]): string | null {
if (!allowlist.includes(publicKey.toLowerCase())) return null;
return `${MAINTENANCE_COOKIE}=${publicKey.toLowerCase()}; path=/; max-age=${BYPASS_COOKIE_MAX_AGE}; SameSite=Lax`;
}

describe("MaintenanceBypass cookie construction", () => {
it("embeds BYPASS_COOKIE_MAX_AGE (24h) as the cookie's max-age for an allowlisted wallet", () => {
const cookie = buildBypassCookie("GALLOWED1", ["gallowed1"]);
expect(cookie).toBe(`${MAINTENANCE_COOKIE}=gallowed1; path=/; max-age=86400; SameSite=Lax`);
expect(BYPASS_COOKIE_MAX_AGE).toBe(60 * 60 * 24);
});

it("does not build a cookie for a wallet outside the allowlist", () => {
expect(buildBypassCookie("GNOTALLOWED", ["gallowed1"])).toBeNull();
});
});

describe("MaintenanceBypass component", () => {
beforeEach(() => {
jest.clearAllMocks();
document.cookie = `${MAINTENANCE_COOKIE}=; path=/; max-age=0`;
});

it("does not set the cookie when no wallet is connected", () => {
mockWallet(null);

render(<MaintenanceBypass />);

expect(document.cookie).not.toContain(MAINTENANCE_COOKIE);
});

it("renders nothing", () => {
mockWallet(null);
const { container } = render(<MaintenanceBypass />);
expect(container).toBeEmptyDOMElement();
});
});
11 changes: 11 additions & 0 deletions src/__tests__/unit/maintenanceConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MAINTENANCE_COOKIE, BYPASS_COOKIE_MAX_AGE } from "@/lib/maintenanceConfig";

describe("maintenanceConfig", () => {
it("exposes a 24 hour max age, in seconds", () => {
expect(BYPASS_COOKIE_MAX_AGE).toBe(60 * 60 * 24);
});

it("exposes the maintenance bypass cookie name", () => {
expect(MAINTENANCE_COOKIE).toBe("maintenance_bypass");
});
});
2 changes: 1 addition & 1 deletion src/components/MaintenanceBypass.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { useEffect } from "react";
import { useWallet } from "@/components/WalletContext";
import { MAINTENANCE_COOKIE, BYPASS_COOKIE_MAX_AGE } from "@/middleware";
import { MAINTENANCE_COOKIE, BYPASS_COOKIE_MAX_AGE } from "@/lib/maintenanceConfig";

const ALLOWLIST = (process.env.NEXT_PUBLIC_MAINTENANCE_ALLOWLIST ?? "")
.split(",")
Expand Down
7 changes: 7 additions & 0 deletions src/lib/maintenanceConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Shared maintenance-mode constants used by both the edge middleware and the
// client-side bypass component. Kept in a dedicated module (rather than
// exported from middleware.ts) so client components don't pull in
// next-intl/next/server middleware code via the import graph.

export const MAINTENANCE_COOKIE = "maintenance_bypass";
export const BYPASS_COOKIE_MAX_AGE = 60 * 60 * 24; // 24 hours, in seconds
4 changes: 1 addition & 3 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import createIntlMiddleware from "next-intl/middleware";
import { NextRequest, NextResponse } from "next/server";
import { routing } from "./i18n/routing";
import { MAINTENANCE_COOKIE, BYPASS_COOKIE_MAX_AGE } from "./lib/maintenanceConfig";

const intlMiddleware = createIntlMiddleware(routing);

const MAINTENANCE_COOKIE = "maintenance_bypass";
const BYPASS_COOKIE_MAX_AGE = 60 * 60 * 24; // 24 h

function isMaintenanceEnabled(): boolean {
return process.env.NEXT_PUBLIC_MAINTENANCE_MODE === "true";
}
Expand Down