87 lines
3.7 KiB
TypeScript
87 lines
3.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { LoaderFunctionArgs } from "react-router";
|
|
|
|
vi.mock("~/components/app/AppLayout", () => ({ default: ({ children }: { children: React.ReactNode }) => children }));
|
|
vi.mock("~/utils/data/.server/adminData", () => ({ loadAdminData: vi.fn() }));
|
|
vi.mock("~/components/ui/errors/ServerError", () => ({ default: () => null }));
|
|
vi.mock("~/modules/metrics/services/.server/MetricTracker", () => ({ createMetrics: vi.fn() }));
|
|
vi.mock("~/locale/i18next.server", () => ({ getTranslations: vi.fn() }));
|
|
vi.mock("@toolctrl/config", () => ({
|
|
loadRequiredWebConfig: () => ({
|
|
routing: {
|
|
appBaseDomain: "example.test",
|
|
webAppHost: "app.example.test",
|
|
adminAppHost: "admin.example.test",
|
|
apiHost: "api.example.test",
|
|
protocol: "https",
|
|
},
|
|
}),
|
|
getDomainRoutingConfig: () => ({
|
|
appBaseDomain: "example.test",
|
|
webAppHost: "app.example.test",
|
|
adminAppHost: "admin.example.test",
|
|
apiHost: "api.example.test",
|
|
protocol: "https",
|
|
}),
|
|
hostMatches: (host: string | undefined, expected: string) => host?.toLowerCase() === expected.toLowerCase(),
|
|
normalizeHost: (host: string | undefined) => host?.toLowerCase() ?? "",
|
|
}));
|
|
vi.mock("~/utils/helpers/.server/PermissionsService", () => ({ verifyUserHasPermission: vi.fn() }));
|
|
vi.mock("~/components/ui/analytics/AnalyticsOverview", () => ({ default: () => null }));
|
|
vi.mock("~/modules/analytics/services/.server/AnalyticsService", () => ({ default: { getOverview: vi.fn() } }));
|
|
|
|
import { createMetrics } from "~/modules/metrics/services/.server/MetricTracker";
|
|
import { loadAdminData } from "~/utils/data/.server/adminData";
|
|
import { adminRootRedirectTarget, isAdminRootPath, loader } from "./admin";
|
|
import { adminAnalyticsRedirectTarget, isAdminAnalyticsRootPath } from "./admin/analytics";
|
|
import { adminMetricsRedirectTarget, isAdminMetricsRootPath } from "./admin/metrics";
|
|
|
|
describe("admin route host-aware redirects", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(createMetrics).mockResolvedValue({
|
|
time: vi.fn(async (_label: string, callback: () => unknown) => callback()),
|
|
getServerTimingHeader: () => ({}),
|
|
} as never);
|
|
vi.mocked(loadAdminData).mockResolvedValue({} as never);
|
|
});
|
|
|
|
it("redirects the admin host root to dashboard without legacy admin prefixes", () => {
|
|
const target = adminRootRedirectTarget();
|
|
|
|
expect(isAdminRootPath("/")).toBe(true);
|
|
expect(isAdminRootPath("/dashboard")).toBe(false);
|
|
expect(target).toBe("/dashboard");
|
|
expect(target).not.toBe("/admin");
|
|
expect(target).not.toBe("/admin/dashboard");
|
|
expect(target).not.toContain("/admin/");
|
|
});
|
|
|
|
it("redirects nested analytics and metrics defaults without legacy admin prefixes", () => {
|
|
expect(isAdminAnalyticsRootPath("/analytics")).toBe(true);
|
|
expect(adminAnalyticsRedirectTarget()).toBe("/analytics/overview");
|
|
expect(adminAnalyticsRedirectTarget()).not.toContain("/admin/");
|
|
|
|
expect(isAdminMetricsRootPath("/metrics")).toBe(true);
|
|
expect(adminMetricsRedirectTarget()).toBe("/metrics/summary");
|
|
expect(adminMetricsRedirectTarget()).not.toContain("/admin/");
|
|
});
|
|
|
|
it("server-redirects admin route clones away from non-admin hosts", async () => {
|
|
try {
|
|
await loader({
|
|
request: new Request("https://app.example.test/dashboard?tab=activity#events", {
|
|
headers: { host: "app.example.test" },
|
|
}),
|
|
params: {},
|
|
context: {},
|
|
unstable_pattern: "admin",
|
|
} as LoaderFunctionArgs);
|
|
} catch (error) {
|
|
const response = error as Response;
|
|
expect(response.headers.get("Location")).toBe("https://admin.example.test/dashboard?tab=activity#events");
|
|
}
|
|
expect(loadAdminData).not.toHaveBeenCalled();
|
|
});
|
|
});
|