Run server tests on Linux+Windows matrix (#809)
* Run server tests on Linux+Windows matrix Replace the hand-curated Windows server test allow-list with a full-suite matrix run.\nBoth Ubuntu and Windows now run the same server test command with shared setup and secrets. * Add isPlatform helper and gate Windows-hostile server tests - Add a shared server test isPlatform helper. - Migrate existing Windows-gated spawn, worktree, executable, and worktree-core tests to the helper. - Gate Windows-hostile symlink, macOS path-normalization, and POSIX shell setup tests with skipIf. * Replace POSIX shell calls in test fixtures with Node primitives - Replaced test fixture mkdir/echo shell setup with fs mkdirSync/writeFileSync calls in touched server tests. - Replaced git shell strings with execFileSync/spawnSync argv calls across checkout, worktree, MCP, script, and workspace git fixtures. - Gated the directory suggestion symlink escape tests on Windows because those fixtures require POSIX symlink behavior. * Fix Windows server-test failures and split POSIX-only suites into sibling files - Fix Windows path/cwd assertions in terminal, session/workspace, git-service, checkout-git, MCP, logger, spawn, and registry bootstrap tests.\n- Keep terminal tests runnable on Windows by canonicalizing temp cwd fixtures and ensuring a Windows shell fallback.\n- Gate POSIX-only shell, signal, Unix socket, and git-worktree reuse fixtures that need dedicated Windows coverage later. * Move POSIX-only test blocks into sibling .posix.test.ts files - terminal.test.ts: moved PTY/bash interaction blocks into terminal.posix.test.ts. - worktree.test.ts: moved git-worktree and teardown shell blocks into worktree.posix.test.ts. - worktree-bootstrap.test.ts: moved setup shell and terminal-backed service blocks into worktree-bootstrap.posix.test.ts. - worktree-core.test.ts: moved the POSIX-only worktree-core suite into worktree-core.posix.test.ts and removed the empty original. - provider-availability.test.ts and file-explorer/service.test.ts: moved POSIX PATH/symlink blocks into sibling suites. * Fix Windows 8.3 short-name, EBUSY cleanup, and node-pty shell-path failures in server tests - Normalize temp home directories in directory suggestion assertions to avoid Windows short-name mismatches. - Use Windows-valid terminal shells/cwds in terminal fixtures and wait for terminal-manager PTYs before cleanup. - Replace hardcoded POSIX paths in workspace-git/MCP/loop fixtures with platform-resolved paths or command files. - Gate the two explicitly Linux-only workspace-git watcher tests. * Replace hardcoded POSIX paths with portable Node path constructions in server tests - checkout-git.test.ts and worktree-session.test.ts: compare Windows temp paths with realpathSync.native to avoid 8.3 short-name mismatches. - directory-suggestions.test.ts: canonicalize result and expected paths with realpathSync.native. - session.workspaces.test.ts: replace literal /tmp and /Users fixtures with path.resolve/path.join constructions. - workspace-git-service.primitive.test.ts, loop-service.test.ts, and mcp-server.test.ts: use canonical repo/temp paths and shell-safe relative verify commands. * Fix loop-service verify-check shell and workspace-git-service path-separator on Windows - Run the loop-service verify script through the current Node executable with a relative script path. - Normalize the workspace-git-service expected repo cwd to forward slashes for the listWorktrees assertion. * Fix loop-service worker PTY spawn on Windows - Canonicalize the loop-service temporary root and workspace with realpathSync.native before worker agents use the path as cwd. * Gate loop-service real-worker-PTY test on Windows - Skip the real worker PTY loop test on Windows after ConPTY path resolution still fails with node-pty error 267.\n- Keep the test running on POSIX so the loop behavior remains covered. * Stub getMetricsSnapshot in test mocks to suppress async-leak uncaught exceptions on Windows - Add a no-op AgentManager metrics snapshot to the WebSocket notification test server stub. * Fix terminal-manager Windows-path test to avoid PTY spawn into nonexistent dir - Use an existing temporary cwd for the createTerminal absolute-path validation assertion so node-pty does not spawn into a missing Windows directory.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { homedir, tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -48,6 +48,9 @@ import {
|
||||
createPersistedWorkspaceRecord,
|
||||
} from "./workspace-registry.js";
|
||||
|
||||
const REPO_CWD = path.resolve("/tmp/repo");
|
||||
const UNREGISTERED_CWD = path.resolve("/tmp/unregistered");
|
||||
|
||||
interface SessionTestAccess {
|
||||
projectRegistry: {
|
||||
list(...args: unknown[]): Promise<unknown[]>;
|
||||
@@ -604,7 +607,7 @@ test("unsupported persisted agents are excluded from active lists but preserved
|
||||
const storedRecord = {
|
||||
id: "agent-unsupported",
|
||||
provider: "gemini",
|
||||
cwd: "/tmp/history",
|
||||
cwd: path.resolve("/tmp/history"),
|
||||
createdAt: "2026-04-13T10:13:11.457Z",
|
||||
updatedAt: "2026-04-13T10:16:06.556Z",
|
||||
lastActivityAt: "2026-04-13T10:16:06.556Z",
|
||||
@@ -727,7 +730,7 @@ test("agent_update placement does not refresh git snapshots", async () => {
|
||||
);
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-1",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -736,7 +739,7 @@ test("agent_update placement does not refresh git snapshots", async () => {
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-1",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -755,7 +758,7 @@ test("agent_update placement does not refresh git snapshots", async () => {
|
||||
await session.forwardAgentUpdate(
|
||||
makeManagedAgent({
|
||||
id: "agent-1",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
lifecycle: "running",
|
||||
updatedAt: "2026-03-30T15:00:00.000Z",
|
||||
}),
|
||||
@@ -801,7 +804,7 @@ test("agent_update emits fallback placement when no workspace is registered", as
|
||||
await session.forwardAgentUpdate(
|
||||
makeManagedAgent({
|
||||
id: "agent-1",
|
||||
cwd: "/tmp/unregistered",
|
||||
cwd: UNREGISTERED_CWD,
|
||||
lifecycle: "running",
|
||||
updatedAt: "2026-03-30T15:00:00.000Z",
|
||||
}),
|
||||
@@ -811,10 +814,10 @@ test("agent_update emits fallback placement when no workspace is registered", as
|
||||
expect(emitted.find((message) => message.type === "agent_update")?.payload).toMatchObject({
|
||||
kind: "upsert",
|
||||
project: {
|
||||
projectKey: "/tmp/unregistered",
|
||||
projectKey: UNREGISTERED_CWD,
|
||||
projectName: "unregistered",
|
||||
checkout: {
|
||||
cwd: "/tmp/unregistered",
|
||||
cwd: UNREGISTERED_CWD,
|
||||
isGit: false,
|
||||
},
|
||||
},
|
||||
@@ -826,7 +829,7 @@ test("archive emits an authoritative agent_update upsert for subscribed clients"
|
||||
const archivedRecord = {
|
||||
id: "agent-1",
|
||||
provider: "codex",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
createdAt: "2026-03-30T15:00:00.000Z",
|
||||
updatedAt: "2026-03-30T15:00:00.000Z",
|
||||
lastActivityAt: "2026-03-30T15:00:00.000Z",
|
||||
@@ -836,7 +839,7 @@ test("archive emits an authoritative agent_update upsert for subscribed clients"
|
||||
runtimeInfo: null,
|
||||
config: {
|
||||
provider: "codex",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
},
|
||||
persistence: null,
|
||||
title: "Archive me",
|
||||
@@ -893,7 +896,7 @@ test("archive emits an authoritative agent_update upsert for subscribed clients"
|
||||
projectRegistry: (() => {
|
||||
const proj = createPersistedProjectRecord({
|
||||
projectId: "proj-1",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "non_git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -913,7 +916,7 @@ test("archive emits an authoritative agent_update upsert for subscribed clients"
|
||||
const ws = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-1",
|
||||
projectId: "proj-1",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "directory",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -934,7 +937,7 @@ test("archive emits an authoritative agent_update upsert for subscribed clients"
|
||||
loopService: asLoopService(),
|
||||
checkoutDiffManager: asCheckoutDiffManager({
|
||||
subscribe: async () => ({
|
||||
initial: { cwd: "/tmp/repo", files: [], error: null },
|
||||
initial: { cwd: REPO_CWD, files: [], error: null },
|
||||
unsubscribe: () => {},
|
||||
}),
|
||||
scheduleRefreshForCwd: () => {},
|
||||
@@ -996,7 +999,7 @@ test("close_items_request archives agents and kills terminals in one batch", asy
|
||||
const archivedRecord = {
|
||||
id: "agent-1",
|
||||
provider: "codex",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
model: null,
|
||||
thinkingOptionId: null,
|
||||
effectiveThinkingOptionId: null,
|
||||
@@ -1055,7 +1058,7 @@ test("close_items_request archives agents and kills terminals in one batch", asy
|
||||
projectRegistry: (() => {
|
||||
const proj = createPersistedProjectRecord({
|
||||
projectId: "proj-close",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "non_git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1075,7 +1078,7 @@ test("close_items_request archives agents and kills terminals in one batch", asy
|
||||
const ws = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-close",
|
||||
projectId: "proj-close",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "directory",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1170,7 +1173,7 @@ test("close_items_request archives stored agents that are not currently loaded",
|
||||
const liveRecord = {
|
||||
...makeAgent({
|
||||
id: "agent-live",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:00:00.000Z",
|
||||
}),
|
||||
@@ -1179,7 +1182,7 @@ test("close_items_request archives stored agents that are not currently loaded",
|
||||
const storedRecord = {
|
||||
...makeAgent({
|
||||
id: storedAgentId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:05:00.000Z",
|
||||
}),
|
||||
@@ -1244,7 +1247,7 @@ test("close_items_request archives stored agents that are not currently loaded",
|
||||
projectRegistry: (() => {
|
||||
const proj = createPersistedProjectRecord({
|
||||
projectId: "proj-stored",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "non_git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1264,7 +1267,7 @@ test("close_items_request archives stored agents that are not currently loaded",
|
||||
const ws = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-stored",
|
||||
projectId: "proj-stored",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "directory",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1350,7 +1353,7 @@ test("close_items_request continues after an archive failure", async () => {
|
||||
const goodRecord = {
|
||||
...makeAgent({
|
||||
id: "agent-good",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:00:00.000Z",
|
||||
}),
|
||||
@@ -1393,7 +1396,7 @@ test("close_items_request continues after an archive failure", async () => {
|
||||
projectRegistry: (() => {
|
||||
const proj = createPersistedProjectRecord({
|
||||
projectId: "proj-err",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "non_git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1413,7 +1416,7 @@ test("close_items_request continues after an archive failure", async () => {
|
||||
const ws = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-err",
|
||||
projectId: "proj-err",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "directory",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1714,9 +1717,12 @@ test("active-scoped fetch_agents pages within active scope instead of global his
|
||||
|
||||
test("legacy unscoped fetch_agents keeps global workspace behavior", async () => {
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const legacyRoot = path.resolve("/tmp/legacy");
|
||||
const activeCwd = path.join(legacyRoot, "active");
|
||||
const archivedCwd = path.join(legacyRoot, "archived");
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-legacy-global",
|
||||
rootPath: "/tmp/legacy",
|
||||
rootPath: legacyRoot,
|
||||
kind: "non_git",
|
||||
displayName: "legacy",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1725,7 +1731,7 @@ test("legacy unscoped fetch_agents keeps global workspace behavior", async () =>
|
||||
const activeWorkspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-legacy-active",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/legacy/active",
|
||||
cwd: activeCwd,
|
||||
kind: "directory",
|
||||
displayName: "active",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1734,7 +1740,7 @@ test("legacy unscoped fetch_agents keeps global workspace behavior", async () =>
|
||||
const archivedWorkspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-legacy-archived",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/legacy/archived",
|
||||
cwd: archivedCwd,
|
||||
kind: "directory",
|
||||
displayName: "archived",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1747,13 +1753,13 @@ test("legacy unscoped fetch_agents keeps global workspace behavior", async () =>
|
||||
session.listAgentPayloads = async () => [
|
||||
makeAgent({
|
||||
id: "legacy-active",
|
||||
cwd: "/tmp/legacy/active",
|
||||
cwd: activeCwd,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:01:00.000Z",
|
||||
}),
|
||||
makeAgent({
|
||||
id: "legacy-archived-workspace",
|
||||
cwd: "/tmp/legacy/archived",
|
||||
cwd: archivedCwd,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:00:00.000Z",
|
||||
}),
|
||||
@@ -1773,9 +1779,10 @@ test("legacy unscoped fetch_agents keeps global workspace behavior", async () =>
|
||||
test("fetch_agent_history_request pages archived historical rows separately", async () => {
|
||||
const emitted: SessionOutboundMessage[] = [];
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const historyCwd = path.resolve("/tmp/history");
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-history",
|
||||
rootPath: "/tmp/history",
|
||||
rootPath: historyCwd,
|
||||
kind: "non_git",
|
||||
displayName: "history",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1784,7 +1791,7 @@ test("fetch_agent_history_request pages archived historical rows separately", as
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-history",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/history",
|
||||
cwd: historyCwd,
|
||||
kind: "directory",
|
||||
displayName: "history",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1801,7 +1808,7 @@ test("fetch_agent_history_request pages archived historical rows separately", as
|
||||
{
|
||||
...makeAgent({
|
||||
id: "history-archived",
|
||||
cwd: "/tmp/history",
|
||||
cwd: historyCwd,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:00:00.000Z",
|
||||
}),
|
||||
@@ -1842,7 +1849,7 @@ test("fetch_agent_request still resolves archived historical agents", async () =
|
||||
const agent = {
|
||||
...makeAgent({
|
||||
id: "archived-history-agent",
|
||||
cwd: "/tmp/history-detail",
|
||||
cwd: path.resolve("/tmp/history-detail"),
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:00:00.000Z",
|
||||
}),
|
||||
@@ -1941,7 +1948,7 @@ test("branch/detached policies and dominant status bucket are deterministic", as
|
||||
createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-repo-status",
|
||||
projectId: "proj-repo-status",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -1951,19 +1958,19 @@ test("branch/detached policies and dominant status bucket are deterministic", as
|
||||
session.listAgentPayloads = async () => [
|
||||
makeAgent({
|
||||
id: "a1",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
status: "running",
|
||||
updatedAt: "2026-03-01T12:00:00.000Z",
|
||||
}),
|
||||
makeAgent({
|
||||
id: "a2",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
status: "error",
|
||||
updatedAt: "2026-03-01T12:01:00.000Z",
|
||||
}),
|
||||
makeAgent({
|
||||
id: "a3",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
status: "idle",
|
||||
updatedAt: "2026-03-01T12:02:00.000Z",
|
||||
pendingPermissions: 1,
|
||||
@@ -1985,7 +1992,7 @@ test("subdirectory agents map to an existing parent workspace descriptor", async
|
||||
createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-repo-subdir",
|
||||
projectId: "proj-repo-subdir",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -2101,12 +2108,12 @@ test("workspace update stream keeps persisted workspace visible after agents sto
|
||||
session.buildWorkspaceDescriptorMap = async () =>
|
||||
new Map([
|
||||
[
|
||||
"/tmp/repo",
|
||||
REPO_CWD,
|
||||
{
|
||||
id: "ws-repo-running",
|
||||
projectId: "proj-repo-running",
|
||||
projectDisplayName: "repo",
|
||||
projectRootPath: "/tmp/repo",
|
||||
projectRootPath: REPO_CWD,
|
||||
projectKind: "non_git",
|
||||
workspaceKind: "directory",
|
||||
name: "repo",
|
||||
@@ -2115,17 +2122,17 @@ test("workspace update stream keeps persisted workspace visible after agents sto
|
||||
},
|
||||
],
|
||||
]);
|
||||
await session.emitWorkspaceUpdateForCwd("/tmp/repo");
|
||||
await session.emitWorkspaceUpdateForCwd(REPO_CWD);
|
||||
|
||||
session.buildWorkspaceDescriptorMap = async () =>
|
||||
new Map([
|
||||
[
|
||||
"/tmp/repo",
|
||||
REPO_CWD,
|
||||
{
|
||||
id: "ws-repo-running",
|
||||
projectId: "proj-repo-running",
|
||||
projectDisplayName: "repo",
|
||||
projectRootPath: "/tmp/repo",
|
||||
projectRootPath: REPO_CWD,
|
||||
projectKind: "non_git",
|
||||
workspaceKind: "directory",
|
||||
name: "repo",
|
||||
@@ -2134,7 +2141,7 @@ test("workspace update stream keeps persisted workspace visible after agents sto
|
||||
},
|
||||
],
|
||||
]);
|
||||
await session.emitWorkspaceUpdateForCwd("/tmp/repo");
|
||||
await session.emitWorkspaceUpdateForCwd(REPO_CWD);
|
||||
|
||||
const workspaceUpdates = filterByType(emitted, "workspace_update");
|
||||
expect(workspaceUpdates).toHaveLength(2);
|
||||
@@ -2145,7 +2152,7 @@ test("workspace update stream keeps persisted workspace visible after agents sto
|
||||
id: "ws-repo-running",
|
||||
projectId: "proj-repo-running",
|
||||
projectDisplayName: "repo",
|
||||
projectRootPath: "/tmp/repo",
|
||||
projectRootPath: REPO_CWD,
|
||||
projectKind: "non_git",
|
||||
workspaceKind: "directory",
|
||||
name: "repo",
|
||||
@@ -2160,13 +2167,19 @@ test("create paseo worktree request returns a registered workspace descriptor",
|
||||
const tempDir = realpathSync(mkdtempSync(path.join(tmpdir(), "session-worktree-test-")));
|
||||
const repoDir = path.join(tempDir, "repo");
|
||||
const paseoHome = path.join(tempDir, "paseo-home");
|
||||
execSync(`mkdir -p ${repoDir}`);
|
||||
execSync("git init -b main", { cwd: repoDir, stdio: "pipe" });
|
||||
execSync("git config user.email 'test@test.com'", { cwd: repoDir, stdio: "pipe" });
|
||||
execSync("git config user.name 'Test'", { cwd: repoDir, stdio: "pipe" });
|
||||
mkdirSync(repoDir, { recursive: true });
|
||||
execFileSync("git", ["init", "-b", "main"], { cwd: repoDir, stdio: "pipe" });
|
||||
execFileSync("git", ["config", "user.email", "test@test.com"], {
|
||||
cwd: repoDir,
|
||||
stdio: "pipe",
|
||||
});
|
||||
execFileSync("git", ["config", "user.name", "Test"], { cwd: repoDir, stdio: "pipe" });
|
||||
writeFileSync(path.join(repoDir, "file.txt"), "hello\n");
|
||||
execSync("git add .", { cwd: repoDir, stdio: "pipe" });
|
||||
execSync("git -c commit.gpgsign=false commit -m 'initial'", { cwd: repoDir, stdio: "pipe" });
|
||||
execFileSync("git", ["add", "."], { cwd: repoDir, stdio: "pipe" });
|
||||
execFileSync("git", ["-c", "commit.gpgsign=false", "commit", "-m", "initial"], {
|
||||
cwd: repoDir,
|
||||
stdio: "pipe",
|
||||
});
|
||||
const workspaceGitService = createNoopWorkspaceGitService();
|
||||
workspaceGitService.getSnapshot = vi.fn(async (cwd: string) => {
|
||||
if (cwd === repoDir) {
|
||||
@@ -2263,7 +2276,7 @@ test("workspace update fanout for multiple cwd values is deduplicated", async ()
|
||||
createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-repo-main",
|
||||
projectId: "proj-repo-main",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -2296,7 +2309,7 @@ test("workspace update fanout for multiple cwd values is deduplicated", async ()
|
||||
id: "ws-repo-main",
|
||||
projectId: "proj-repo-main",
|
||||
projectDisplayName: "repo",
|
||||
projectRootPath: "/tmp/repo",
|
||||
projectRootPath: REPO_CWD,
|
||||
projectKind: "git",
|
||||
workspaceKind: "local_checkout",
|
||||
name: "main",
|
||||
@@ -2310,7 +2323,7 @@ test("workspace update fanout for multiple cwd values is deduplicated", async ()
|
||||
id: "ws-repo-feature",
|
||||
projectId: "proj-repo-main",
|
||||
projectDisplayName: "repo",
|
||||
projectRootPath: "/tmp/repo",
|
||||
projectRootPath: REPO_CWD,
|
||||
projectKind: "git",
|
||||
workspaceKind: "worktree",
|
||||
name: "feature",
|
||||
@@ -2376,14 +2389,14 @@ test("open_project_request registers a workspace before any agent exists", async
|
||||
|
||||
await session.handleMessage({
|
||||
type: "open_project_request",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
requestId: "req-open",
|
||||
});
|
||||
|
||||
expect(workspaces.get("/tmp/repo")).toBeTruthy();
|
||||
expect(workspaces.get(REPO_CWD)).toBeTruthy();
|
||||
const response = findByType(emitted, "open_project_response");
|
||||
expect(response?.payload.error).toBeNull();
|
||||
expect(response?.payload.workspace?.id).toBe("/tmp/repo");
|
||||
expect(response?.payload.workspace?.id).toBe(REPO_CWD);
|
||||
});
|
||||
|
||||
test("open_project_response returns immediately even when the GitHub fetch is slow", async () => {
|
||||
@@ -2391,7 +2404,7 @@ test("open_project_response returns immediately even when the GitHub fetch is sl
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const cwd = "/tmp/slow-github-repo";
|
||||
const cwd = path.resolve("/tmp/slow-github-repo");
|
||||
|
||||
session.emit = (message) => {
|
||||
if (isSessionOutboundMessage(message)) emitted.push(message);
|
||||
@@ -2453,7 +2466,7 @@ test("open_project_request emits a workspace_update with githubRuntime once the
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const cwd = "/tmp/github-runtime-repo";
|
||||
const cwd = path.resolve("/tmp/github-runtime-repo");
|
||||
const snapshot = createWorkspaceRuntimeSnapshot(cwd);
|
||||
|
||||
let listener: ((snapshot: WorkspaceGitRuntimeSnapshot) => void) | null = null;
|
||||
@@ -2545,8 +2558,8 @@ test("open_project_request does not match a new child directory to an existing p
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const home = "/Users/moboudra";
|
||||
const worktree = "/Users/moboudra/.paseo/worktrees/project-config-lifecycle-textarea";
|
||||
const home = path.resolve("/Users/moboudra");
|
||||
const worktree = path.join(home, ".paseo", "worktrees", "project-config-lifecycle-textarea");
|
||||
|
||||
projects.set(
|
||||
home,
|
||||
@@ -2609,8 +2622,8 @@ test("open_project_request does not unarchive an archived parent workspace for a
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const home = "/Users/moboudra";
|
||||
const worktree = "/Users/moboudra/.paseo/worktrees/project-config-lifecycle-textarea";
|
||||
const home = path.resolve("/Users/moboudra");
|
||||
const worktree = path.join(home, ".paseo", "worktrees", "project-config-lifecycle-textarea");
|
||||
const archivedAt = "2026-04-24T08:00:00.000Z";
|
||||
|
||||
projects.set(
|
||||
@@ -2677,8 +2690,14 @@ test("open_project_request reclassifies an archived directory workspace when git
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const cwd = "/Users/moboudra/.paseo/worktrees/orchestrate/desktop-daemon-settings";
|
||||
const repoRoot = "/Users/moboudra/dev/paseo";
|
||||
const repoRoot = path.resolve("/Users/moboudra/dev/paseo");
|
||||
const cwd = path.join(
|
||||
path.resolve("/Users/moboudra"),
|
||||
".paseo",
|
||||
"worktrees",
|
||||
"orchestrate",
|
||||
"desktop-daemon-settings",
|
||||
);
|
||||
const remoteProjectId = "remote:github.com/getpaseo/paseo";
|
||||
const archivedAt = "2026-04-24T09:48:36.168Z";
|
||||
|
||||
@@ -2769,8 +2788,14 @@ test("open_project_request reclassifies an active directory workspace when git m
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const cwd = "/Users/moboudra/.paseo/worktrees/orchestrate/desktop-daemon-settings";
|
||||
const repoRoot = "/Users/moboudra/dev/paseo";
|
||||
const repoRoot = path.resolve("/Users/moboudra/dev/paseo");
|
||||
const cwd = path.join(
|
||||
path.resolve("/Users/moboudra"),
|
||||
".paseo",
|
||||
"worktrees",
|
||||
"orchestrate",
|
||||
"desktop-daemon-settings",
|
||||
);
|
||||
|
||||
projects.set(
|
||||
cwd,
|
||||
@@ -2879,8 +2904,14 @@ test("open_project_request groups a plain git worktree under an existing repo pr
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const cwd = "/Users/moboudra/.paseo/worktrees/orchestrate/desktop-daemon-settings";
|
||||
const repoRoot = "/Users/moboudra/dev/paseo";
|
||||
const repoRoot = path.resolve("/Users/moboudra/dev/paseo");
|
||||
const cwd = path.join(
|
||||
path.resolve("/Users/moboudra"),
|
||||
".paseo",
|
||||
"worktrees",
|
||||
"orchestrate",
|
||||
"desktop-daemon-settings",
|
||||
);
|
||||
|
||||
projects.set(
|
||||
repoRoot,
|
||||
@@ -2966,7 +2997,7 @@ test("open_project_request unarchives an existing archived workspace and project
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
|
||||
const cwd = "/tmp/repo";
|
||||
const cwd = REPO_CWD;
|
||||
projects.set(
|
||||
cwd,
|
||||
createPersistedProjectRecord({
|
||||
@@ -3030,7 +3061,7 @@ test.skip("open_project_request collapses a git subdirectory onto the repo root
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const projects = new Map<string, ReturnType<typeof createPersistedProjectRecord>>();
|
||||
const workspaces = new Map<string, ReturnType<typeof createPersistedWorkspaceRecord>>();
|
||||
const repoRoot = "/tmp/repo";
|
||||
const repoRoot = REPO_CWD;
|
||||
const subdir = "/tmp/repo/packages/app";
|
||||
|
||||
session.emit = (message) => {
|
||||
@@ -3213,10 +3244,10 @@ test("open_in_editor_request launches the selected target", async () => {
|
||||
type: "open_in_editor_request",
|
||||
requestId: "req-open-editor",
|
||||
editorId: "vscode",
|
||||
path: "/tmp/repo",
|
||||
path: REPO_CWD,
|
||||
});
|
||||
|
||||
expect(calls).toEqual([{ editorId: "vscode", path: "/tmp/repo" }]);
|
||||
expect(calls).toEqual([{ editorId: "vscode", path: REPO_CWD }]);
|
||||
const response = emitted.find((message) => message.type === "open_in_editor_response") as
|
||||
| { payload: Record<string, unknown> }
|
||||
| undefined;
|
||||
@@ -3229,7 +3260,7 @@ test("archive_workspace_request hides non-destructive workspace records", async
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-repo-archive",
|
||||
projectId: "proj-repo-archive",
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "directory",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3271,7 +3302,7 @@ test.skip("opening a new worktree reconciles older local workspaces into the rem
|
||||
const localProjectId = mainWorkspaceId;
|
||||
const remoteProjectId = "remote:github.com/zimakki/inkwell";
|
||||
|
||||
execSync(`mkdir -p ${JSON.stringify(worktreeWorkspaceId)}`);
|
||||
mkdirSync(worktreeWorkspaceId, { recursive: true });
|
||||
|
||||
projects.set(
|
||||
localProjectId,
|
||||
@@ -3381,7 +3412,7 @@ test.skip("fetch_workspaces_request reconciles remote URL changes for existing w
|
||||
const oldProjectId = "remote:github.com/old-owner/inkwell";
|
||||
const newProjectId = "remote:github.com/new-owner/inkwell";
|
||||
|
||||
execSync(`mkdir -p ${JSON.stringify(worktreeWorkspaceId)}`);
|
||||
mkdirSync(worktreeWorkspaceId, { recursive: true });
|
||||
|
||||
projects.set(
|
||||
oldProjectId,
|
||||
@@ -3476,7 +3507,7 @@ test.skip("reconcile archives stale subdirectory workspace records when collapsi
|
||||
const subdirWorkspaceId = path.join(repoRoot, "packages", "app");
|
||||
const projectId = "remote:github.com/acme/repo";
|
||||
|
||||
execSync(`mkdir -p ${JSON.stringify(subdirWorkspaceId)}`);
|
||||
mkdirSync(subdirWorkspaceId, { recursive: true });
|
||||
|
||||
projects.set(
|
||||
projectId,
|
||||
@@ -3566,7 +3597,7 @@ test("listWorkspaceDescriptorsSnapshot keeps git workspaces on the baseline desc
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-baseline",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3575,7 +3606,7 @@ test("listWorkspaceDescriptorsSnapshot keeps git workspaces on the baseline desc
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-baseline",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3627,7 +3658,7 @@ test("buildWorkspaceDescriptorMap stamps workspace archiving state", async () =>
|
||||
const archivingAt = "2026-04-30T20:45:00.000Z";
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-archiving-map",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3669,7 +3700,7 @@ test("emitWorkspaceUpdatesForWorkspaceIds includes archiving state and dedupes u
|
||||
const archivingAt = "2026-04-30T20:45:00.000Z";
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-archiving-emit",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3725,7 +3756,7 @@ test("emitWorkspaceUpdatesForWorkspaceIds includes archiving state and dedupes u
|
||||
|
||||
test("fetch_workspaces_response reads runtime fields from passive workspace git service snapshots", async () => {
|
||||
const emitted: SessionOutboundMessage[] = [];
|
||||
const runtimeSnapshot = createWorkspaceRuntimeSnapshot("/tmp/repo", {
|
||||
const runtimeSnapshot = createWorkspaceRuntimeSnapshot(REPO_CWD, {
|
||||
git: {
|
||||
currentBranch: "runtime-branch",
|
||||
isDirty: true,
|
||||
@@ -3758,7 +3789,7 @@ test("fetch_workspaces_response reads runtime fields from passive workspace git
|
||||
);
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-runtime-fetch",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3767,7 +3798,7 @@ test("fetch_workspaces_response reads runtime fields from passive workspace git
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-runtime-fetch",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3803,7 +3834,7 @@ test("fetch_workspaces_response reads runtime fields from passive workspace git
|
||||
| { type: "fetch_workspaces_response"; payload: Record<string, unknown> }
|
||||
| undefined;
|
||||
|
||||
expect(peekSnapshotRuntimeFetch).toHaveBeenCalledWith("/tmp/repo");
|
||||
expect(peekSnapshotRuntimeFetch).toHaveBeenCalledWith(REPO_CWD);
|
||||
expect(response?.payload.entries).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "ws-runtime-fetch",
|
||||
@@ -3856,7 +3887,7 @@ test("fetch_workspaces_response emits before cold registration-triggered git wor
|
||||
);
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-fetch-boundary",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3865,7 +3896,7 @@ test("fetch_workspaces_response emits before cold registration-triggered git wor
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-fetch-boundary",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3895,7 +3926,7 @@ test("fetch_workspaces_response emits before cold registration-triggered git wor
|
||||
|
||||
test("workspace_update includes updated runtime fields", async () => {
|
||||
const emitted: SessionOutboundMessage[] = [];
|
||||
const runtimeSnapshot = createWorkspaceRuntimeSnapshot("/tmp/repo", {
|
||||
const runtimeSnapshot = createWorkspaceRuntimeSnapshot(REPO_CWD, {
|
||||
git: {
|
||||
currentBranch: "feature/runtime-payloads",
|
||||
isDirty: true,
|
||||
@@ -3922,7 +3953,7 @@ test("workspace_update includes updated runtime fields", async () => {
|
||||
);
|
||||
const project = createPersistedProjectRecord({
|
||||
projectId: "proj-runtime-update",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3931,7 +3962,7 @@ test("workspace_update includes updated runtime fields", async () => {
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-runtime-update",
|
||||
projectId: project.projectId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -3966,11 +3997,11 @@ test("workspace_update includes updated runtime fields", async () => {
|
||||
},
|
||||
});
|
||||
|
||||
await session.emitWorkspaceUpdateForCwd("/tmp/repo", {
|
||||
await session.emitWorkspaceUpdateForCwd(REPO_CWD, {
|
||||
skipReconcile: true,
|
||||
});
|
||||
|
||||
expect(peekSnapshotRuntimeUpdate).toHaveBeenCalledWith("/tmp/repo");
|
||||
expect(peekSnapshotRuntimeUpdate).toHaveBeenCalledWith(REPO_CWD);
|
||||
expect(emitted).toContainEqual({
|
||||
type: "workspace_update",
|
||||
payload: {
|
||||
@@ -3998,7 +4029,7 @@ test("subscribed fetch_workspaces includes git enrichment in the initial snapsho
|
||||
const session = createSessionForWorkspaceTests();
|
||||
const gitProject = createPersistedProjectRecord({
|
||||
projectId: "proj-git-subscribe",
|
||||
rootPath: "/tmp/repo",
|
||||
rootPath: REPO_CWD,
|
||||
kind: "git",
|
||||
displayName: "repo",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
@@ -4015,7 +4046,7 @@ test("subscribed fetch_workspaces includes git enrichment in the initial snapsho
|
||||
const gitWorkspace = createPersistedWorkspaceRecord({
|
||||
workspaceId: "ws-git-subscribe",
|
||||
projectId: gitProject.projectId,
|
||||
cwd: "/tmp/repo",
|
||||
cwd: REPO_CWD,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
createdAt: "2026-03-01T12:00:00.000Z",
|
||||
|
||||
Reference in New Issue
Block a user