## Summary Restores the non-blocking full suite on `main` after consistent shard failures (latest red: [run 30982276306](https://github.com/Runfusion/Fusion/actions/runs/30982276306); all four shards failed on `@fusion/core`, `@fusion/engine`, and `@fusion/plugin-sdk`). ### Fixes - **Path / import drift** after code-organization peels: update static-guard and integration tests to new module locations (`central/`, `board/`, `execution/`, `merge/`, `worktree/`, `plugins/`, `types/*` barrels, etc.). - **Inventory re-pins**: - SQLite production `DatabaseSync` allowlist (`central/project-identity.ts`, `db/sqlite-validation.ts`) - Engine blocking-shellout allowlist regenerated from live source (33 audited sites) - Core log-severity manifest paths for peeled modules - **Partial protocol assert update** for `isPlanReviewSatisfied` (file also quarantined until full rescue) ### Quarantine (deletion ratchet) Remaining behavioral reds quarantined on sight — no timeout/retry/assertion appeasement: - **14 core** files (incomplete unit fakes for `layer.db.select`, ledger/census drift, 15s wedge timeout, serialization protocol drift) - **13 engine** files (mock-hoist errors, fake-store/census/behavior drift under suite) Paired updates: `scripts/lib/test-quarantine.json` + package vitest excludes. Deletion clock starts `2026-08-05`. ### Local verification - Path-fixed core scanners: 173 passed - Path-fixed engine scanners: 58 passed - `@fusion/plugin-sdk` full: 16 passed - PG smokes: mission-autopilot, research-execution, satellite, transition-pending, workflow-sync ## Test plan - [ ] CI PR checks green (lint/typecheck/build/gate) - [ ] Full suite on merge to main: all 4 shards green or only intentional non-blocking signal - [ ] Confirm quarantined files appear in ledger + vitest excludes and are not executed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Updated test coverage to reflect reorganized source locations and module paths. * Refreshed static checks, allowlists, and source-based assertions without changing tested behavior. * **Chores** * Quarantined failing core and engine test suites with documented tracking details. * Updated test configuration and quarantine records to improve suite stability and reporting. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
309 lines
9.8 KiB
TypeScript
309 lines
9.8 KiB
TypeScript
import { describe, it, expect, beforeAll } from "vitest";
|
|
import { definePlugin } from "../index.js";
|
|
import type { FusionPlugin, PluginUiContributionSurface } from "../../../core/src/plugins/plugin-types.js";
|
|
import type { TaskStore } from "../../../core/src/store.js";
|
|
import { validatePluginManifest } from "../../../core/src/plugins/plugin-types.js";
|
|
|
|
type AssertNever<T extends never> = T;
|
|
type NoStaleStructuredSurface = AssertNever<Extract<
|
|
PluginUiContributionSurface,
|
|
"settings-integration-card" | "onboarding-recommendation-card"
|
|
>>;
|
|
|
|
let validateFn: typeof validatePluginManifest;
|
|
|
|
beforeAll(async () => {
|
|
validateFn = validatePluginManifest;
|
|
});
|
|
|
|
describe("Plugin SDK", () => {
|
|
// ── definePlugin ────────────────────────────────────────────────────
|
|
|
|
describe("definePlugin", () => {
|
|
it("returns the input unchanged (identity function)", () => {
|
|
const plugin = {
|
|
manifest: {
|
|
id: "test-plugin",
|
|
name: "Test Plugin",
|
|
version: "1.0.0",
|
|
},
|
|
state: "installed" as const,
|
|
hooks: {},
|
|
tools: [],
|
|
routes: [],
|
|
};
|
|
|
|
const result = definePlugin(plugin);
|
|
|
|
expect(result).toBe(plugin);
|
|
});
|
|
|
|
it("provides type narrowing for plugin definitions", () => {
|
|
// This test verifies that definePlugin provides proper TypeScript inference
|
|
const plugin = definePlugin({
|
|
manifest: {
|
|
id: "my-plugin",
|
|
name: "My Plugin",
|
|
version: "1.0.0",
|
|
description: "A test plugin",
|
|
},
|
|
state: "installed",
|
|
hooks: {
|
|
onLoad: async (ctx) => {
|
|
ctx.logger.info("Loaded!");
|
|
},
|
|
},
|
|
tools: [
|
|
{
|
|
name: "my_tool",
|
|
description: "A useful tool",
|
|
parameters: {},
|
|
execute: async (_params, _ctx) => ({
|
|
content: [{ type: "text" as const, text: "Hello!" }],
|
|
}),
|
|
},
|
|
],
|
|
routes: [],
|
|
});
|
|
|
|
expect(plugin.manifest.id).toBe("my-plugin");
|
|
expect(plugin.manifest.name).toBe("My Plugin");
|
|
expect(plugin.hooks.onLoad).toBeDefined();
|
|
expect(plugin.tools).toHaveLength(1);
|
|
expect(plugin.tools![0].name).toBe("my_tool");
|
|
});
|
|
|
|
it("works with minimal plugin definition", () => {
|
|
const plugin = definePlugin({
|
|
manifest: {
|
|
id: "minimal",
|
|
name: "Minimal",
|
|
version: "0.1.0",
|
|
},
|
|
state: "installed",
|
|
hooks: {},
|
|
tools: [],
|
|
routes: [],
|
|
});
|
|
|
|
expect(plugin.manifest.id).toBe("minimal");
|
|
});
|
|
|
|
it("preserves all plugin properties", () => {
|
|
const plugin = definePlugin({
|
|
manifest: {
|
|
id: "full-plugin",
|
|
name: "Full Plugin",
|
|
version: "2.0.0",
|
|
description: "A full-featured plugin",
|
|
author: "Test Author",
|
|
homepage: "https://example.com",
|
|
fusionVersion: "1.0.0",
|
|
dependencies: ["other-plugin"],
|
|
settingsSchema: {
|
|
apiKey: {
|
|
type: "string" as const,
|
|
required: true,
|
|
},
|
|
},
|
|
},
|
|
state: "installed",
|
|
hooks: {
|
|
onLoad: async () => {},
|
|
onUnload: async () => {},
|
|
onTaskCreated: async () => {},
|
|
onTaskMoved: async () => {},
|
|
onTaskCompleted: async () => {},
|
|
onError: async () => {},
|
|
},
|
|
tools: [
|
|
{
|
|
name: "tool1",
|
|
description: "Tool 1",
|
|
parameters: {},
|
|
execute: async () => ({ content: [] }),
|
|
},
|
|
],
|
|
routes: [
|
|
{
|
|
method: "GET" as const,
|
|
path: "/status",
|
|
handler: async () => ({ status: "ok" }),
|
|
description: "Health check",
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(plugin.manifest.id).toBe("full-plugin");
|
|
expect(plugin.manifest.description).toBe("A full-featured plugin");
|
|
expect(plugin.manifest.author).toBe("Test Author");
|
|
expect(plugin.manifest.homepage).toBe("https://example.com");
|
|
expect(plugin.manifest.fusionVersion).toBe("1.0.0");
|
|
expect(plugin.manifest.dependencies).toEqual(["other-plugin"]);
|
|
expect(plugin.manifest.settingsSchema).toBeDefined();
|
|
expect(plugin.hooks.onLoad).toBeDefined();
|
|
expect(plugin.hooks.onUnload).toBeDefined();
|
|
expect(plugin.hooks.onTaskCreated).toBeDefined();
|
|
expect(plugin.hooks.onTaskMoved).toBeDefined();
|
|
expect(plugin.hooks.onTaskCompleted).toBeDefined();
|
|
expect(plugin.hooks.onError).toBeDefined();
|
|
expect(plugin.tools).toHaveLength(1);
|
|
expect(plugin.routes).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
// ── Type exports ────────────────────────────────────────────────────
|
|
|
|
describe("type exports", () => {
|
|
it("exports PluginManifest type", () => {
|
|
// This is a compile-time test - if types are exported correctly, this will compile
|
|
const manifest: import("../../../core/src/plugins/plugin-types.js").PluginManifest = {
|
|
id: "test",
|
|
name: "Test",
|
|
version: "1.0.0",
|
|
};
|
|
expect(manifest.id).toBe("test");
|
|
});
|
|
|
|
it("exports FusionPlugin type", () => {
|
|
const plugin: FusionPlugin = {
|
|
manifest: {
|
|
id: "test",
|
|
name: "Test",
|
|
version: "1.0.0",
|
|
},
|
|
state: "installed",
|
|
hooks: {},
|
|
tools: [],
|
|
routes: [],
|
|
};
|
|
expect(plugin.manifest.id).toBe("test");
|
|
});
|
|
|
|
it("exports CLI provider contract types", () => {
|
|
const cliProvider: import("../../../core/src/plugins/plugin-types.js").CliProviderContribution = {
|
|
providerId: "cursor-cli",
|
|
displayName: "Cursor CLI",
|
|
binaryName: "cursor-agent",
|
|
providerType: "cli",
|
|
statusRoute: "/providers/cursor-cli/status",
|
|
authRoute: "/auth/cursor-cli",
|
|
};
|
|
expect(cliProvider.providerId).toBe("cursor-cli");
|
|
});
|
|
|
|
it("exports PluginContext type", () => {
|
|
const ctx: import("../../../core/src/plugins/plugin-types.js").PluginContext = {
|
|
pluginId: "test",
|
|
taskStore: {} as unknown as TaskStore,
|
|
settings: {},
|
|
logger: {
|
|
info: () => {},
|
|
warn: () => {},
|
|
error: () => {},
|
|
debug: () => {},
|
|
},
|
|
emitEvent: () => {},
|
|
};
|
|
expect(ctx.pluginId).toBe("test");
|
|
});
|
|
|
|
it("exports PluginUiSlotDefinition type", () => {
|
|
// This is a compile-time test - if types are exported correctly, this will compile
|
|
const slot: import("../../../core/src/plugins/plugin-types.js").PluginUiSlotDefinition = {
|
|
slotId: "task-detail-tab",
|
|
label: "Task Details",
|
|
icon: "FileText",
|
|
componentPath: "./components/TaskDetailTab.js",
|
|
};
|
|
expect(slot.slotId).toBe("task-detail-tab");
|
|
expect(slot.label).toBe("Task Details");
|
|
expect(slot.icon).toBe("FileText");
|
|
expect(slot.componentPath).toBe("./components/TaskDetailTab.js");
|
|
});
|
|
|
|
it("PluginUiSlotDefinition icon is optional", () => {
|
|
// This is a compile-time test - if types are exported correctly, this will compile
|
|
const slot: import("../../../core/src/plugins/plugin-types.js").PluginUiSlotDefinition = {
|
|
slotId: "header-action",
|
|
label: "Header Action",
|
|
componentPath: "./components/HeaderAction.js",
|
|
};
|
|
expect(slot.slotId).toBe("header-action");
|
|
expect((slot as any).icon).toBeUndefined();
|
|
});
|
|
|
|
it("PluginUiSlotDefinition can be used in FusionPlugin", () => {
|
|
// This verifies that PluginUiSlotDefinition can be used in the FusionPlugin interface
|
|
const plugin: FusionPlugin = {
|
|
manifest: {
|
|
id: "test",
|
|
name: "Test",
|
|
version: "1.0.0",
|
|
},
|
|
state: "installed",
|
|
hooks: {},
|
|
tools: [],
|
|
routes: [],
|
|
uiSlots: [
|
|
{
|
|
slotId: "custom-tab",
|
|
label: "Custom Tab",
|
|
componentPath: "./components/CustomTab.js",
|
|
},
|
|
],
|
|
};
|
|
expect(plugin.uiSlots).toHaveLength(1);
|
|
expect(plugin.uiSlots![0].slotId).toBe("custom-tab");
|
|
});
|
|
it("PluginDashboardViewDefinition can be used in FusionPlugin", () => {
|
|
const plugin: FusionPlugin = {
|
|
manifest: { id: "test", name: "Test", version: "1.0.0" },
|
|
state: "installed",
|
|
hooks: {},
|
|
tools: [],
|
|
routes: [],
|
|
dashboardViews: [
|
|
{ viewId: "graph", label: "Graph", componentPath: "./views/Graph.js", placement: "more" },
|
|
],
|
|
};
|
|
expect(plugin.dashboardViews).toHaveLength(1);
|
|
expect(plugin.dashboardViews?.[0].viewId).toBe("graph");
|
|
});
|
|
|
|
it("exposes only normalized structured surface names", () => {
|
|
const surface: PluginUiContributionSurface = "settings-config-section";
|
|
expect(surface).toBe("settings-config-section");
|
|
|
|
const compileGuard: NoStaleStructuredSurface | undefined = undefined;
|
|
expect(compileGuard).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
// ── validatePluginManifest ───────────────────────────────────────────
|
|
|
|
describe("validatePluginManifest", () => {
|
|
it("validates a valid manifest", () => {
|
|
const result = validateFn({
|
|
id: "valid-plugin",
|
|
name: "Valid Plugin",
|
|
version: "1.0.0",
|
|
});
|
|
|
|
expect(result.valid).toBe(true);
|
|
expect(result.errors).toEqual([]);
|
|
});
|
|
|
|
it("rejects invalid manifest", () => {
|
|
const result = validateFn({
|
|
id: "",
|
|
name: "",
|
|
version: "",
|
|
});
|
|
|
|
expect(result.valid).toBe(false);
|
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
});
|