132 lines
4.3 KiB
TypeScript
132 lines
4.3 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|||
|
|
|
||
|
|
import {
|
||
|
|
describeSessionDirectorySources,
|
||
|
|
resolveSessionDirectoryFromSources,
|
||
|
|
} from './session-directory-resolution';
|
||
|
|
|
||
|
|
const WORKTREE = '/repo/.worktrees/feature';
|
||
|
|
const MAIN = '/repo';
|
||
|
|
|
||
|
|
describe('resolveSessionDirectoryFromSources', () => {
|
||
|
|
test('authoritative directory beats a selection-time fallback', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: WORKTREE,
|
||
|
|
selected: MAIN,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBe(WORKTREE);
|
||
|
|
expect(resolution.source).toBe('authoritative');
|
||
|
|
expect(resolution.conflict).toEqual({ source: 'selected', directory: MAIN });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('authoritative directory beats a directory persisted across restarts', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: WORKTREE,
|
||
|
|
remembered: MAIN,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBe(WORKTREE);
|
||
|
|
expect(resolution.conflict).toEqual({ source: 'remembered', directory: MAIN });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('the indexed directory outranks a locally requested worktree path', () => {
|
||
|
|
// attachment/worktreeMetadata hold the path this client asked for, before
|
||
|
|
// the server canonicalized it. Letting them win would route prompts to a
|
||
|
|
// directory that no child store owns.
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
attachment: '/requested/worktree',
|
||
|
|
worktreeMetadata: '/requested/worktree',
|
||
|
|
authoritative: WORKTREE,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBe(WORKTREE);
|
||
|
|
expect(resolution.source).toBe('authoritative');
|
||
|
|
expect(resolution.conflict).toEqual({ source: 'attachment', directory: '/requested/worktree' });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('a worktree attachment is used while the session is not indexed yet', () => {
|
||
|
|
// A guessed selection is not passed as `selected` at all, so the worktree
|
||
|
|
// assignment is the best available value during the bootstrap race.
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: null,
|
||
|
|
selected: null,
|
||
|
|
attachment: WORKTREE,
|
||
|
|
remembered: MAIN,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBe(WORKTREE);
|
||
|
|
expect(resolution.source).toBe('attachment');
|
||
|
|
expect(resolution.conflict).toEqual({ source: 'remembered', directory: MAIN });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('a server-confirmed selection outranks the requested worktree path', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: null,
|
||
|
|
selected: '/canonical/worktree',
|
||
|
|
attachment: '/requested/worktree',
|
||
|
|
worktreeMetadata: '/requested/worktree',
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBe('/canonical/worktree');
|
||
|
|
expect(resolution.source).toBe('selected');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('falls back to the selection hint while the session is not indexed yet', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: null,
|
||
|
|
selected: WORKTREE,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBe(WORKTREE);
|
||
|
|
expect(resolution.source).toBe('selected');
|
||
|
|
expect(resolution.conflict).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('agreeing sources report no conflict', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: WORKTREE,
|
||
|
|
selected: WORKTREE,
|
||
|
|
remembered: WORKTREE,
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.conflict).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('reports the first disagreeing source, not the last', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
authoritative: WORKTREE,
|
||
|
|
selected: MAIN,
|
||
|
|
remembered: '/somewhere/else',
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.conflict).toEqual({ source: 'selected', directory: MAIN });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('treats missing and blank values as unknown, never as a directory', () => {
|
||
|
|
const resolution = resolveSessionDirectoryFromSources({
|
||
|
|
attachment: null,
|
||
|
|
worktreeMetadata: ' ',
|
||
|
|
authoritative: undefined,
|
||
|
|
selected: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(resolution.directory).toBeNull();
|
||
|
|
expect(resolution.source).toBe('none');
|
||
|
|
expect(resolution.conflict).toBeNull();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('describeSessionDirectorySources', () => {
|
||
|
|
test('lists populated sources in precedence order', () => {
|
||
|
|
expect(describeSessionDirectorySources({
|
||
|
|
remembered: MAIN,
|
||
|
|
authoritative: WORKTREE,
|
||
|
|
selected: '',
|
||
|
|
})).toEqual([
|
||
|
|
{ source: 'authoritative', directory: WORKTREE },
|
||
|
|
{ source: 'remembered', directory: MAIN },
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
});
|