test: add test isolation and pause wiring tests
- worktree.ts: Added _resetServiceCache() for test isolation - test-isolation.ts: Shared test isolation utilities - 5 worktree tests: Isolate from global ~/.gsd/preferences.md - 2 RTK tests: Clear GSD_RTK_DISABLED before running - pre-execution-pause-wiring.test.ts: Pause wiring integration tests - preferences.ts: Add enhanced_verification to mergePreferences
This commit is contained in:
parent
23c38807ac
commit
6d96386cc5
9 changed files with 197 additions and 10 deletions
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* Test isolation utilities for integration tests.
|
||||
*
|
||||
* Integration tests often call `mergeMilestoneToMain` and other functions that
|
||||
* load preferences. If the user's global ~/.gsd/preferences.md has
|
||||
* `git.main_branch: master`, tests fail because test repos use `main`.
|
||||
*
|
||||
* These utilities isolate tests from the user's global environment.
|
||||
*/
|
||||
|
||||
import { mkdtempSync, rmSync, realpathSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { _resetServiceCache } from "../../worktree.ts";
|
||||
import { _clearGsdRootCache } from "../../paths.ts";
|
||||
|
||||
let originalHome: string | undefined;
|
||||
let fakeHome: string | null = null;
|
||||
|
||||
/**
|
||||
* Isolate the test environment from user's global preferences.
|
||||
* Creates a fake HOME directory so loadEffectiveGSDPreferences() returns
|
||||
* empty global preferences instead of the user's ~/.gsd/preferences.md.
|
||||
*
|
||||
* Call this in a test.before() hook.
|
||||
*/
|
||||
export function isolateFromGlobalPreferences(): void {
|
||||
originalHome = process.env.HOME;
|
||||
fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-test-home-")));
|
||||
process.env.HOME = fakeHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the original HOME and clean up the fake home directory.
|
||||
*
|
||||
* Call this in a test.after() hook.
|
||||
*/
|
||||
export function restoreGlobalPreferences(): void {
|
||||
if (originalHome !== undefined) {
|
||||
process.env.HOME = originalHome;
|
||||
} else {
|
||||
delete process.env.HOME;
|
||||
}
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
if (fakeHome) {
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
fakeHome = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@ import {
|
|||
teardownAutoWorktree,
|
||||
mergeMilestoneToMain,
|
||||
} from "../auto-worktree.ts";
|
||||
import { _resetServiceCache } from "../worktree.ts";
|
||||
import { _clearGsdRootCache } from "../paths.ts";
|
||||
|
||||
function run(command: string, cwd: string): string {
|
||||
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
||||
|
|
@ -62,6 +64,13 @@ test("mergeMilestoneToMain restores cwd to project root", () => {
|
|||
const savedCwd = process.cwd();
|
||||
let tempDir = "";
|
||||
|
||||
// Isolate from user's global preferences (which may have git.main_branch set)
|
||||
const originalHome = process.env.HOME;
|
||||
const fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-fake-home-")));
|
||||
process.env.HOME = fakeHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
|
||||
try {
|
||||
tempDir = createTempRepo();
|
||||
|
||||
|
|
@ -97,9 +106,13 @@ test("mergeMilestoneToMain restores cwd to project root", () => {
|
|||
assert.ok(!existsSync(wtPath), "worktree directory removed after merge");
|
||||
} finally {
|
||||
process.chdir(savedCwd);
|
||||
process.env.HOME = originalHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
if (tempDir && existsSync(tempDir)) {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,27 @@ import { tmpdir } from "node:os";
|
|||
import { execSync } from "node:child_process";
|
||||
|
||||
import { createAutoWorktree, mergeMilestoneToMain } from "../auto-worktree.ts";
|
||||
import { _resetServiceCache } from "../worktree.ts";
|
||||
import { _clearGsdRootCache } from "../paths.ts";
|
||||
|
||||
// Isolate from user's global preferences (which may have git.main_branch set)
|
||||
let originalHome: string | undefined;
|
||||
let fakeHome: string;
|
||||
|
||||
test.before(() => {
|
||||
originalHome = process.env.HOME;
|
||||
fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-fake-home-")));
|
||||
process.env.HOME = fakeHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
process.env.HOME = originalHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
function run(cmd: string, cwd: string): string {
|
||||
return execSync(cmd, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,27 @@ import { tmpdir } from "node:os";
|
|||
import { execSync } from "node:child_process";
|
||||
|
||||
import { createAutoWorktree, mergeMilestoneToMain } from "../auto-worktree.ts";
|
||||
import { _resetServiceCache } from "../worktree.ts";
|
||||
import { _clearGsdRootCache } from "../paths.ts";
|
||||
|
||||
// Isolate from user's global preferences (which may have git.main_branch set)
|
||||
let originalHome: string | undefined;
|
||||
let fakeHome: string;
|
||||
|
||||
test.before(() => {
|
||||
originalHome = process.env.HOME;
|
||||
fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-fake-home-")));
|
||||
process.env.HOME = fakeHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
process.env.HOME = originalHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
function run(cmd: string, cwd: string): string {
|
||||
return execSync(cmd, {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ import {
|
|||
getSliceBranchName,
|
||||
autoCommitCurrentBranch,
|
||||
SLICE_BRANCH_RE,
|
||||
_resetServiceCache,
|
||||
} from "../worktree.ts";
|
||||
|
||||
import { deriveState } from "../state.ts";
|
||||
import { _clearGsdRootCache } from "../paths.ts";
|
||||
import { describe, test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
|
|
@ -74,6 +76,14 @@ run("git add .", base);
|
|||
run('git commit -m "chore: init"', base);
|
||||
|
||||
describe('worktree-integration', async () => {
|
||||
// Isolate from user's global preferences (which may have git.main_branch set).
|
||||
// Reset caches so getService() creates a fresh instance with empty preferences.
|
||||
const originalHome = process.env.HOME;
|
||||
const fakeHome = mkdtempSync(join(tmpdir(), "gsd-fake-home-"));
|
||||
process.env.HOME = fakeHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
|
||||
// ── Verify main tree baseline ──────────────────────────────────────────────
|
||||
|
||||
console.log("\n=== Main tree baseline ===");
|
||||
|
|
@ -197,4 +207,10 @@ describe('worktree-integration', async () => {
|
|||
assert.deepStrictEqual(listWorktrees(base).length, 0, "all worktrees removed");
|
||||
|
||||
rmSync(base, { recursive: true, force: true });
|
||||
|
||||
// Restore HOME and reset caches
|
||||
process.env.HOME = originalHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ import {
|
|||
resolveProjectRoot,
|
||||
setActiveMilestoneId,
|
||||
SLICE_BRANCH_RE,
|
||||
_resetServiceCache,
|
||||
} from "../worktree.ts";
|
||||
import { readIntegrationBranch } from "../git-service.ts";
|
||||
import { _resetHasChangesCache } from "../native-git-bridge.ts";
|
||||
import { _clearGsdRootCache } from "../paths.ts";
|
||||
import { describe, test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
|
|
@ -165,15 +167,30 @@ describe('worktree', async () => {
|
|||
run("git checkout -b my-feature", repo);
|
||||
captureIntegrationBranch(repo, "M001");
|
||||
|
||||
// Without milestone set, getMainBranch returns "main"
|
||||
setActiveMilestoneId(repo, null);
|
||||
assert.deepStrictEqual(getMainBranch(repo), "main",
|
||||
"getMainBranch returns main without milestone set");
|
||||
// Isolate from user's global preferences (which may have git.main_branch set).
|
||||
// Reset caches so getService() creates a fresh instance with empty preferences.
|
||||
const originalHome = process.env.HOME;
|
||||
const fakeHome = mkdtempSync(join(tmpdir(), "gsd-fake-home-"));
|
||||
process.env.HOME = fakeHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
|
||||
// With milestone set, getMainBranch returns feature branch
|
||||
setActiveMilestoneId(repo, "M001");
|
||||
assert.deepStrictEqual(getMainBranch(repo), "my-feature",
|
||||
"getMainBranch returns integration branch with milestone set");
|
||||
try {
|
||||
// Without milestone set, getMainBranch returns "main"
|
||||
setActiveMilestoneId(repo, null);
|
||||
assert.deepStrictEqual(getMainBranch(repo), "main",
|
||||
"getMainBranch returns main without milestone set");
|
||||
|
||||
// With milestone set, getMainBranch returns feature branch
|
||||
setActiveMilestoneId(repo, "M001");
|
||||
assert.deepStrictEqual(getMainBranch(repo), "my-feature",
|
||||
"getMainBranch returns integration branch with milestone set");
|
||||
} finally {
|
||||
process.env.HOME = originalHome;
|
||||
_clearGsdRootCache();
|
||||
_resetServiceCache();
|
||||
rmSync(fakeHome, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
rmSync(repo, { recursive: true, force: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,16 @@ function getService(basePath: string): GitServiceImpl {
|
|||
return cachedService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached GitServiceImpl. For testing only — forces the next
|
||||
* getService() call to re-read preferences and create a fresh instance.
|
||||
* @internal
|
||||
*/
|
||||
export function _resetServiceCache(): void {
|
||||
cachedService = null;
|
||||
cachedBasePath = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the active milestone ID on the cached GitServiceImpl.
|
||||
* This enables integration branch resolution in getMainBranch().
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import test from "node:test";
|
||||
import test, { beforeEach, afterEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
|
@ -12,6 +12,24 @@ import {
|
|||
} from "../resources/extensions/shared/rtk-session-stats.ts";
|
||||
import { createFakeRtk } from "./rtk-test-utils.ts";
|
||||
|
||||
// Store original env values for restoration
|
||||
let originalRtkDisabled: string | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
// Save and clear GSD_RTK_DISABLED so tests can use fake RTK binaries
|
||||
originalRtkDisabled = process.env.GSD_RTK_DISABLED;
|
||||
delete process.env.GSD_RTK_DISABLED;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original env
|
||||
if (originalRtkDisabled !== undefined) {
|
||||
process.env.GSD_RTK_DISABLED = originalRtkDisabled;
|
||||
} else {
|
||||
delete process.env.GSD_RTK_DISABLED;
|
||||
}
|
||||
});
|
||||
|
||||
function summary(totalCommands: number, totalInput: number, totalOutput: number, totalSaved: number, totalTimeMs = 1000) {
|
||||
return JSON.stringify({
|
||||
summary: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import test from "node:test";
|
||||
import test, { beforeEach, afterEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
|
|
@ -19,6 +19,24 @@ import {
|
|||
} from "../rtk.ts";
|
||||
import { createFakeRtk } from "./rtk-test-utils.ts";
|
||||
|
||||
// Store original env values for restoration
|
||||
let originalRtkDisabled: string | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
// Save and clear GSD_RTK_DISABLED so tests can use fake RTK binaries
|
||||
originalRtkDisabled = process.env.GSD_RTK_DISABLED;
|
||||
delete process.env.GSD_RTK_DISABLED;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original env
|
||||
if (originalRtkDisabled !== undefined) {
|
||||
process.env.GSD_RTK_DISABLED = originalRtkDisabled;
|
||||
} else {
|
||||
delete process.env.GSD_RTK_DISABLED;
|
||||
}
|
||||
});
|
||||
|
||||
test("resolveRtkAssetName maps supported release assets correctly", () => {
|
||||
assert.equal(resolveRtkAssetName("darwin", "arm64"), "rtk-aarch64-apple-darwin.tar.gz");
|
||||
assert.equal(resolveRtkAssetName("darwin", "x64"), "rtk-x86_64-apple-darwin.tar.gz");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue