refactor: remove legacy kb compatibility
Drops the .kb/kb.db migration path, legacy backup filename handling, and backward-compat test suites. Renames internal kbDir identifiers to fusionDir and hasKbProject/isValidKbProject to their fusion equivalents. - Remove needsCentralMigration, autoMigrateToCentral, and the "needs-migration" FirstRunState; checkAndMigrate and KB_SKIP_MIGRATION env var are gone - Remove LEGACY_BACKUP_DIR and canonicalizeBackupDir; listBackups no longer matches kb-* filenames - Delete backward-compat.test.ts and store-backward-compat.test.ts; update remaining tests to new 3-state first-run model Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,16 +12,16 @@ function makeTmpDir(): string {
|
||||
|
||||
describe("ChatStore", () => {
|
||||
let tmpDir: string;
|
||||
let kbDir: string;
|
||||
let fusionDir: string;
|
||||
let db: Database;
|
||||
let store: ChatStore;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir();
|
||||
kbDir = join(tmpDir, ".fusion");
|
||||
db = new Database(kbDir);
|
||||
fusionDir = join(tmpDir, ".fusion");
|
||||
db = new Database(fusionDir);
|
||||
db.init();
|
||||
store = new ChatStore(kbDir, db);
|
||||
store = new ChatStore(fusionDir, db);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { mkdirSync, rmSync, existsSync } from "node:fs";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { join } from "node:path";
|
||||
import { tempWorkspace } from "@fusion/test-utils";
|
||||
import { TaskStore } from "../store.js";
|
||||
import { CentralCore } from "../central-core.js";
|
||||
|
||||
// Helper to create a fake fusion project structure for the current store implementation
|
||||
function createFakeFusionProject(dir: string): void {
|
||||
const fusionDir = join(dir, ".fusion");
|
||||
mkdirSync(fusionDir, { recursive: true });
|
||||
const db = new DatabaseSync(join(fusionDir, "kb.db"));
|
||||
db.exec("CREATE TABLE IF NOT EXISTS sanity (id INTEGER PRIMARY KEY)");
|
||||
db.close();
|
||||
}
|
||||
|
||||
describe("TaskStore Backward Compatibility", () => {
|
||||
let tempDir: string;
|
||||
let centralCore: CentralCore;
|
||||
let originalCwd: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = tempWorkspace("kb-compat-test-");
|
||||
centralCore = new CentralCore(tempDir);
|
||||
await centralCore.init();
|
||||
originalCwd = process.cwd();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
try {
|
||||
process.chdir(originalCwd);
|
||||
await centralCore.close();
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
});
|
||||
|
||||
describe("getOrCreateForProject", () => {
|
||||
it("should create store for specified project ID", async () => {
|
||||
const projectDir = join(tempDir, "my-project");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
// Register the project first
|
||||
const project = await centralCore.registerProject({
|
||||
name: "my-project",
|
||||
path: projectDir,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
const store = await TaskStore.getOrCreateForProject(project.id, centralCore);
|
||||
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
// Verify it's using the correct path
|
||||
const settings = await store.getSettings();
|
||||
expect(settings).toBeDefined();
|
||||
});
|
||||
|
||||
it("should fall back to exact project name lookup when ID lookup misses", async () => {
|
||||
const projectDir = join(tempDir, "my-project");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
// Register the project
|
||||
await centralCore.registerProject({
|
||||
name: "my-project",
|
||||
path: projectDir,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
// Look up by name instead of ID
|
||||
const store = await TaskStore.getOrCreateForProject("my-project", centralCore);
|
||||
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
});
|
||||
|
||||
it("should use single registered project when no ID provided", async () => {
|
||||
const projectDir = join(tempDir, "single-project");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
// Register exactly one project
|
||||
await centralCore.registerProject({
|
||||
name: "single-project",
|
||||
path: projectDir,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
const store = await TaskStore.getOrCreateForProject(undefined, centralCore);
|
||||
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
});
|
||||
|
||||
it("should throw when multiple projects and no ID specified", async () => {
|
||||
const project1 = join(tempDir, "project-1");
|
||||
const project2 = join(tempDir, "project-2");
|
||||
mkdirSync(project1, { recursive: true });
|
||||
mkdirSync(project2, { recursive: true });
|
||||
|
||||
// Register two projects
|
||||
await centralCore.registerProject({
|
||||
name: "project-1",
|
||||
path: project1,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
await centralCore.registerProject({
|
||||
name: "project-2",
|
||||
path: project2,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
await expect(
|
||||
TaskStore.getOrCreateForProject(undefined, centralCore)
|
||||
).rejects.toThrow("Multiple projects registered");
|
||||
});
|
||||
|
||||
|
||||
it("should fall back to process.cwd() legacy mode against the current .fusion path", async () => {
|
||||
const projectDir = join(tempDir, "legacy-project");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
createFakeFusionProject(projectDir);
|
||||
process.chdir(projectDir);
|
||||
|
||||
const centralDb = join(tempDir, "fusion-central.db");
|
||||
await centralCore.close();
|
||||
rmSync(centralDb, { force: true });
|
||||
centralCore = new CentralCore(tempDir);
|
||||
|
||||
const store = await TaskStore.getOrCreateForProject(undefined, centralCore);
|
||||
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
const task = await store.createTask({ description: "legacy task" });
|
||||
expect(task.id).toBe("FN-001");
|
||||
expect(existsSync(join(projectDir, ".fusion", "kb.db"))).toBe(true);
|
||||
expect(existsSync(join(projectDir, ".fusion", "tasks", task.id, "task.json"))).toBe(true);
|
||||
});
|
||||
|
||||
it("should throw when project ID not found", async () => {
|
||||
await expect(
|
||||
TaskStore.getOrCreateForProject("non-existent-project", centralCore)
|
||||
).rejects.toThrow('Project "non-existent-project" not found');
|
||||
});
|
||||
|
||||
it("should find project by exact registered name", async () => {
|
||||
const projectDir = join(tempDir, "Casey");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
await centralCore.registerProject({
|
||||
name: "Casey",
|
||||
path: projectDir,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
const store = await TaskStore.getOrCreateForProject("Casey", centralCore);
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
});
|
||||
|
||||
it("should auto-initialize central core if not provided", async () => {
|
||||
const projectDir = join(tempDir, "my-project");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
// Register a project
|
||||
const { id: projectId } = await centralCore.registerProject({
|
||||
name: "my-project",
|
||||
path: projectDir,
|
||||
isolationMode: "in-process",
|
||||
});
|
||||
|
||||
// Pass the central core explicitly to ensure it uses the right database
|
||||
const store = await TaskStore.getOrCreateForProject(projectId, centralCore);
|
||||
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
});
|
||||
});
|
||||
|
||||
describe("existing constructor", () => {
|
||||
it("should still support direct TaskStore construction", async () => {
|
||||
const projectDir = join(tempDir, "direct-project");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
// Direct construction should still work
|
||||
const store = new TaskStore(projectDir, join(projectDir, ".fusion-global-settings"));
|
||||
await store.init();
|
||||
|
||||
expect(store).toBeInstanceOf(TaskStore);
|
||||
|
||||
// Should be able to create tasks
|
||||
const task = await store.createTask({
|
||||
description: "Test task",
|
||||
column: "triage",
|
||||
});
|
||||
|
||||
expect(task.id).toBeDefined();
|
||||
expect(task.description).toBe("Test task");
|
||||
});
|
||||
});
|
||||
|
||||
describe("events without central core", () => {
|
||||
it("should emit events in single-project mode", async () => {
|
||||
const projectDir = join(tempDir, "event-test");
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
|
||||
const store = new TaskStore(projectDir, join(projectDir, ".fusion-global-settings"));
|
||||
await store.init();
|
||||
|
||||
const taskCreatedListener = vi.fn();
|
||||
store.on("task:created", taskCreatedListener);
|
||||
|
||||
await store.createTask({
|
||||
description: "Event test task",
|
||||
column: "triage",
|
||||
});
|
||||
|
||||
expect(taskCreatedListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,14 +16,14 @@ function sleep(ms: number): Promise<void> {
|
||||
|
||||
describe("TaskStore task documents", () => {
|
||||
let rootDir: string;
|
||||
let kbDir: string;
|
||||
let fusionDir: string;
|
||||
let db: Database;
|
||||
let store: TaskStore;
|
||||
|
||||
beforeEach(async () => {
|
||||
rootDir = makeTmpDir();
|
||||
kbDir = join(rootDir, ".fusion");
|
||||
db = new Database(kbDir);
|
||||
fusionDir = join(rootDir, ".fusion");
|
||||
db = new Database(fusionDir);
|
||||
db.init();
|
||||
store = new TaskStore(rootDir, join(rootDir, ".fusion-global-settings"));
|
||||
await store.init();
|
||||
|
||||
Reference in New Issue
Block a user