- pathing.ts: use path.resolve() on POSIX instead of win32.normalize - registry.ts: replace ensureWindowsAbsolutePath with path.isAbsolute() - Tests: platform-conditional assertions for both Windows and POSIX - Windows behavior preserved unchanged via os.platform() guard All 17 tests pass on macOS. Windows tests guarded behind IS_WINDOWS.
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
import {
|
|
addProject,
|
|
listProjects,
|
|
removeProject,
|
|
registryFilePath,
|
|
type RegistryProject,
|
|
} from '../../src/lib/registry';
|
|
|
|
const IS_WINDOWS = os.platform() === 'win32';
|
|
|
|
async function withTempUserProfile(run: (userProfile: string) => Promise<void>): Promise<void> {
|
|
const previous = process.env.USERPROFILE;
|
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-registry-'));
|
|
process.env.USERPROFILE = tempDir;
|
|
|
|
try {
|
|
await run(tempDir);
|
|
} finally {
|
|
if (previous === undefined) {
|
|
delete process.env.USERPROFILE;
|
|
} else {
|
|
process.env.USERPROFILE = previous;
|
|
}
|
|
|
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
test('registryFilePath resolves under user home/.beadboard/projects.json', async () => {
|
|
await withTempUserProfile(async (userProfile) => {
|
|
const result = registryFilePath();
|
|
assert.equal(result, path.join(userProfile, '.beadboard', 'projects.json'));
|
|
});
|
|
});
|
|
|
|
test('listProjects returns empty when registry does not exist', async () => {
|
|
await withTempUserProfile(async () => {
|
|
const result = await listProjects();
|
|
assert.deepEqual(result, []);
|
|
});
|
|
});
|
|
|
|
if (IS_WINDOWS) {
|
|
test('addProject persists normalized path and deduplicates case/separators', async () => {
|
|
await withTempUserProfile(async () => {
|
|
const first = await addProject('c:/Work/Alpha/');
|
|
assert.equal(first.added, true);
|
|
|
|
const second = await addProject('C:\\work\\alpha');
|
|
assert.equal(second.added, false);
|
|
|
|
const listed = await listProjects();
|
|
assert.equal(listed.length, 1);
|
|
assert.equal(listed[0].path, 'C:/Work/Alpha');
|
|
|
|
const file = await fs.readFile(registryFilePath(), 'utf8');
|
|
const parsed = JSON.parse(file) as { projects: RegistryProject[] };
|
|
assert.equal(parsed.projects.length, 1);
|
|
});
|
|
});
|
|
|
|
test('removeProject removes matching normalized path', async () => {
|
|
await withTempUserProfile(async () => {
|
|
await addProject('D:/Repos/One');
|
|
await addProject('D:/Repos/Two');
|
|
|
|
const removed = await removeProject('d:\\repos\\one\\');
|
|
assert.equal(removed.removed, true);
|
|
|
|
const listed = await listProjects();
|
|
assert.deepEqual(
|
|
listed.map((project) => project.path),
|
|
['D:/Repos/Two'],
|
|
);
|
|
});
|
|
});
|
|
} else {
|
|
test('addProject persists and deduplicates POSIX paths', async () => {
|
|
await withTempUserProfile(async () => {
|
|
const first = await addProject('/opt/projects/alpha');
|
|
assert.equal(first.added, true);
|
|
|
|
const second = await addProject('/opt/projects/alpha/');
|
|
assert.equal(second.added, false);
|
|
|
|
const listed = await listProjects();
|
|
assert.equal(listed.length, 1);
|
|
assert.equal(listed[0].path, '/opt/projects/alpha');
|
|
});
|
|
});
|
|
|
|
test('removeProject removes matching POSIX path', async () => {
|
|
await withTempUserProfile(async () => {
|
|
await addProject('/opt/repos/one');
|
|
await addProject('/opt/repos/two');
|
|
|
|
const removed = await removeProject('/opt/repos/one');
|
|
assert.equal(removed.removed, true);
|
|
|
|
const listed = await listProjects();
|
|
assert.deepEqual(
|
|
listed.map((project) => project.path),
|
|
['/opt/repos/two'],
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
test('addProject rejects relative paths', async () => {
|
|
await withTempUserProfile(async () => {
|
|
await assert.rejects(() => addProject('relative/path'), /absolute/i);
|
|
});
|
|
});
|