Files
compras/scripts/menu-code-check.mjs
2026-10-30 20:14:24 +02:00

172 lines
5.1 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import { spawnSync } from 'node:child_process';
import {
procurementNavigation,
activeProcurementEntry,
} from '../resources/js/lib/procurement-navigation.ts';
const result = spawnSync(
'php',
['artisan', 'route:list', '--json', '--except-vendor'],
{ encoding: 'utf8' },
);
assert.equal(result.status, 0, result.stderr);
const routes = JSON.parse(result.stdout);
const rows = new Map();
for (const role of [
'admin',
'procurement',
'finance',
'department_head',
'warehouse',
'requester',
]) {
for (const payables of [false, true]) {
const groups = procurementNavigation(role, payables);
assert.ok(
groups.length <= 6,
'Navigation must stay within six related functional groups',
);
const ids = groups.flatMap((group) =>
group.entries.map((entry) => entry.id),
);
assert.equal(
new Set(ids).size,
ids.length,
'Navigation entries must not be duplicated',
);
for (const group of groups)
for (const entry of group.entries) {
const uri = new URL(entry.path, 'http://local').pathname.slice(
1,
);
const route = routes.find(
(route) =>
route.uri === uri &&
route.method.split('|').includes('GET'),
);
assert.ok(
route,
`${role}: ${entry.label} has no registered GET route (${uri})`,
);
assert.equal(
activeProcurementEntry(groups, entry.path),
entry.id,
`${role}: wrong active menu for ${entry.path}`,
);
rows.set(`${role}:${entry.id}`, {
role,
group: group.label,
id: entry.id,
label: entry.label,
path: entry.path,
action: route.action,
full_page: !!entry.fullPage,
});
}
assert.equal(
groups.some((group) =>
group.entries.some(
(entry) => entry.id === 'procurement_reports',
),
),
['admin', 'procurement', 'finance', 'department_head'].includes(
role,
),
);
}
}
const configuredFinance = procurementNavigation(['finance'], false).flatMap(
(g) => g.entries,
);
const separate = procurementNavigation('admin', true);
assert.ok(
separate
.find((group) => group.id === 'purchasing')
.entries.every((entry) => !entry.path.startsWith('/pessoas')),
);
assert.ok(
separate
.find((group) => group.id === 'people')
.entries.every((entry) => entry.path.startsWith('/pessoas')),
);
for (const permission of [
'people_service_prepare',
'people_service_review',
'people_staff_prepare',
'people_staff_review',
]) {
const entries = procurementNavigation([permission], false).flatMap(
(group) => group.entries,
);
assert.equal(
entries.some((entry) => entry.id === 'people_hiring'),
true,
);
assert.equal(
entries.some(
(entry) =>
entry.id === 'company' ||
entry.id === 'invoices' ||
entry.id === 'new_request',
),
false,
);
}
for (const permission of ['people_registry_read', 'people_registry_manage']) {
const entries = procurementNavigation([permission], false).flatMap(
(group) => group.entries,
);
assert.equal(
entries.some((entry) => entry.id === 'people_registry'),
true,
);
assert.equal(
entries.some((entry) => entry.id === 'people_hiring'),
false,
);
}
assert.equal(
configuredFinance.some((e) => e.id === 'organization_parameters'),
false,
);
assert.equal(
configuredFinance.some((e) => e.id === 'new_request'),
false,
);
const configuredMulti = procurementNavigation(
['finance', 'requester', 'admin'],
false,
).flatMap((g) => g.entries);
assert.equal(
configuredMulti.some((e) => e.id === 'organization_parameters'),
true,
);
assert.equal(
configuredMulti.some((e) => e.id === 'new_request'),
true,
);
const proof = {
checked_at: new Date().toISOString(),
result: 'passed',
entries: rows.size,
roles: 6,
configurable_permissions_and_multiple_profiles: true,
maximum_functional_groups: 6,
source: 'procurementNavigation compared with actual Laravel GET routes',
scope: 'Menu routing, active entry and visibility contract; not a substitute for functional or visual workflow acceptance',
rows: [...rows.values()],
};
fs.writeFileSync(
'docs/proofs/menu-code-contract.json',
JSON.stringify(proof, null, 2) + '\n',
);
console.log(
JSON.stringify({
result: proof.result,
entries: proof.entries,
roles: proof.roles,
}),
);