Files
pleno-vue/tests/unit/session-token-initialization-contract.spec.js
T

167 lines
6.1 KiB
JavaScript

import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
const root = process.cwd();
const normalizeSource = (source) => source.replace(/\r\n/g, "\n");
const readSource = (relativePath) => normalizeSource(readFileSync(join(root, relativePath), "utf8"));
const sessionUserSource = readSource("src/components/session/token/SessionUser.vue");
const superUserObjectSource = readSource("src/components/session/token/superUserObject.vue");
const extractBlock = (source, startToken, endToken) => {
const startIndex = source.indexOf(startToken);
if (startIndex === -1) {
throw new Error(`Unable to find start token: ${startToken}`);
}
const contentStartIndex = startIndex + startToken.length;
const endIndex = source.indexOf(endToken, contentStartIndex);
if (endIndex === -1) {
throw new Error(`Unable to find end token: ${endToken}`);
}
return source.slice(contentStartIndex, endIndex);
};
const parseDirectBindings = (sourceBlock) => {
const bindings = new Map();
const pattern = /^\s*([A-Za-z0-9_]+)\s*:\s*([A-Za-z0-9_]+)\s*,\s*$/gm;
for (const match of sourceBlock.matchAll(pattern)) {
bindings.set(match[1], match[2]);
}
return bindings;
};
const parseGetterBindings = (sourceBlock) => {
const bindings = new Map();
const pattern = /^\s*get\s+([A-Za-z0-9_]+)\s*\(\)\s*{\s*return\s+([A-Za-z0-9_]+);\s*},?\s*$/gm;
for (const match of sourceBlock.matchAll(pattern)) {
bindings.set(match[1], match[2]);
}
return bindings;
};
const expectedSessionObjectBindings = {
categories: "Categories",
products: "Products",
product_options: "ProductOptions",
departments: "Departments",
department_categories: "DepartmentCategories",
economic_departments: "EconomicDepartments",
economic_products: "EconomicProducts",
roles: "Roles",
permissions: "Permissions",
collectedOrderInvoices: "CollectedOrderInvoices",
department_daily_reports: "DepartmentDailyReports",
goals: "Goals",
notifications: "Notifications",
orders: "Orders",
forms: "Forms",
bookings: "Bookings",
Branding: "Branding",
vehicles: "Vehicles",
department_notification_sms: "DepartmentNotificationSms",
department_time_bookings_opening_hours: "DepartmentTimeBookingsOpeningHours",
department_time_bookings_types: "DepartmentTimeBookingsTypes",
department_time_bookings_entries: "DepartmentTimeBookingsEntries",
department_variables: "DepartmentVariables",
xlvask_vehicle_types: "XLVaskVehicleTypes",
openai: "OpenAI",
department_lanes: "DepartmentLanes",
department_gates: "DepartmentGates",
department_relays: "DepartmentRelays",
order_bookings: "OrderBookings",
module_action_logs: "ModuleActionLogs",
self_serve_questions: "SelfServeQuestions",
self_serve_tasks: "SelfServeTasks",
self_serve_conditions: "SelfServeConditions",
self_serve_vehicle_conditions: "SelfServeVehicleConditions",
self_serve_condition_rules: "SelfServeConditionRules",
self_serve_machine_types: "SelfServeMachineTypes",
subuser_grants: "SubuserGrants",
global: "ObjectsGlobal",
};
const expectedSuperUserModuleBindings = {
economic: "Economic",
reCAPTCHA: "reCAPTCHA",
email: "Email",
backups: "Backups",
motorapi: "MotorAPI",
stripe: "Stripe",
fxratesapi: "FXRatesAPI",
weatherapi: "WeatherAPI",
gatewayapi: "GatewayAPI",
xlvask: "XLVask",
entra: "Entra",
limble: "Limble",
ocrspace: "OcrSpace",
openai: "OpenAI",
licenseplaterecognizer: "LicensePlateRecognizer",
virkdata: "VirkData",
shelly: "Shelly",
selfserve: "SelfServe",
bird: "Bird",
};
const superUserModulesBlock = extractBlock(superUserObjectSource, "modules: {", "\n },\n /** Intimidate a user */");
const superUserSystemBlock = extractBlock(superUserObjectSource, "system: {", "\n },\n modules: {");
const sessionObjectsContentBlock = extractBlock(
sessionUserSource,
"objects: {",
"\n },\n /** The user's token and authentication status */"
);
describe("session token initialization regression contract", () => {
it("keeps department id lookup decoupled from router module initialization", () => {
expect(sessionUserSource).not.toContain('import router from "@/router.js";');
expect(sessionUserSource).not.toContain("router.currentRoute.value.params.departmentId");
expect(sessionUserSource).toMatch(/const path = window\.location\.pathname \|\| ["']["'];/);
expect(sessionUserSource).toContain("const match = path.match(/^\\/admin\\/(\\d+)(?:\\/|$)/);");
expect(sessionUserSource).toContain("const departmentId = Number.parseInt(match[1], 10);");
expect(sessionUserSource).toContain(
"return Number.isInteger(departmentId) && departmentId > 0 ? departmentId : undefined;"
);
});
it("keeps SessionUser.objects eagerly bound for all shared object modules", () => {
const directBindings = parseDirectBindings(sessionObjectsContentBlock);
const getterBindings = parseGetterBindings(sessionObjectsContentBlock);
expect(getterBindings.size).toBe(0);
for (const [key, expectedBinding] of Object.entries(expectedSessionObjectBindings)) {
expect(directBindings.get(key)).toBe(expectedBinding);
}
});
});
describe("superuser module initialization regression contract", () => {
it("keeps superuser system binding lazy to avoid circular-import TDZ errors", () => {
const systemBindings = parseGetterBindings(superUserSystemBlock);
expect(systemBindings.get("database")).toBe("DatabaseSystemObject");
expect(systemBindings.get("redis")).toBe("RedisSystemObject");
expect(systemBindings.get("minio")).toBe("MinioSystemObject");
});
it("keeps all superuser module bindings lazy at object construction time", () => {
const moduleGetterBindings = parseGetterBindings(superUserModulesBlock);
const moduleDirectBindings = parseDirectBindings(superUserModulesBlock);
expect(moduleDirectBindings.size).toBe(0);
for (const [key, expectedBinding] of Object.entries(expectedSuperUserModuleBindings)) {
expect(moduleGetterBindings.get(key)).toBe(expectedBinding);
}
expect(superUserObjectSource).toMatch(/get\s+cron\s*\(\)\s*{\s*return\s+Cron;\s*}/);
});
});