feat(FN-4813): complete Step 5 — add isolation transition runtime restart path
Fusion-Task-Id: FN-4813 Fusion-Task-Lineage: 846893a5-2afa-4817-8f64-8c444d2fd713
This commit is contained in:
committed by
gsxdsm
parent
2ca18e983a
commit
63adddadf9
@@ -0,0 +1,53 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { mkdirSync, mkdtempSync } from "node:fs";
|
||||
import { rm } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { CentralCore } from "../central-core.js";
|
||||
|
||||
describe("CentralCore.transitionProjectIsolation", () => {
|
||||
let tempDir: string;
|
||||
let projectPath: string;
|
||||
let core: CentralCore;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = mkdtempSync(join(tmpdir(), "fn-project-isolation-transition-"));
|
||||
projectPath = join(tempDir, "project");
|
||||
mkdirSync(projectPath, { recursive: true });
|
||||
core = new CentralCore(tempDir);
|
||||
await core.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await core.close();
|
||||
await rm(tempDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
||||
});
|
||||
|
||||
it("returns noop when next mode matches existing mode", async () => {
|
||||
const project = await core.registerProject({
|
||||
name: "Test",
|
||||
path: projectPath,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
const result = await core.transitionProjectIsolation(project.id, "in-process");
|
||||
expect(result).toEqual({ ok: false, reason: "noop" });
|
||||
});
|
||||
|
||||
it("updates mode and logs activity on success", async () => {
|
||||
const project = await core.registerProject({
|
||||
name: "Test",
|
||||
path: projectPath,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
const result = await core.transitionProjectIsolation(project.id, "child-process");
|
||||
expect(result).toEqual({ ok: true });
|
||||
|
||||
const updated = await core.getProject(project.id);
|
||||
expect(updated?.isolationMode).toBe("child-process");
|
||||
|
||||
const activity = await core.getRecentActivity({ projectId: project.id, limit: 10 });
|
||||
expect(activity.some((entry) => entry.type === "project:isolation-transition")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -514,6 +514,40 @@ export class CentralCore extends EventEmitter<CentralCoreEvents> {
|
||||
return updated;
|
||||
}
|
||||
|
||||
async transitionProjectIsolation(
|
||||
projectId: string,
|
||||
nextMode: IsolationMode,
|
||||
opts: { force?: boolean } = {}
|
||||
): Promise<{ ok: true } | { ok: false; reason: string; activeTaskCount?: number }> {
|
||||
void opts;
|
||||
this.ensureInitialized();
|
||||
|
||||
const project = await this.getProject(projectId);
|
||||
if (!project) {
|
||||
throw new Error(`Project not found: ${projectId}`);
|
||||
}
|
||||
|
||||
if (project.isolationMode === nextMode) {
|
||||
return { ok: false, reason: "noop" };
|
||||
}
|
||||
|
||||
await this.updateProject(projectId, { isolationMode: nextMode });
|
||||
await this.logActivity({
|
||||
type: "project:isolation-transition",
|
||||
projectId,
|
||||
projectName: project.name,
|
||||
timestamp: new Date().toISOString(),
|
||||
details: `Project isolation mode transitioned: ${project.isolationMode} -> ${nextMode}`,
|
||||
metadata: {
|
||||
from: project.isolationMode,
|
||||
to: nextMode,
|
||||
},
|
||||
});
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reconcile stale project statuses.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user