open-design/apps/daemon/tests/resolve-data-dir.test.ts
Denis Redozubov 9a3424d68c
feat(daemon): add sandbox runtime foundation (#3242)
* feat(daemon): add sandbox runtime foundation

* fix(daemon): preserve sandbox roots after agent env overrides

* fix(daemon): keep readiness probes pathless

* fix(daemon): harden headless run fallbacks

* fix(daemon): bootstrap sandbox runtime discovery

* fix(daemon): preserve explicit sandbox agent profile mounts

* fix(daemon): keep sandbox profile lookup run scoped

* fix(daemon): normalize sandbox data dir input

* fix(daemon): pin sandbox env roots to base data dir
2026-05-30 15:06:05 +00:00

112 lines
4.3 KiB
TypeScript

/**
* Unit tests for resolveDataDir, the OD_DATA_DIR path resolver. Covers the
* $HOME / ${HOME} / ~/ shorthands that launchers can pass literally when no
* shell is in the loop (#390), with both forward and backslash separators so
* Windows launchers behave the same as Unix ones.
*
* Hermetic: every test runs against a fresh mkdtemp() home + projectRoot
* pair, and os.homedir() is stubbed so resolveDataDir's writability check
* never touches the developer's or CI runner's real home directory.
*/
import os from 'node:os';
import path from 'node:path';
import { mkdtempSync } from 'node:fs';
import { rm } from 'node:fs/promises';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { resolveDataDir } from '../src/server.js';
describe('resolveDataDir', () => {
let fakeHome: string;
let projectRoot: string;
let homedirSpy: ReturnType<typeof vi.spyOn>;
beforeEach(() => {
fakeHome = mkdtempSync(path.join(os.tmpdir(), 'rdd-home-'));
projectRoot = mkdtempSync(path.join(os.tmpdir(), 'rdd-project-'));
homedirSpy = vi.spyOn(os, 'homedir').mockReturnValue(fakeHome);
});
afterEach(async () => {
homedirSpy.mockRestore();
await rm(fakeHome, { recursive: true, force: true });
await rm(projectRoot, { recursive: true, force: true });
});
it('returns <projectRoot>/.od when OD_DATA_DIR is unset', () => {
expect(resolveDataDir(undefined, projectRoot)).toBe(path.join(projectRoot, '.od'));
expect(resolveDataDir('', projectRoot)).toBe(path.join(projectRoot, '.od'));
});
it('requires an explicit OD_DATA_DIR when sandbox mode requires one', () => {
expect(() =>
resolveDataDir(undefined, projectRoot, { requireExplicit: true }),
).toThrow('OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled');
expect(() => resolveDataDir('', projectRoot, { requireExplicit: true })).toThrow(
'OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled',
);
expect(() =>
resolveDataDir(' ', projectRoot, { requireExplicit: true }),
).toThrow('OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled');
});
it('trims OD_DATA_DIR before resolving the storage root', () => {
const out = resolveDataDir(' rel-od ', projectRoot, { requireExplicit: true });
expect(out).toBe(path.join(projectRoot, 'rel-od'));
});
it('expands a leading ~/ against the user home directory', () => {
const out = resolveDataDir('~/od-test', projectRoot);
expect(out).toBe(path.join(fakeHome, 'od-test'));
});
it('expands ~\\ (backslash) against the user home directory', () => {
const out = resolveDataDir('~\\od-test', projectRoot);
expect(out).toBe(path.join(fakeHome, 'od-test'));
});
it('expands a bare ~ to the user home directory', () => {
expect(resolveDataDir('~', projectRoot)).toBe(fakeHome);
});
it('expands $HOME/ against the user home directory', () => {
const out = resolveDataDir('$HOME/od-test', projectRoot);
expect(out).toBe(path.join(fakeHome, 'od-test'));
});
it('expands $HOME\\ (backslash, Windows launcher) against the user home directory', () => {
const out = resolveDataDir('$HOME\\od-test', projectRoot);
expect(out).toBe(path.join(fakeHome, 'od-test'));
});
it('expands ${HOME}/ against the user home directory', () => {
const out = resolveDataDir('${HOME}/od-test', projectRoot);
expect(out).toBe(path.join(fakeHome, 'od-test'));
});
it('expands ${HOME}\\ (backslash) against the user home directory', () => {
const out = resolveDataDir('${HOME}\\od-test', projectRoot);
expect(out).toBe(path.join(fakeHome, 'od-test'));
});
it('expands a bare $HOME to the user home directory', () => {
expect(resolveDataDir('$HOME', projectRoot)).toBe(fakeHome);
});
it('expands a bare ${HOME} to the user home directory', () => {
expect(resolveDataDir('${HOME}', projectRoot)).toBe(fakeHome);
});
it('passes absolute paths through unchanged', async () => {
const abs = mkdtempSync(path.join(os.tmpdir(), 'rdd-abs-'));
try {
expect(resolveDataDir(abs, projectRoot)).toBe(abs);
} finally {
await rm(abs, { recursive: true, force: true });
}
});
it('resolves relative paths against projectRoot', () => {
const out = resolveDataDir('rel-od', projectRoot);
expect(out).toBe(path.join(projectRoot, 'rel-od'));
});
});