- is_admin flag is the sole admin authority; remove role-name fallbacks (dependencies, reports, tasks, agent lifecycle, security badge, frontend) - normalize (resource, ACTION) across REST/MCP/catalog; strict admin role/permission parsing (400 on unknown) - gate LLM provider CRUD/status/test on admin:settings; require auth on agent llm-status; quarantine listing requires scenario:automation MANAGE - enforce logical-session revocation/idle in require_api_key_or_jwt - last-admin lockout guards for user/role CRUD; block non-admin from granting is_admin - frontend: flag-only admin, route permission map from the nav registry, write-control gating, 403 dedupe - tests: orthogonal edge coverage (flag-only admin, lockout boundaries, session states, normalization)
63 lines
3.2 KiB
TypeScript
63 lines
3.2 KiB
TypeScript
// #region Tests.ProjectSections.Guard [C:2] [TYPE Module] [SEMANTICS test, sidebar, features, manifest, guard]
|
|
// @SEMANTICS: tests, sidebar, features, manifest
|
|
// @BRIEF: Guard — nav feature-ids, the projectSections.json manifest, and what buildSidebarSections
|
|
// surfaces must stay in sync. Catches a raw requiredFeature string that bypassed the manifest
|
|
// and a manifest id that no nav node uses anymore.
|
|
// @LAYER Tests
|
|
// @RELATION DEPENDS_ON -> [EXT:frontend:SidebarNavigation]
|
|
// @RELATION BINDS_TO -> [Core.ConfigModels.FeaturesConfig]
|
|
// @TEST_INVARIANT: NavFeatureManifestSync -> VERIFIED_BY: nav_ids_match_manifest, admin_view_surfaces_manifest, every_node_has_context.
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
import { buildSidebarSections, getTogglableFeatureNodes } from "../sidebarNavigation";
|
|
import manifest from "../projectSections.json";
|
|
|
|
const adminUser = { roles: [{ name: "Admin", is_admin: true, permissions: [] }] };
|
|
|
|
// #region Tests.ProjectSections.Guard.NavFeatureIdsFromBuiltSections [TYPE Function]
|
|
// @BRIEF: Collect feature ids surfaced by buildSidebarSections for an admin with all flags enabled.
|
|
function navFeatureIdsFromBuiltSections(): string[] {
|
|
const seen = new Set<string>();
|
|
for (const section of buildSidebarSections({ nav: {} }, adminUser, {})) {
|
|
for (const category of section.categories) {
|
|
if (category.requiredFeature) seen.add(category.requiredFeature);
|
|
for (const sub of category.subItems || []) {
|
|
if (sub.requiredFeature) seen.add(sub.requiredFeature);
|
|
}
|
|
}
|
|
}
|
|
return [...seen].sort();
|
|
}
|
|
// #endregion Tests.ProjectSections.Guard.NavFeatureIdsFromBuiltSections
|
|
|
|
describe("projectSections manifest guard", () => {
|
|
it("nav_ids_match_manifest: getTogglableFeatureNodes covers exactly the manifest ids", () => {
|
|
const fromRegistry = getTogglableFeatureNodes().map((node) => node.featureId).sort();
|
|
expect(fromRegistry).toEqual([...manifest.navFeatures].sort());
|
|
});
|
|
|
|
it("admin_view_surfaces_manifest: buildSidebarSections exposes every manifest flag", () => {
|
|
expect(navFeatureIdsFromBuiltSections()).toEqual([...manifest.navFeatures].sort());
|
|
});
|
|
|
|
it("every_node_has_context: each togglable node carries section and category context", () => {
|
|
const nodes = getTogglableFeatureNodes();
|
|
expect(nodes.length).toBeGreaterThan(0);
|
|
for (const node of nodes) {
|
|
expect(node.featureId, "featureId must be set").toBeTruthy();
|
|
expect(node.labelKey, `labelKey missing for ${node.featureId}`).toBeTruthy();
|
|
expect(node.sectionId, `sectionId missing for ${node.featureId}`).toBeTruthy();
|
|
expect(node.sectionLabelKey, `sectionLabelKey missing for ${node.featureId}`).toBeTruthy();
|
|
expect(node.categoryId, `categoryId missing for ${node.featureId}`).toBeTruthy();
|
|
expect(node.categoryLabelKey, `categoryLabelKey missing for ${node.featureId}`).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
it("groups follow nav order: resources → operations → system", () => {
|
|
const nodes = getTogglableFeatureNodes();
|
|
const sectionOrder = [...new Set(nodes.map((node) => node.sectionId))];
|
|
expect(sectionOrder).toEqual(["resources", "operations", "system"]);
|
|
});
|
|
});
|
|
// #endregion Tests.ProjectSections.Guard
|