105 lines
4.2 KiB
TypeScript
105 lines
4.2 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
|
|
import type { ActionRegistryItem } from "@/types/appops";
|
|
import { buildActionRegistryPresetView, buildActionRegistryView, getActionRegistryPresetFilters, paginateActionRegistryItems, type ActionRegistryFilters } from "./actionRegistryView.ts";
|
|
|
|
const item = (overrides: Partial<ActionRegistryItem>): ActionRegistryItem => ({
|
|
id: "safety.summary",
|
|
label: "Safety / Summary",
|
|
category: "safety",
|
|
target: "safety",
|
|
risk: "low",
|
|
capability: "read_only",
|
|
policyDecision: "allowed",
|
|
policyReason: "Read-only summary.",
|
|
...overrides,
|
|
});
|
|
|
|
const registryItems: ActionRegistryItem[] = [
|
|
item({ id: "safety.summary", label: "Safety / Summary", category: "safety", target: "safety", risk: "low", capability: "read_only", policyDecision: "allowed" }),
|
|
item({ id: "volume.delete", label: "Volume / Delete", category: "volume", target: "volume", risk: "critical", capability: "destructive", policyDecision: "blocked" }),
|
|
item({ id: "stack.redeploy", label: "Stack / Redeploy", category: "stack", target: "stack", risk: "high", capability: "operator", policyDecision: "approval_required" }),
|
|
item({ id: "radarr.add_movie", label: "Radarr / Add Movie", category: "radarr", target: "radarr", risk: "low", capability: "operator", policyDecision: "allowed" }),
|
|
];
|
|
|
|
describe("buildActionRegistryView", () => {
|
|
it("filters by search, category, risk and policy", () => {
|
|
const filters: ActionRegistryFilters = {
|
|
query: "delete",
|
|
category: "volume",
|
|
risk: "critical",
|
|
policyDecision: "blocked",
|
|
capability: "destructive",
|
|
};
|
|
|
|
const result = buildActionRegistryView(registryItems, filters);
|
|
|
|
assert.deepEqual(result.items.map((entry) => entry.id), ["volume.delete"]);
|
|
assert.equal(result.activeFilterCount, 5);
|
|
});
|
|
|
|
it("computes facets from the full item set before filtering", () => {
|
|
const result = buildActionRegistryView(registryItems, { query: "radarr" });
|
|
|
|
assert.deepEqual(result.items.map((entry) => entry.id), ["radarr.add_movie"]);
|
|
assert.deepEqual(result.facets.categories, ["radarr", "safety", "stack", "volume"]);
|
|
assert.equal(result.facets.policy.blocked, 1);
|
|
assert.equal(result.facets.risk.critical, 1);
|
|
assert.equal(result.facets.capability.read_only, 1);
|
|
});
|
|
|
|
it("sorts highest-risk blocked actions first by default", () => {
|
|
const result = buildActionRegistryView(registryItems, {});
|
|
|
|
assert.deepEqual(result.items.map((entry) => entry.id), [
|
|
"volume.delete",
|
|
"stack.redeploy",
|
|
"radarr.add_movie",
|
|
"safety.summary",
|
|
]);
|
|
});
|
|
|
|
it("provides operator preset filters for common review modes", () => {
|
|
assert.deepEqual(getActionRegistryPresetFilters("blocked_destructive"), {
|
|
policyDecision: "blocked",
|
|
capability: "destructive",
|
|
});
|
|
assert.deepEqual(getActionRegistryPresetFilters("approval_needed"), {
|
|
policyDecision: "approval_required",
|
|
});
|
|
assert.deepEqual(getActionRegistryPresetFilters("safe_read_only"), {
|
|
policyDecision: "allowed",
|
|
capability: "read_only",
|
|
});
|
|
});
|
|
|
|
it("describes active presets with count and operator intent", () => {
|
|
const blocked = buildActionRegistryPresetView("blocked_destructive", registryItems);
|
|
const approval = buildActionRegistryPresetView("approval_needed", registryItems);
|
|
|
|
assert.equal(blocked.label, "Blocked destructive");
|
|
assert.equal(blocked.count, 1);
|
|
assert.equal(blocked.filters.policyDecision, "blocked");
|
|
assert.match(blocked.description, /gevaarlijke/i);
|
|
assert.equal(approval.count, 1);
|
|
assert.match(approval.description, /approval/i);
|
|
});
|
|
|
|
it("bounds registry rendering and clamps pages after filtering", () => {
|
|
const items = Array.from({ length: 65 }, (_, index) => item({ id: `action.${index}` }));
|
|
|
|
const secondPage = paginateActionRegistryItems(items, 2, 30);
|
|
assert.equal(secondPage.items.length, 30);
|
|
assert.equal(secondPage.start, 31);
|
|
assert.equal(secondPage.end, 60);
|
|
assert.equal(secondPage.pageCount, 3);
|
|
|
|
const clamped = paginateActionRegistryItems(items.slice(0, 4), 9, 30);
|
|
assert.equal(clamped.page, 1);
|
|
assert.equal(clamped.items.length, 4);
|
|
assert.equal(clamped.start, 1);
|
|
assert.equal(clamped.end, 4);
|
|
});
|
|
});
|