init
This commit is contained in:
commit
0964819cdc
34 files changed
+7912
No files matched your search
@@ -0,0 +1,222 @@
|
||||
import { strict as assert } from 'node:assert'
|
||||
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { test } from 'node:test'
|
||||
|
||||
import {
|
||||
DEFAULT_FOCUS_MARKERS,
|
||||
ancestorsTo,
|
||||
discoverScopes,
|
||||
focusKey,
|
||||
isInside,
|
||||
resolveFocus,
|
||||
} from '../lib/focus.js'
|
||||
|
||||
const WORKSPACE = '/repo'
|
||||
|
||||
/**
|
||||
* The two filesystem injection points, faked.
|
||||
*
|
||||
* `exists` and `isDirectory` are separate because the real code distinguishes
|
||||
* them: a focus path must not merely exist, it must be a directory. A single
|
||||
* predicate would let a test pass a file where a directory is required.
|
||||
*
|
||||
* @param {string[]} files paths that exist
|
||||
* @param {string[]} [directories] paths that are directories (defaults to `files`)
|
||||
*/
|
||||
function fakeFs(files, directories = files) {
|
||||
const fileSet = new Set(files)
|
||||
const dirSet = new Set(directories)
|
||||
return { exists: (path) => fileSet.has(path), isDirectory: (path) => dirSet.has(path) }
|
||||
}
|
||||
|
||||
test('isInside treats a directory as inside itself and rejects siblings', () => {
|
||||
assert.equal(isInside('/repo', '/repo'), true)
|
||||
assert.equal(isInside('/repo', '/repo/src'), true)
|
||||
assert.equal(isInside('/repo', '/repository'), false, 'a shared prefix is not a child')
|
||||
assert.equal(isInside('/repo', '/other'), false)
|
||||
})
|
||||
|
||||
test('ancestorsTo walks up to the stop directory and no further', () => {
|
||||
assert.deepEqual(ancestorsTo('/repo/src/a/b', '/repo'), ['/repo/src/a/b', '/repo/src/a', '/repo/src', '/repo'])
|
||||
assert.deepEqual(ancestorsTo('/repo', '/repo'), ['/repo'])
|
||||
// A start outside the stop does not run away to the filesystem root.
|
||||
assert.deepEqual(ancestorsTo('/elsewhere/x', '/repo'), ['/elsewhere/x'])
|
||||
})
|
||||
|
||||
test('no markers anywhere means the workspace, which is the old behaviour', () => {
|
||||
const result = resolveFocus({ workspace: WORKSPACE, cwd: WORKSPACE, ...fakeFs([]) })
|
||||
assert.equal(result.root, '/repo')
|
||||
assert.equal(result.source, 'workspace')
|
||||
})
|
||||
|
||||
test('a marker at the workspace root keeps the scope at the workspace', () => {
|
||||
// This is the live case today: AGENTS.md sits at the repository root, so
|
||||
// adopting focus changes nothing until a nested folder carries a marker.
|
||||
const result = resolveFocus({ workspace: WORKSPACE, cwd: WORKSPACE, ...fakeFs(['/repo/AGENTS.md']) })
|
||||
assert.equal(result.root, '/repo')
|
||||
assert.equal(result.source, 'marker')
|
||||
assert.equal(result.marker, 'AGENTS.md')
|
||||
})
|
||||
|
||||
test('a nested controller marker narrows the scope to that controller', () => {
|
||||
const controller = '/repo/src/controllers/room/occupancy'
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
cwd: controller,
|
||||
...fakeFs(['/repo/AGENTS.md', `${controller}/CONTROLLER.md`]),
|
||||
})
|
||||
assert.equal(result.root, controller)
|
||||
assert.equal(result.source, 'marker')
|
||||
assert.equal(result.marker, 'CONTROLLER.md')
|
||||
})
|
||||
|
||||
test('the nearest marker wins over a further one', () => {
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
cwd: '/repo/src/controllers/room/occupancy/state_machine',
|
||||
...fakeFs(['/repo/AGENTS.md', '/repo/src/controllers/room/occupancy/CONTROLLER.md']),
|
||||
})
|
||||
assert.equal(result.root, '/repo/src/controllers/room/occupancy')
|
||||
})
|
||||
|
||||
test('CONTROLLER.md outranks AGENTS.md in the same directory', () => {
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
cwd: WORKSPACE,
|
||||
...fakeFs(['/repo/AGENTS.md', '/repo/CONTROLLER.md']),
|
||||
})
|
||||
assert.equal(result.marker, 'CONTROLLER.md', 'the more specific marker should be reported')
|
||||
})
|
||||
|
||||
test('an explicit focus wins over any marker', () => {
|
||||
const lights = '/repo/src/controllers/room/device/lights'
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
cwd: WORKSPACE,
|
||||
explicit: 'src/controllers/room/device/lights',
|
||||
...fakeFs(['/repo/AGENTS.md', lights]),
|
||||
})
|
||||
assert.equal(result.root, lights)
|
||||
assert.equal(result.source, 'explicit')
|
||||
})
|
||||
|
||||
test('an explicit focus may be absolute', () => {
|
||||
const result = resolveFocus({ workspace: WORKSPACE, explicit: '/repo/src/x', ...fakeFs(['/repo/src/x']) })
|
||||
assert.equal(result.root, '/repo/src/x')
|
||||
assert.equal(result.source, 'explicit')
|
||||
})
|
||||
|
||||
test('an explicit focus that is a file, not a directory, is refused', () => {
|
||||
// The two predicates must genuinely differ, or this passes by accident.
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
explicit: 'src/AGENTS.md',
|
||||
...fakeFs(['/repo/src/AGENTS.md'], ['/repo', '/repo/src']),
|
||||
})
|
||||
assert.equal(result.root, '/repo')
|
||||
assert.match(result.problem, /not a directory/)
|
||||
})
|
||||
|
||||
test('a focus outside the workspace is refused and reported, not obeyed', () => {
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
explicit: '../elsewhere',
|
||||
...fakeFs(['/elsewhere'], ['/elsewhere']),
|
||||
})
|
||||
assert.equal(result.root, '/repo', 'must fall back to the workspace')
|
||||
assert.match(result.problem, /outside the workspace/)
|
||||
})
|
||||
|
||||
test('a focus that does not exist is refused and reported', () => {
|
||||
const result = resolveFocus({ workspace: WORKSPACE, explicit: 'src/typo', ...fakeFs([]) })
|
||||
assert.equal(result.root, '/repo')
|
||||
assert.match(result.problem, /not a directory/)
|
||||
})
|
||||
|
||||
test('an empty marker list still resolves, to the workspace', () => {
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
cwd: WORKSPACE,
|
||||
markers: [],
|
||||
...fakeFs(['/repo/AGENTS.md']),
|
||||
})
|
||||
assert.equal(result.root, '/repo')
|
||||
assert.equal(result.source, 'workspace')
|
||||
})
|
||||
|
||||
test('a cwd outside the workspace cannot escape it', () => {
|
||||
const result = resolveFocus({
|
||||
workspace: WORKSPACE,
|
||||
cwd: '/somewhere/else',
|
||||
...fakeFs(['/somewhere/else/AGENTS.md', '/repo/AGENTS.md']),
|
||||
})
|
||||
assert.equal(result.root, '/repo', 'a stray cwd must not pull the scope out of the workspace')
|
||||
})
|
||||
|
||||
test('focusKey is a stable relative path, with "." for the workspace itself', () => {
|
||||
assert.equal(focusKey('/repo', '/repo'), '.')
|
||||
assert.equal(focusKey('/repo', '/repo/src/controllers/room/occupancy'), 'src/controllers/room/occupancy')
|
||||
})
|
||||
|
||||
test('the default markers include the controller instruction files', () => {
|
||||
assert.ok(DEFAULT_FOCUS_MARKERS.includes('CONTROLLER.md'))
|
||||
assert.ok(DEFAULT_FOCUS_MARKERS.includes('DEBUG_UI.md'))
|
||||
// CONTROLLER.md must be tried before AGENTS.md, since both may be present.
|
||||
assert.ok(DEFAULT_FOCUS_MARKERS.indexOf('CONTROLLER.md') < DEFAULT_FOCUS_MARKERS.indexOf('AGENTS.md'))
|
||||
})
|
||||
|
||||
test('discoverScopes finds marker directories, sorted and workspace-relative', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'recall-scopes-'))
|
||||
try {
|
||||
await mkdir(join(root, 'src', 'weather'), { recursive: true })
|
||||
await mkdir(join(root, 'src', 'auth'), { recursive: true })
|
||||
await mkdir(join(root, 'node_modules', 'dep'), { recursive: true })
|
||||
await writeFile(join(root, 'src', 'auth', 'CONTROLLER.md'), '# auth\n')
|
||||
await writeFile(join(root, 'src', 'weather', 'CONTROLLER.md'), '# weather\n')
|
||||
await writeFile(join(root, 'AGENTS.md'), '# repo\n')
|
||||
// A marker inside a dependency describes somebody else's project.
|
||||
await writeFile(join(root, 'node_modules', 'dep', 'CONTROLLER.md'), '# not ours\n')
|
||||
await writeFile(join(root, 'src', 'code.ts'), 'export const x = 1\n')
|
||||
|
||||
const scopes = await discoverScopes({ workspace: root })
|
||||
assert.deepEqual(
|
||||
scopes.map((scope) => scope.path),
|
||||
['.', 'src/auth', 'src/weather'],
|
||||
'sorted, relative, and node_modules excluded',
|
||||
)
|
||||
assert.equal(scopes[0].marker, 'AGENTS.md')
|
||||
assert.equal(scopes[1].marker, 'CONTROLLER.md')
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('discoverScopes honours a custom marker list and maxDepth', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'recall-scopes-'))
|
||||
try {
|
||||
await mkdir(join(root, 'deep', 'deeper', 'deepest'), { recursive: true })
|
||||
await writeFile(join(root, 'deep', 'SCOPE.md'), '# scope\n')
|
||||
await writeFile(join(root, 'deep', 'deeper', 'deepest', 'SCOPE.md'), '# scope\n')
|
||||
|
||||
const shallow = await discoverScopes({ workspace: root, markers: ['SCOPE.md'], maxDepth: 1 })
|
||||
assert.deepEqual(shallow.map((scope) => scope.path), ['deep'])
|
||||
|
||||
const deep = await discoverScopes({ workspace: root, markers: ['SCOPE.md'], maxDepth: 4 })
|
||||
assert.deepEqual(deep.map((scope) => scope.path), ['deep', 'deep/deeper/deepest'])
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('discoverScopes returns nothing for a directory with no markers', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'recall-scopes-'))
|
||||
try {
|
||||
await mkdir(join(root, 'src'), { recursive: true })
|
||||
await writeFile(join(root, 'src', 'a.ts'), 'a\n')
|
||||
assert.deepEqual(await discoverScopes({ workspace: root }), [])
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user