Files
AgentCompany/tests/unit/workspace-admin.test.ts
T

62 lines
4.1 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { mkdirSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
import { DatabaseConnection } from "../../src/main/database/connection";
import { OrganizationService } from "../../src/main/services/organization-service";
import { deleteWorkspace, setPrimaryWorkspace, updateWorkspace } from "../../src/main/services/workspace-admin";
import { RISK_LEVEL, TASK_PRIORITY, TASK_STATUS } from "../../src/shared/domain";
describe("managing a workspace after adding it", () => {
test("can be renamed, pointed at a moved folder, relinked, made primary, and removed once nothing open uses it", () => {
const connection = new DatabaseConnection(join(tmpdir(), `ac-workspace-admin-${randomUUID()}.db`));
const db = connection.db;
const organization = new OrganizationService(db);
const folder = (name: string) => {
const path = join(tmpdir(), `ac-ws-${name}-${randomUUID()}`);
mkdirSync(path, { recursive: true });
return path;
};
let orgId = "";
let wsA = "";
let wsB = "";
let projectId = "";
connection.transaction(() => {
orgId = organization.createOrganization({ organizationName: "Acme", employeeName: "Yang", mission: "m", goal: "g" });
projectId = organization.createProject({ orgId, goalId: null, name: "App", description: "d", leadAgentId: null });
wsA = organization.createWorkspace({ orgId, name: "A", localPath: folder("a"), projectId: null });
wsB = organization.createWorkspace({ orgId, name: "B", localPath: folder("b"), projectId: null });
});
// A run queued for the old folder must not run against the new one.
db.prepare("insert into task_runs(id, org_id, task_id, trigger, state, workspace_mount_json, created_at) values ('run_q', ?, ?, 'manual', 'queued', ?, ?)")
.run(orgId, organization.createTask({
orgId, title: "Queued", description: "d", status: TASK_STATUS.BACKLOG, assigneeAgentId: null, risk: RISK_LEVEL.LOW,
projectId: null, goalId: null, workspaceId: wsA, priority: TASK_PRIORITY.NORMAL, requiresFilesystem: false,
}), JSON.stringify([{ workspaceId: wsA, mode: "read_only" }]), new Date().toISOString());
const moved = folder("moved");
updateWorkspace(db, { orgId, workspaceId: wsA, name: "Main repo", localPath: moved, projectId });
expect(db.prepare("select name, local_path, project_id from workspaces where id = ?").get(wsA)).toEqual({ name: "Main repo", local_path: moved, project_id: projectId });
expect(db.prepare("select state from task_runs where id = 'run_q'").get()).toEqual({ state: "superseded" });
expect(() => updateWorkspace(db, { orgId, workspaceId: wsA, name: "Main repo", localPath: join(tmpdir(), "ac-missing-folder"), projectId: null }))
.toThrow("is not a folder on this computer.");
expect(() => updateWorkspace(db, { orgId, workspaceId: wsA, name: " ", localPath: moved, projectId: null })).toThrow("The workspace needs a name.");
setPrimaryWorkspace(db, orgId, wsB);
expect(db.prepare("select id from workspaces where org_id = ? and is_primary = 1").all(orgId)).toEqual([{ id: wsB }]);
expect(() => deleteWorkspace(db, orgId, wsB)).toThrow("This is the primary workspace; make another one primary first.");
const taskId = organization.createTask({
orgId, title: "Build", description: "d", status: TASK_STATUS.IN_PROGRESS, assigneeAgentId: null, risk: RISK_LEVEL.LOW,
projectId: null, goalId: null, workspaceId: wsA, priority: TASK_PRIORITY.NORMAL, requiresFilesystem: false,
});
expect(() => deleteWorkspace(db, orgId, wsA)).toThrow("2 open tasks use this workspace; move them to another workspace first.");
db.prepare("update tasks set status = 'done' where workspace_id = ?").run(wsA);
db.prepare("update tasks set status = 'done' where id = ?").run(taskId);
deleteWorkspace(db, orgId, wsA);
expect(db.prepare("select count(*) as n from workspaces where id = ?").get(wsA)).toEqual({ n: 0 });
expect(db.prepare("select workspace_id from tasks where id = ?").get(taskId)).toEqual({ workspace_id: null });
connection.close();
});
});