Skip to content

Commit 830136a

Browse files
committed
test: prove canonical Rescue routing on aliases
1 parent 4782943 commit 830136a

2 files changed

Lines changed: 30 additions & 4 deletions

File tree

tests/integration/companion.test.mjs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { spawn } from 'node:child_process';
44
import { randomBytes } from 'node:crypto';
55
import { mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, symlink, unlink, writeFile } from 'node:fs/promises';
66
import { tmpdir } from 'node:os';
7-
import { basename, join } from 'node:path';
7+
import { basename, join, sep } from 'node:path';
88
import { fileURLToPath } from 'node:url';
99
import test from 'node:test';
1010
import { PassThrough } from 'node:stream';
@@ -784,11 +784,38 @@ test('prepared Rescue canonicalizes a resolvable cwd alias before exact binding
784784
parentSessionId: 'aliased-parent', parentTurnId: 'aliased-origin', childId, childTurnId: 'aliased-child-turn',
785785
prompt: '$zcode:rescue --fresh --wait preserve canonical workspace identity',
786786
});
787+
const aliasedWorkspace = `${context.workspace}${sep}nested${sep}..`;
788+
const canonicalWorkspace = (await resolveWorkspaceStorage(context)).workspacePath;
789+
assert.notEqual(aliasedWorkspace, canonicalWorkspace);
787790
const output = await runDirectInvocation(['invoke-prepared', 'rescue'], {
788-
cwd: join(context.workspace, 'nested', '..'), env: { ...context.env, CODEX_THREAD_ID: childId },
791+
cwd: aliasedWorkspace, env: { ...context.env, CODEX_THREAD_ID: childId },
789792
});
790793
assert.equal(output.job.status, 'succeeded');
791-
assert.equal(output.job.workspace, (await resolveWorkspaceStorage(context)).workspacePath);
794+
assert.equal(output.job.workspace, canonicalWorkspace);
795+
});
796+
797+
test('bound Rescue choice canonicalizes the persisted caller workspace before reservation', async () => {
798+
const context = await fixture(); const parentSessionId = 'aliased-choice-parent'; const childId = 'aliased-choice-child'; const childTurnId = 'aliased-choice-turn';
799+
await mkdir(join(context.workspace, 'nested'));
800+
await prepareDirectRescueChild(context, {
801+
parentSessionId, parentTurnId: 'aliased-choice-origin', childId, childTurnId,
802+
prompt: '$zcode:rescue --fresh --wait establish exact session',
803+
});
804+
const initial = await runDirectInvocation(['invoke-prepared', 'rescue'], { cwd: context.workspace, env: { ...context.env, CODEX_THREAD_ID: childId } });
805+
assert.equal(initial.job.status, 'succeeded');
806+
await markForwarding(context.dataRoot, {
807+
session_id: parentSessionId, turn_id: childTurnId, cwd: context.workspace, hook_event_name: 'SubagentStop',
808+
agent_id: childId, agent_type: 'zcode-rescue',
809+
});
810+
const identity = createIdentityStore({ dataRoot: context.dataRoot });
811+
await identity.beginCallerTurn({ sessionId: parentSessionId, turnId: 'aliased-choice-next', workspace: context.workspace, permissionMode: 'workspace-write', prompt: '$zcode:rescue continue exact session' });
812+
const preparation = new PassThrough(); preparation.end(`${JSON.stringify({ version: 1, source: 'explicit', task: 'continue exact session', options: { execution: 'foreground' } })}\n`);
813+
await runDirectInvocation(['prepare', 'rescue'], { cwd: context.workspace, env: { ...context.env, CODEX_THREAD_ID: parentSessionId }, input: preparation });
814+
assert.equal((await runDirectInvocation(['invoke-prepared', 'rescue'], { cwd: context.workspace, env: { ...context.env, CODEX_THREAD_ID: childId } })).type, 'needs-choice');
815+
const aliasedWorkspace = `${context.workspace}${sep}nested${sep}..`;
816+
const resumed = await runDirectInvocation(['invoke-choice', 'rescue', 'resume'], { cwd: aliasedWorkspace, env: { ...context.env, CODEX_THREAD_ID: childId } });
817+
assert.equal(resumed.job.status, 'succeeded');
818+
assert.equal(resumed.job.zcodeSessionId, initial.job.zcodeSessionId);
792819
});
793820

794821
test('isolated child loss recovers the accepted parent-owned turn without another session send', { skip: windowsRealSignalSkip }, async (t) => {

tests/rescue-binding.test.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,6 @@ test('publication rejects binding-partition and state-lock replacement without a
344344
const store = createStateStore({ dataRoot: base.dataRoot, testOnlyPublicationHook: async (seam) => {
345345
if (replaced || seam !== 'fresh:owner-binding') return; replaced = true;
346346
const lock = join(storage.directory, '.state.lock');
347-
if (process.platform === 'win32') throw new Error('test-only Windows held state-lock replacement fault');
348347
await rename(lock, `${lock}.replaced`); await mkdir(lock, { mode: 0o700 }); await writeFile(join(lock, 'advisory.lock'), '', { mode: 0o600 });
349348
} });
350349
await assert.rejects(store.reserveFreshRescueJob({ workspace: base.workspace, reservation: reservation(base.workspace), executor: executor(base.workspace) }), { code: process.platform === 'win32' ? 'RESCUE_PUBLICATION_TEST_FAULT' : 'RESCUE_BINDING_INVALID' });

0 commit comments

Comments
 (0)