feat: complete MVP foundation auth and dashboard
This commit is contained in:
51
tests/dashboard-model.test.ts
Normal file
51
tests/dashboard-model.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
dashboardKpis,
|
||||
dashboardNavigation,
|
||||
pipelineStages,
|
||||
reviewQueue,
|
||||
} from "../lib/dashboard-model";
|
||||
|
||||
type NavigationItem = { label: string; href: string };
|
||||
type PipelineStage = { title: string; count: number };
|
||||
type ReviewQueueItem = { title: string };
|
||||
|
||||
test("dashboardNavigation contains the expected sidebar routes in order", () => {
|
||||
assert.deepEqual(
|
||||
dashboardNavigation.map((item: NavigationItem) => [item.label, item.href]),
|
||||
[
|
||||
["Dashboard", "/dashboard"],
|
||||
["Campaigns", "/dashboard/campaigns"],
|
||||
["Leads", "/dashboard/leads"],
|
||||
["Audits", "/dashboard/audits"],
|
||||
["Outreach", "/dashboard/outreach"],
|
||||
["Analytics", "/dashboard/analytics"],
|
||||
["Blacklist", "/dashboard/blacklist"],
|
||||
["Settings", "/dashboard/settings"],
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test("pipelineStages keep the first-screen workflow focused on pipeline overview", () => {
|
||||
assert.deepEqual(
|
||||
pipelineStages.map((stage: PipelineStage) => stage.title),
|
||||
["Kampagnen", "Lead-Recherche", "Audit-Freigabe", "Outreach"],
|
||||
);
|
||||
assert.equal(
|
||||
pipelineStages.every((stage: PipelineStage) => stage.count >= 0),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("dashboardKpis and reviewQueue expose the above-the-fold dashboard summary", () => {
|
||||
assert.equal(dashboardKpis.length, 4);
|
||||
assert.equal(reviewQueue.length, 3);
|
||||
assert.equal(
|
||||
reviewQueue.some((item: ReviewQueueItem) =>
|
||||
item.title.includes("Audit-Freigabe"),
|
||||
),
|
||||
true,
|
||||
);
|
||||
});
|
||||
71
tests/mock-auth.test.ts
Normal file
71
tests/mock-auth.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
MOCK_SESSION_COOKIE_NAME,
|
||||
MOCK_SESSION_COOKIE_VALUE,
|
||||
createClearedMockSessionCookie,
|
||||
createMockSessionCookie,
|
||||
getMockSession,
|
||||
hasMockSession,
|
||||
} from "../lib/mock-auth";
|
||||
|
||||
type CookieLookupName = string;
|
||||
|
||||
test("hasMockSession returns true only for the expected mock session cookie", () => {
|
||||
assert.equal(
|
||||
hasMockSession({
|
||||
get: (name: CookieLookupName) =>
|
||||
name === MOCK_SESSION_COOKIE_NAME
|
||||
? { name, value: MOCK_SESSION_COOKIE_VALUE }
|
||||
: undefined,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
hasMockSession({
|
||||
get: () => ({ name: MOCK_SESSION_COOKIE_NAME, value: "wrong" }),
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
assert.equal(hasMockSession({ get: () => undefined }), false);
|
||||
});
|
||||
|
||||
test("createMockSessionCookie creates an http-only lax root cookie", () => {
|
||||
assert.deepEqual(createMockSessionCookie(), {
|
||||
name: MOCK_SESSION_COOKIE_NAME,
|
||||
value: MOCK_SESSION_COOKIE_VALUE,
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
secure: true,
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
});
|
||||
});
|
||||
|
||||
test("getMockSession returns the simulated admin user only when the cookie is valid", () => {
|
||||
const validStore = {
|
||||
get: (name: CookieLookupName) =>
|
||||
name === MOCK_SESSION_COOKIE_NAME
|
||||
? { name, value: MOCK_SESSION_COOKIE_VALUE }
|
||||
: undefined,
|
||||
};
|
||||
|
||||
assert.deepEqual(getMockSession(validStore), {
|
||||
name: "Matthias Meister",
|
||||
email: "matthias@webdev-pipeline.local",
|
||||
});
|
||||
|
||||
assert.equal(getMockSession({ get: () => undefined }), null);
|
||||
});
|
||||
|
||||
test("createClearedMockSessionCookie expires the mock session at the root path", () => {
|
||||
assert.deepEqual(createClearedMockSessionCookie(), {
|
||||
name: MOCK_SESSION_COOKIE_NAME,
|
||||
value: "",
|
||||
path: "/",
|
||||
maxAge: 0,
|
||||
});
|
||||
});
|
||||
26
tests/proxy-auth.test.ts
Normal file
26
tests/proxy-auth.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { shouldRedirectDashboardRequest } from "../lib/proxy-auth";
|
||||
|
||||
test("shouldRedirectDashboardRequest protects dashboard paths without a valid mock cookie", () => {
|
||||
assert.equal(
|
||||
shouldRedirectDashboardRequest("/dashboard", undefined),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldRedirectDashboardRequest("/dashboard/leads", "wrong"),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("shouldRedirectDashboardRequest allows valid mock sessions and non-dashboard paths", () => {
|
||||
assert.equal(
|
||||
shouldRedirectDashboardRequest(
|
||||
"/dashboard",
|
||||
"mock-admin",
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(shouldRedirectDashboardRequest("/audit/example", undefined), false);
|
||||
});
|
||||
18
tests/route-guards.test.ts
Normal file
18
tests/route-guards.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { getDashboardRedirectPath } from "../lib/route-guards";
|
||||
|
||||
test("getDashboardRedirectPath sends guests back to the auth entry", () => {
|
||||
assert.equal(getDashboardRedirectPath(null), "/");
|
||||
});
|
||||
|
||||
test("getDashboardRedirectPath lets authenticated mock users stay on dashboard", () => {
|
||||
assert.equal(
|
||||
getDashboardRedirectPath({
|
||||
name: "Matthias Meister",
|
||||
email: "matthias@webdev-pipeline.local",
|
||||
}),
|
||||
null,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user