From ff60f5f62f38fd9bcd83ee48c1f2f7f72c48c4f3 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 11:40:18 +0200 Subject: [PATCH] test(sf): make worktree suites explicit --- .../sf/tests/worktree-sync-milestones.test.ts | 130 ++---- .../extensions/sf/tests/worktree.test.ts | 389 +++++++++--------- 2 files changed, 242 insertions(+), 277 deletions(-) diff --git a/src/resources/extensions/sf/tests/worktree-sync-milestones.test.ts b/src/resources/extensions/sf/tests/worktree-sync-milestones.test.ts index b00f404d3..b80aca163 100644 --- a/src/resources/extensions/sf/tests/worktree-sync-milestones.test.ts +++ b/src/resources/extensions/sf/tests/worktree-sync-milestones.test.ts @@ -34,7 +34,7 @@ import { } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { describe } from 'vitest'; +import { describe, test } from "vitest"; import { syncProjectRootToWorktree, syncSfStateToWorktree, @@ -51,10 +51,8 @@ function cleanup(base: string): void { rmSync(base, { recursive: true, force: true }); } -describe("worktree-sync-milestones", async () => { - // ─── 1. Milestone directory synced from main to worktree ────────────── - console.log("\n=== 1. milestone directory synced from main to worktree ==="); - { +describe("worktree-sync-milestones", () => { + test("milestone directory synced from main to worktree", () => { const mainBase = createBase("main"); const wtBase = createBase("wt"); @@ -92,11 +90,9 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); cleanup(wtBase); } - } + }); - // ─── 2. Missing slices synced ────────────────────────────────────────── - console.log("\n=== 2. missing slices within milestone are synced ==="); - { + test("missing slices within milestone are synced", () => { const mainBase = createBase("main"); const wtBase = createBase("wt"); @@ -146,11 +142,9 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); cleanup(wtBase); } - } + }); - // ─── 3. empty sf.db deleted in worktree after sync ──────────────────── - console.log("\n=== 3. empty sf.db deleted in worktree after sync ==="); - { + test("empty sf.db deleted in worktree after sync", () => { const mainBase = createBase("main"); const wtBase = createBase("wt"); @@ -176,13 +170,9 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); cleanup(wtBase); } - } + }); - // ─── 3b. non-empty sf.db preserved in worktree after sync (#2815) ─── - console.log( - "\n=== 3b. non-empty sf.db preserved in worktree after sync (#2815) ===", - ); - { + test("non-empty sf.db preserved in worktree after sync (#2815)", () => { const mainBase = createBase("main"); const wtBase = createBase("wt"); @@ -208,11 +198,9 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); cleanup(wtBase); } - } + }); - // ─── 4. No-op when paths are equal ──────────────────────────────────── - console.log("\n=== 4. no-op when paths are equal ==="); - { + test("no-op when paths are equal", () => { const base = createBase("same"); try { // Should not throw @@ -221,11 +209,9 @@ describe("worktree-sync-milestones", async () => { } finally { cleanup(base); } - } + }); - // ─── 5. No-op when milestoneId is null ──────────────────────────────── - console.log("\n=== 5. no-op when milestoneId is null ==="); - { + test("no-op when milestoneId is null", () => { const mainBase = createBase("main"); const wtBase = createBase("wt"); try { @@ -235,22 +221,18 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); cleanup(wtBase); } - } + }); - // ─── 6. Non-existent directories handled gracefully ─────────────────── - console.log("\n=== 6. non-existent directories → no-op ==="); - syncProjectRootToWorktree( - "/tmp/does-not-exist-main", - "/tmp/does-not-exist-wt", - "M001", - ); - assert.ok(true, "no crash on missing directories"); + test("non-existent directories are handled gracefully", () => { + syncProjectRootToWorktree( + "/tmp/does-not-exist-main", + "/tmp/does-not-exist-wt", + "M001", + ); + assert.ok(true, "no crash on missing directories"); + }); - // ─── 7. milestones/ directory created in worktree when missing ──────── - console.log( - "\n=== 7. milestones/ directory created in worktree when missing ===", - ); - { + test("milestones/ directory created in worktree when missing", () => { const mainBase = createBase("main"); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-sync-wt-")); @@ -296,13 +278,9 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 8. syncWorktreeStateBack recurses into tasks/ (#1678) ─────────── - console.log( - "\n=== 8. syncWorktreeStateBack copies tasks/ subdirectory (#1678) ===", - ); - { + test("syncWorktreeStateBack recurses into tasks/ (#1678)", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-back-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-back-wt-")); @@ -361,13 +339,9 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 9. syncWorktreeStateBack syncs root-level .sf/ files ────────── - console.log( - "\n=== 9. syncWorktreeStateBack syncs root-level files (REQUIREMENTS, PROJECT) ===", - ); - { + test("syncWorktreeStateBack syncs root-level .sf/ files", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-back-root-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-back-root-wt-")); @@ -439,13 +413,9 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 10. syncWorktreeStateBack syncs ALL milestone directories ───── - console.log( - "\n=== 10. syncWorktreeStateBack syncs all milestone dirs, not just current ===", - ); - { + test("syncWorktreeStateBack syncs all milestone dirs, not just current", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-back-all-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-back-all-wt-")); @@ -524,13 +494,9 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 11. Full M006→M007 transition scenario ─────────────────────────── - console.log( - "\n=== 11. complete-milestone creates next-milestone artifacts that survive sync ===", - ); - { + test("complete-milestone creates next-milestone artifacts that survive sync", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-transition-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-transition-wt-")); @@ -652,13 +618,9 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 12. syncWorktreeStateBack no-op for root files that don't exist ── - console.log( - "\n=== 12. root files not in worktree are not created in main ===", - ); - { + test("root files not in worktree are not created in main", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-back-noroot-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-back-noroot-wt-")); @@ -690,13 +652,9 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 13. syncWorktreeStateBack syncs QUEUE.md and completed-units.json (#1787) ── - console.log( - "\n=== 13. QUEUE.md and completed-units.json synced from worktree (#1787) ===", - ); - { + test("QUEUE.md and completed-units.json synced from worktree (#1787)", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-back-queue-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-back-queue-wt-")); @@ -764,13 +722,9 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); - // ─── 14. syncSfStateToWorktree syncs non-standard milestone dir names (#1547) ── - console.log( - "\n=== 14. syncSfStateToWorktree syncs non-standard milestone dir names (#1547) ===", - ); - { + test("syncSfStateToWorktree syncs non-standard milestone dir names (#1547)", () => { const mainBase = createBase("main"); const wtBase = createBase("wt"); @@ -821,13 +775,9 @@ describe("worktree-sync-milestones", async () => { cleanup(mainBase); cleanup(wtBase); } - } + }); - // ─── 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ── - console.log( - "\n=== 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ===", - ); - { + test("syncWorktreeStateBack syncs non-standard milestone dir names (#1547)", () => { const mainBase = mkdtempSync(join(tmpdir(), "sf-wt-back-custom-main-")); const wtBase = mkdtempSync(join(tmpdir(), "sf-wt-back-custom-wt-")); @@ -861,5 +811,5 @@ describe("worktree-sync-milestones", async () => { rmSync(mainBase, { recursive: true, force: true }); rmSync(wtBase, { recursive: true, force: true }); } - } + }); }); diff --git a/src/resources/extensions/sf/tests/worktree.test.ts b/src/resources/extensions/sf/tests/worktree.test.ts index 8dbc8f2ea..cbf9270f9 100644 --- a/src/resources/extensions/sf/tests/worktree.test.ts +++ b/src/resources/extensions/sf/tests/worktree.test.ts @@ -10,7 +10,7 @@ import { } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { describe } from 'vitest'; +import { afterAll, describe, test } from "vitest"; import { readIntegrationBranch } from "../git-service.ts"; import { _resetHasChangesCache } from "../native-git-bridge.ts"; import { _clearSfRootCache } from "../paths.ts"; @@ -69,126 +69,147 @@ writeFileSync( run("git add .", base); run('git commit -m "chore: init"', base); -describe("worktree", async () => { - console.log("\n=== autoCommitCurrentBranch ==="); - // Clean — should return null - const cleanResult = autoCommitCurrentBranch( - base, - "execute-task", - "M001/S01/T01", - ); - assert.deepStrictEqual(cleanResult, null, "returns null for clean repo"); +describe("worktree", () => { + afterAll(() => { + rmSync(base, { recursive: true, force: true }); + delete process.env.SF_PROJECT_ROOT; + delete process.env.SF_HOME; + _clearSfRootCache(); + _resetServiceCache(); + _resetHasChangesCache(); + }); - // Make dirty — reset the nativeHasChanges cache so the fresh dirt is detected - _resetHasChangesCache(); - writeFileSync(join(base, "dirty.txt"), "uncommitted\n", "utf-8"); - const dirtyResult = autoCommitCurrentBranch( - base, - "execute-task", - "M001/S01/T01", - ); - assert.ok(dirtyResult !== null, "returns commit message for dirty repo"); - assert.ok( - dirtyResult!.includes("M001/S01/T01"), - "commit message includes unit id", - ); - assert.deepStrictEqual( - run("git status --short", base), - "", - "repo is clean after auto-commit", - ); + test("autoCommitCurrentBranch", () => { + // Clean — should return null + const cleanResult = autoCommitCurrentBranch( + base, + "execute-task", + "M001/S01/T01", + ); + assert.deepStrictEqual(cleanResult, null, "returns null for clean repo"); - console.log("\n=== getSliceBranchName ==="); - assert.deepStrictEqual( - getSliceBranchName("M001", "S01"), - "sf/M001/S01", - "branch name format correct", - ); - assert.deepStrictEqual( - getSliceBranchName("M001", "S01", null), - "sf/M001/S01", - "null worktree = plain branch", - ); - assert.deepStrictEqual( - getSliceBranchName("M001", "S01", "my-wt"), - "sf/my-wt/M001/S01", - "worktree-namespaced branch", - ); + // Make dirty — reset the nativeHasChanges cache so the fresh dirt is detected + _resetHasChangesCache(); + writeFileSync(join(base, "dirty.txt"), "uncommitted\n", "utf-8"); + const dirtyResult = autoCommitCurrentBranch( + base, + "execute-task", + "M001/S01/T01", + ); + assert.ok(dirtyResult !== null, "returns commit message for dirty repo"); + assert.ok( + dirtyResult!.includes("M001/S01/T01"), + "commit message includes unit id", + ); + assert.deepStrictEqual( + run("git status --short", base), + "", + "repo is clean after auto-commit", + ); + }); - console.log("\n=== parseSliceBranch ==="); - const plain = parseSliceBranch("sf/M001/S01"); - assert.ok(plain !== null, "parses plain branch"); - assert.deepStrictEqual( - plain!.worktreeName, - null, - "plain branch has no worktree name", - ); - assert.deepStrictEqual(plain!.milestoneId, "M001", "plain branch milestone"); - assert.deepStrictEqual(plain!.sliceId, "S01", "plain branch slice"); + test("getSliceBranchName", () => { + assert.deepStrictEqual( + getSliceBranchName("M001", "S01"), + "sf/M001/S01", + "branch name format correct", + ); + assert.deepStrictEqual( + getSliceBranchName("M001", "S01", null), + "sf/M001/S01", + "null worktree = plain branch", + ); + assert.deepStrictEqual( + getSliceBranchName("M001", "S01", "my-wt"), + "sf/my-wt/M001/S01", + "worktree-namespaced branch", + ); + }); - const namespaced = parseSliceBranch("sf/feature-auth/M001/S01"); - assert.ok(namespaced !== null, "parses worktree-namespaced branch"); - assert.deepStrictEqual( - namespaced!.worktreeName, - "feature-auth", - "worktree name extracted", - ); - assert.deepStrictEqual( - namespaced!.milestoneId, - "M001", - "namespaced branch milestone", - ); - assert.deepStrictEqual(namespaced!.sliceId, "S01", "namespaced branch slice"); + test("parseSliceBranch", () => { + const plain = parseSliceBranch("sf/M001/S01"); + assert.ok(plain !== null, "parses plain branch"); + assert.deepStrictEqual( + plain!.worktreeName, + null, + "plain branch has no worktree name", + ); + assert.deepStrictEqual( + plain!.milestoneId, + "M001", + "plain branch milestone", + ); + assert.deepStrictEqual(plain!.sliceId, "S01", "plain branch slice"); - const invalid = parseSliceBranch("main"); - assert.deepStrictEqual(invalid, null, "non-slice branch returns null"); + const namespaced = parseSliceBranch("sf/feature-auth/M001/S01"); + assert.ok(namespaced !== null, "parses worktree-namespaced branch"); + assert.deepStrictEqual( + namespaced!.worktreeName, + "feature-auth", + "worktree name extracted", + ); + assert.deepStrictEqual( + namespaced!.milestoneId, + "M001", + "namespaced branch milestone", + ); + assert.deepStrictEqual( + namespaced!.sliceId, + "S01", + "namespaced branch slice", + ); - const worktreeBranch = parseSliceBranch("worktree/foo"); - assert.deepStrictEqual( - worktreeBranch, - null, - "worktree/ prefix is not a slice branch", - ); + const invalid = parseSliceBranch("main"); + assert.deepStrictEqual(invalid, null, "non-slice branch returns null"); - console.log("\n=== SLICE_BRANCH_RE ==="); - assert.ok(SLICE_BRANCH_RE.test("sf/M001/S01"), "regex matches plain branch"); - assert.ok( - SLICE_BRANCH_RE.test("sf/my-wt/M001/S01"), - "regex matches worktree branch", - ); - assert.ok(!SLICE_BRANCH_RE.test("main"), "regex rejects main"); - assert.ok(!SLICE_BRANCH_RE.test("sf/"), "regex rejects bare sf/"); - assert.ok( - !SLICE_BRANCH_RE.test("worktree/foo"), - "regex rejects worktree/foo", - ); + const worktreeBranch = parseSliceBranch("worktree/foo"); + assert.deepStrictEqual( + worktreeBranch, + null, + "worktree/ prefix is not a slice branch", + ); + }); - console.log("\n=== detectWorktreeName ==="); - assert.deepStrictEqual( - detectWorktreeName("/projects/myapp"), - null, - "no worktree in plain path", - ); - assert.deepStrictEqual( - detectWorktreeName("/projects/myapp/.sf/worktrees/feature-auth"), - "feature-auth", - "detects worktree name", - ); - assert.deepStrictEqual( - detectWorktreeName("/projects/myapp/.sf/worktrees/my-wt/subdir"), - "my-wt", - "detects worktree with subdir", - ); + test("SLICE_BRANCH_RE", () => { + assert.ok( + SLICE_BRANCH_RE.test("sf/M001/S01"), + "regex matches plain branch", + ); + assert.ok( + SLICE_BRANCH_RE.test("sf/my-wt/M001/S01"), + "regex matches worktree branch", + ); + assert.ok(!SLICE_BRANCH_RE.test("main"), "regex rejects main"); + assert.ok(!SLICE_BRANCH_RE.test("sf/"), "regex rejects bare sf/"); + assert.ok( + !SLICE_BRANCH_RE.test("worktree/foo"), + "regex rejects worktree/foo", + ); + }); + + test("detectWorktreeName", () => { + assert.deepStrictEqual( + detectWorktreeName("/projects/myapp"), + null, + "no worktree in plain path", + ); + assert.deepStrictEqual( + detectWorktreeName("/projects/myapp/.sf/worktrees/feature-auth"), + "feature-auth", + "detects worktree name", + ); + assert.deepStrictEqual( + detectWorktreeName("/projects/myapp/.sf/worktrees/my-wt/subdir"), + "my-wt", + "detects worktree with subdir", + ); + }); // ═══════════════════════════════════════════════════════════════════════ // Integration branch — facade-level tests // ═══════════════════════════════════════════════════════════════════════ - // ── captureIntegrationBranch on a feature branch ────────────────────── - - console.log("\n=== captureIntegrationBranch: records current branch ==="); - - { + test("captureIntegrationBranch records current branch", () => { const repo = mkdtempSync(join(tmpdir(), "sf-integ-facade-")); run("git init -b main", repo); run("git config user.name 'Pi Test'", repo); @@ -220,13 +241,9 @@ describe("worktree", async () => { ); rmSync(repo, { recursive: true, force: true }); - } + }); - // ── captureIntegrationBranch skips slice branches ───────────────────── - - console.log("\n=== captureIntegrationBranch: skips slice branches ==="); - - { + test("captureIntegrationBranch skips slice branches", () => { const repo = mkdtempSync(join(tmpdir(), "sf-integ-skip-")); run("git init -b main", repo); run("git config user.name 'Pi Test'", repo); @@ -244,13 +261,9 @@ describe("worktree", async () => { ); rmSync(repo, { recursive: true, force: true }); - } + }); - // ── setActiveMilestoneId makes getMainBranch return integration branch ─ - - console.log("\n=== setActiveMilestoneId + getMainBranch ==="); - - { + test("setActiveMilestoneId + getMainBranch", () => { const repo = mkdtempSync(join(tmpdir(), "sf-integ-main-")); run("git init -b main", repo); run("git config user.name 'Pi Test'", repo); @@ -293,77 +306,81 @@ describe("worktree", async () => { } rmSync(repo, { recursive: true, force: true }); - } + }); - // ── detectWorktreeName: symlink-resolved paths ─────────────────────────── - console.log("\n=== detectWorktreeName (symlink-resolved paths) ==="); - assert.deepStrictEqual( - detectWorktreeName("/Users/fran/.sf/projects/89e1c9ad49bf/worktrees/M001"), - "M001", - "detects milestone in symlink-resolved path", - ); - assert.deepStrictEqual( - detectWorktreeName("/Users/fran/.sf/projects/abc123/worktrees/M002/subdir"), - "M002", - "detects milestone with trailing subdir in symlink-resolved path", - ); - assert.deepStrictEqual( - detectWorktreeName("/Users/fran/.sf/projects/abc123"), - null, - "returns null for project root without worktrees segment", - ); - assert.deepStrictEqual( - detectWorktreeName("/foo/.sf/worktrees/M001"), - "M001", - "still detects direct layout path", - ); + test("detectWorktreeName with symlink-resolved paths", () => { + assert.deepStrictEqual( + detectWorktreeName( + "/Users/fran/.sf/projects/89e1c9ad49bf/worktrees/M001", + ), + "M001", + "detects milestone in symlink-resolved path", + ); + assert.deepStrictEqual( + detectWorktreeName( + "/Users/fran/.sf/projects/abc123/worktrees/M002/subdir", + ), + "M002", + "detects milestone with trailing subdir in symlink-resolved path", + ); + assert.deepStrictEqual( + detectWorktreeName("/Users/fran/.sf/projects/abc123"), + null, + "returns null for project root without worktrees segment", + ); + assert.deepStrictEqual( + detectWorktreeName("/foo/.sf/worktrees/M001"), + "M001", + "still detects direct layout path", + ); + }); - // ── resolveProjectRoot: symlink-resolved paths ────────────────────────── - console.log("\n=== resolveProjectRoot (symlink-resolved paths) ==="); + test("resolveProjectRoot with symlink-resolved paths", () => { + // BUG FIX: symlink-resolved paths that land inside ~/.sf should NOT + // resolve to the home directory. When the .git file fallback can't find + // the real project root (no git worktree metadata in these synthetic paths), + // resolveProjectRoot returns the input unchanged rather than returning ~. - // BUG FIX: symlink-resolved paths that land inside ~/.sf should NOT - // resolve to the home directory. When the .git file fallback can't find - // the real project root (no git worktree metadata in these synthetic paths), - // resolveProjectRoot returns the input unchanged rather than returning ~. + // With SF_PROJECT_ROOT env var set (layer 1 — coordinator passes it) + process.env.SF_PROJECT_ROOT = "/real/project"; + assert.deepStrictEqual( + resolveProjectRoot( + "/Users/fran/.sf/projects/89e1c9ad49bf/worktrees/M001", + ), + "/real/project", + "uses SF_PROJECT_ROOT when set", + ); + delete process.env.SF_PROJECT_ROOT; - // With SF_PROJECT_ROOT env var set (layer 1 — coordinator passes it) - process.env.SF_PROJECT_ROOT = "/real/project"; - assert.deepStrictEqual( - resolveProjectRoot("/Users/fran/.sf/projects/89e1c9ad49bf/worktrees/M001"), - "/real/project", - "uses SF_PROJECT_ROOT when set", - ); - delete process.env.SF_PROJECT_ROOT; + // Without SF_PROJECT_ROOT, direct layout still works (no ~/.sf collision) + assert.deepStrictEqual( + resolveProjectRoot("/some/repo"), + "/some/repo", + "ignores SF_PROJECT_ROOT override for non-worktree paths", + ); + delete process.env.SF_PROJECT_ROOT; - // Without SF_PROJECT_ROOT, direct layout still works (no ~/.sf collision) - assert.deepStrictEqual( - resolveProjectRoot("/some/repo"), - "/some/repo", - "ignores SF_PROJECT_ROOT override for non-worktree paths", - ); - delete process.env.SF_PROJECT_ROOT; + // Without SF_PROJECT_ROOT, direct layout still works (no ~/.sf collision) + assert.deepStrictEqual( + resolveProjectRoot("/foo/.sf/worktrees/M001"), + "/foo", + "still resolves direct layout path", + ); + assert.deepStrictEqual( + resolveProjectRoot("/some/repo"), + "/some/repo", + "returns unchanged for non-worktree path", + ); - // Without SF_PROJECT_ROOT, direct layout still works (no ~/.sf collision) - assert.deepStrictEqual( - resolveProjectRoot("/foo/.sf/worktrees/M001"), - "/foo", - "still resolves direct layout path", - ); - assert.deepStrictEqual( - resolveProjectRoot("/some/repo"), - "/some/repo", - "returns unchanged for non-worktree path", - ); + // Without SF_PROJECT_ROOT, direct layout with nested subdirs + assert.deepStrictEqual( + resolveProjectRoot("/data/.sf/worktrees/M003/nested"), + "/data", + "resolves correctly with nested subdirs after worktree name (direct layout)", + ); + }); - // Without SF_PROJECT_ROOT, direct layout with nested subdirs - assert.deepStrictEqual( - resolveProjectRoot("/data/.sf/worktrees/M003/nested"), - "/data", - "resolves correctly with nested subdirs after worktree name (direct layout)", - ); - - // Real symlink + git worktree scenario, with deep nested path from cwd - { + test("resolveProjectRoot with real symlink + git worktree", () => { const fakeHome = mkdtempSync(join(tmpdir(), "sf-home-")); const project = realpathSync(mkdtempSync(join(tmpdir(), "sf-proj-"))); const storage = join(fakeHome, ".sf", "projects", "abc123def456"); @@ -406,7 +423,5 @@ describe("worktree", async () => { rmSync(project, { recursive: true, force: true }); rmSync(fakeHome, { recursive: true, force: true }); - } - - rmSync(base, { recursive: true, force: true }); + }); });