Files
fusion/packages/plugin-sdk/src/__tests__/index.test.ts
gsxdsm de38ead4c9 fix(ci): restore main full-suite after path peel and suite drift (#3334)
## 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 -->
2026-08-07 00:25:10 -07:00

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);
});
});
});