Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/application/featureManifests.test.ts
T

171 lines
8.2 KiB
TypeScript

import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { getEnvVariableDefinition } from "@toolctrl/config";
import { loadFeatureManifests } from "@toolctrl/plugins";
import { queueNames } from "@toolctrl/queue";
const manifestLoad = await loadFeatureManifests();
const manifests = manifestLoad.manifests;
const requiredPlaybooks = [
"docs/agent-entrypoints.md",
"docs/agent-playbooks/feature-integration.md",
"docs/agent-playbooks/mvp-intake.md",
"docs/agent-playbooks/admin-managed-features.md",
"docs/agent-playbooks/worker-backed-features.md",
];
function expectStringArray(value: unknown) {
expect(Array.isArray(value)).toBe(true);
expect((value as unknown[]).every((item) => typeof item === "string")).toBe(true);
}
describe("agent feature planning docs", () => {
it("provides the required agent entrypoint and playbooks", () => {
requiredPlaybooks.forEach((file) => {
expect(fs.existsSync(path.join(process.cwd(), file)), file).toBe(true);
});
});
it("documents the key concepts agents need before implementation", () => {
const entrypoint = fs.readFileSync(path.join(process.cwd(), "docs/agent-entrypoints.md"), "utf8");
const featureIntegration = fs.readFileSync(path.join(process.cwd(), "docs/agent-playbooks/feature-integration.md"), "utf8");
const adminManaged = fs.readFileSync(path.join(process.cwd(), "docs/agent-playbooks/admin-managed-features.md"), "utf8");
const workerBacked = fs.readFileSync(path.join(process.cwd(), "docs/agent-playbooks/worker-backed-features.md"), "utf8");
expect(entrypoint).toContain("MVP import");
expect(featureIntegration).toContain("Feature Integration Playbook");
expect(featureIntegration).toContain("Do not expose provider secrets");
expect(adminManaged).toContain("admin-managed");
expect(adminManaged).toContain("Provider keys must not be sent to tenant loaders");
expect(workerBacked).toContain("Browser-backed features require extra constraints");
});
});
describe("feature manifests", () => {
it("keeps every manifest on the integration-aware schema", () => {
manifests.forEach((manifest) => {
expect(manifest.schemaVersion).toBe("1.0");
expect(manifest.id).toMatch(/^[a-z0-9][a-z0-9-]*$/);
expect(manifest.title).toEqual(expect.any(String));
expect(["core", "plugin-candidate", "remove-candidate", "mixed"]).toContain(manifest.classification);
expect(["active", "proposed", "review-needed"]).toContain(manifest.status);
expectStringArray(manifest.rationale);
expectStringArray(manifest.codeAreas);
expectStringArray(manifest.dependencies);
expectStringArray(manifest.consumers);
expectStringArray(manifest.boundaries.mustKeep);
expectStringArray(manifest.boundaries.mayMoveToPlugin);
expectStringArray(manifest.boundaries.mustNotTouch);
expectStringArray(manifest.pruning.notes);
expectStringArray(manifest.validation.source);
expect(manifest.validation.lastReviewed).toMatch(/^\d{4}-\d{2}-\d{2}$/);
});
});
it("requires explicit ownership, surfaces, configuration, workers and UAT", () => {
manifests.forEach((manifest) => {
expect(["tenant-owned", "admin-managed", "hybrid", "system-only"]).toContain(manifest.integration.ownership);
expectStringArray(manifest.integration.surfaces.adminRoutes);
expectStringArray(manifest.integration.surfaces.tenantRoutes);
expectStringArray(manifest.integration.surfaces.publicRoutes);
expectStringArray(manifest.integration.surfaces.apiRoutes);
expectStringArray(manifest.integration.configuration.admin);
expectStringArray(manifest.integration.configuration.tenant);
expectStringArray(manifest.integration.configuration.environment);
expectStringArray(manifest.integration.configuration.secrets);
expect(typeof manifest.integration.workers.required).toBe("boolean");
expectStringArray(manifest.integration.workers.queues);
expectStringArray(manifest.integration.workers.jobs);
expectStringArray(manifest.integration.workers.notes);
expectStringArray(manifest.integration.externalProviders);
expectStringArray(manifest.integration.permissions);
expectStringArray(manifest.integration.auditEvents);
expectStringArray(manifest.integration.billingImpacts);
expectStringArray(manifest.integration.uat);
expect(manifest.integration.uat.length, manifest.id).toBeGreaterThan(0);
});
});
it("loads real docs manifests without loader diagnostics", () => {
expect(manifestLoad.ok).toBe(true);
expect(manifestLoad.diagnostics).toEqual([]);
});
it("guards the Feature Manifest manifest decision contract in docs", () => {
const inventory = fs.readFileSync(path.join(process.cwd(), "docs/feature-inventory.md"), "utf8");
const manifestReadme = fs.readFileSync(path.join(process.cwd(), "docs/feature-manifests/README.md"), "utf8");
["Manifest-Decision", "Pruned Core", "Full-Boilerplate", "plugin-candidate", "remove-candidate"].forEach((anchor) => {
expect(inventory, `inventory missing ${anchor}`).toContain(anchor);
expect(manifestReadme, `manifest README missing ${anchor}`).toContain(anchor);
});
["core", "plugin-candidates", "remove-candidates"].forEach((manifestIdAnchor) => {
expect(manifestReadme, `manifest README missing ${manifestIdAnchor}`).toContain(manifestIdAnchor);
});
});
it("guards admin-managed secret ownership and worker-backed declarations", () => {
manifests
.filter((manifest) => manifest.integration.ownership === "admin-managed")
.forEach((manifest) => {
expect(manifest.integration.configuration.admin.length, manifest.id).toBeGreaterThan(0);
expect(manifest.integration.configuration.secrets.join(" ").toLowerCase(), manifest.id).toMatch(/admin|platform/);
expect(manifest.integration.permissions.some((permission) => permission.startsWith("admin.")), manifest.id).toBe(true);
expect(manifest.integration.auditEvents.length, manifest.id).toBeGreaterThan(0);
});
manifests
.filter((manifest) => manifest.integration.workers.required)
.forEach((manifest) => {
expect(manifest.integration.workers.queues.length, manifest.id).toBeGreaterThan(0);
expect(manifest.integration.workers.jobs.length, manifest.id).toBeGreaterThan(0);
});
});
it("includes the AI chatbot reference pattern for admin-managed worker-backed features", () => {
const chatbot = manifests.find((manifest) => manifest.id === "ai-chatbot-admin-managed");
expect(chatbot).toBeDefined();
expect(chatbot?.integration.ownership).toBe("admin-managed");
expect(chatbot?.integration.externalProviders).toContain("deployment-region-ai-provider");
expect(chatbot?.integration.workers.required).toBe(true);
expect(chatbot?.integration.workers.queues).toContain(queueNames.aiChatbot);
expect(chatbot?.integration.workers.queues).toContain(queueNames.browserAutomation);
expect(chatbot?.integration.workers.jobs).toEqual(expect.arrayContaining(["chat-completion", "browser-session-run", "browser-session-cleanup", "usage-flush"]));
expect(chatbot?.integration.configuration.environment).toEqual(
expect.arrayContaining([
"MISTRAL_API_KEY",
"MISTRAL_MODEL",
"SCALEWAY_API_KEY",
"SCALEWAY_MODEL",
"SCALEWAY_BASE_URL",
"ALEPH_ALPHA_API_KEY",
"ALEPH_ALPHA_MODEL",
"ALEPH_ALPHA_BASE_URL",
"BULLMQ_REDIS_URL",
"BROWSER_RUNTIME_URL",
])
);
expect(chatbot?.integration.configuration.secrets.join(" ")).toContain("never be exposed");
});
it("keeps admin-managed AI/browser feature contracts backed by config and queue packages", () => {
[
"BULLMQ_REDIS_URL",
"BROWSER_RUNTIME_URL",
"BROWSER_RUNTIME_API_KEY",
"BROWSER_SESSION_TIMEOUT_MS",
].forEach((name) => {
const definition = getEnvVariableDefinition(name);
expect(definition?.classification, name).toBe("optional-provider");
expect(definition?.targets.some((target) => target === "api" || target === "worker"), name).toBe(true);
});
expect(queueNames.aiChatbot).toBe("toolctrl.ai-chatbot");
expect(queueNames.browserAutomation).toBe("toolctrl.browser-automation");
});
});