mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* 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
112 lines
4.3 KiB
TypeScript
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'));
|
|
});
|
|
});
|