test(FN-4150): complete Step 3 — add workflow integration assertions
Fusion-Task-Id: FN-4150 Fusion-Task-Lineage: 153c239b-f6cd-49e4-bc13-216e0d2ca815
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { validatePluginManifest } from "@fusion/core";
|
||||
import plugin from "../index.js";
|
||||
import plugin, { CLI_PRINTING_PRESS_WORKFLOW_STEPS } from "../index.js";
|
||||
import { ensureCliPressSchema } from "../store/cli-press-store.js";
|
||||
import { makeFakeRegistry } from "./fixtures/registry.js";
|
||||
|
||||
@@ -23,4 +23,22 @@ describe("plugin registration contracts", () => {
|
||||
h.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
it("contributes script-mode workflow step templates", () => {
|
||||
expect(plugin.workflowSteps?.length).toBeGreaterThan(0);
|
||||
expect(plugin.workflowSteps).toEqual(CLI_PRINTING_PRESS_WORKFLOW_STEPS);
|
||||
|
||||
for (const step of plugin.workflowSteps ?? []) {
|
||||
expect(step.stepId).toMatch(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
|
||||
}
|
||||
|
||||
expect(
|
||||
plugin.workflowSteps?.some((step) => step.mode === "script" && step.phase === "pre-merge"),
|
||||
).toBe(true);
|
||||
|
||||
expect(plugin.manifest.workflowSteps?.map((step) => step.stepId)).toEqual(
|
||||
plugin.workflowSteps?.map((step) => step.stepId),
|
||||
);
|
||||
expect(validatePluginManifest(plugin.manifest).valid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,117 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { exec } from "node:child_process";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { TaskStore, type WorkflowStepTemplate } from "@fusion/core";
|
||||
import plugin from "../index.js";
|
||||
import { installExecMock } from "./fixtures/exec-mock.js";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
async function makeTaskStore() {
|
||||
const rootDir = await mkdtemp(join(tmpdir(), "fn-4150-cli-printing-press-"));
|
||||
const globalDir = await mkdtemp(join(tmpdir(), "fn-4150-cli-printing-press-global-"));
|
||||
const store = new TaskStore(rootDir, globalDir, { inMemoryDb: true });
|
||||
await store.init();
|
||||
return { store, rootDir, globalDir };
|
||||
}
|
||||
|
||||
async function cleanupTaskStore(ctx: Awaited<ReturnType<typeof makeTaskStore>>) {
|
||||
ctx.store.stopWatching();
|
||||
ctx.store.close();
|
||||
await rm(ctx.rootDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
||||
await rm(ctx.globalDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
||||
}
|
||||
|
||||
function injectPluginTemplates(store: TaskStore): void {
|
||||
const templates: Array<{ pluginId: string; template: WorkflowStepTemplate }> = (plugin.workflowSteps ?? []).map((template) => ({
|
||||
pluginId: plugin.manifest.id,
|
||||
template: {
|
||||
id: `plugin:${plugin.manifest.id}:${template.stepId}`,
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
prompt: template.prompt ?? "",
|
||||
mode: template.mode,
|
||||
phase: template.phase,
|
||||
scriptName: template.scriptName,
|
||||
toolMode: template.toolMode,
|
||||
defaultOn: template.defaultOn,
|
||||
modelProvider: template.modelProvider,
|
||||
modelId: template.modelId,
|
||||
enabled: template.enabled,
|
||||
category: "Plugin",
|
||||
icon: "puzzle",
|
||||
},
|
||||
}));
|
||||
store.setPluginWorkflowStepTemplates(templates);
|
||||
}
|
||||
|
||||
async function runScriptStepFromSettings(store: TaskStore, scriptName: string): Promise<{ stdout: string; stderr: string }> {
|
||||
const settings = await store.getSettings();
|
||||
const command = settings.scripts?.[scriptName];
|
||||
if (!command) {
|
||||
throw new Error(`Missing script command for ${scriptName}`);
|
||||
}
|
||||
return execAsync(command, { timeout: 20_000, maxBuffer: 5 * 1024 * 1024 });
|
||||
}
|
||||
|
||||
describe("workflow integration contracts", () => {
|
||||
// FN-4150/FN-3768 track future workflow-step template + runWorkflowSteps coverage.
|
||||
const stores: Array<Awaited<ReturnType<typeof makeTaskStore>>> = [];
|
||||
|
||||
afterEach(async () => {
|
||||
while (stores.length > 0) {
|
||||
const ctx = stores.pop();
|
||||
if (ctx) {
|
||||
await cleanupTaskStore(ctx);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("guards against execSync usage in workflow-oriented execution fixtures", () => {
|
||||
const execMock = installExecMock();
|
||||
execMock.assertExecSyncUnused();
|
||||
expect(typeof plugin.manifest.id).toBe("string");
|
||||
});
|
||||
|
||||
it("contributes plugin workflow step templates", () => {
|
||||
expect(plugin.workflowSteps?.length).toBeGreaterThan(0);
|
||||
expect(plugin.workflowSteps?.some((step) => step.mode === "script")).toBe(true);
|
||||
it("materializes plugin script-mode workflow step through TaskStore", async () => {
|
||||
const ctx = await makeTaskStore();
|
||||
stores.push(ctx);
|
||||
injectPluginTemplates(ctx.store);
|
||||
|
||||
const step = await ctx.store.getWorkflowStep("plugin:fusion-plugin-cli-printing-press:run-service-cli");
|
||||
expect(step).toMatchObject({
|
||||
id: "plugin:fusion-plugin-cli-printing-press:run-service-cli",
|
||||
mode: "script",
|
||||
phase: "pre-merge",
|
||||
scriptName: "cli-printing-press:run-service-cli",
|
||||
});
|
||||
});
|
||||
|
||||
it("runs a plugin script-mode step end-to-end via TaskStore plus project scripts", async () => {
|
||||
const execMock = installExecMock();
|
||||
execMock.setNextResult({ stdout: "ok\n", stderr: "", code: 0 });
|
||||
|
||||
const ctx = await makeTaskStore();
|
||||
stores.push(ctx);
|
||||
injectPluginTemplates(ctx.store);
|
||||
|
||||
await ctx.store.updateSettings({
|
||||
scripts: {
|
||||
"cli-printing-press:run-service-cli": "echo ok",
|
||||
},
|
||||
});
|
||||
|
||||
const step = await ctx.store.getWorkflowStep("plugin:fusion-plugin-cli-printing-press:run-service-cli");
|
||||
expect(step).toMatchObject({
|
||||
mode: "script",
|
||||
scriptName: "cli-printing-press:run-service-cli",
|
||||
phase: "pre-merge",
|
||||
});
|
||||
|
||||
const result = await runScriptStepFromSettings(ctx.store, step!.scriptName!);
|
||||
expect(result.stdout).toContain("ok");
|
||||
execMock.assertExecSyncUnused();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user