import type { WorkspaceFolder } from 'vscode' import { beforeEach, describe, expect, it, vi } from 'vitest' import { keyUri, locateQa } from './folderStore' const fake = vi.hoisted(() => { /** 够用的 Uri:协议、主机、路径;测试里都是 POSIX 路径,fsPath 同 path */ class FakeUri { constructor(readonly scheme: string, readonly authority: string, readonly path: string) {} get fsPath(): string { return this.path } with(change: { path?: string }): FakeUri { return new FakeUri(this.scheme, this.authority, change.path ?? this.path) } toString(): string { return `${this.scheme}://${this.authority}${this.path}` } static file(fsPath: string): FakeUri { return new FakeUri('file', '', fsPath) } static joinPath(base: FakeUri, ...parts: string[]): FakeUri { return base.with({ path: [base.path, ...parts].join('/') }) } } /** 眼下打开的工作区文件夹,按顺序 */ const folders: WorkspaceFolder[] = [] const workspace = { get workspaceFolders(): WorkspaceFolder[] | undefined { return folders.length > 0 ? folders : undefined }, getWorkspaceFolder: (uri: FakeUri) => folders.find(folder => uri.path === folder.uri.path || uri.path.startsWith(`${folder.uri.path}/`)), } return { FakeUri, folders, workspace } }) vi.mock('vscode', () => ({ Uri: fake.FakeUri, workspace: fake.workspace, Disposable: {}, EventEmitter: class {}, RelativePattern: class {}, window: {} })) vi.mock('node:os', () => ({ homedir: () => '/Users/me' })) const { FakeUri } = fake const folder = (path: string, index: number): WorkspaceFolder => ({ uri: FakeUri.file(path) as never, name: path.split('/').at(-1)!, index }) const APP = folder('/Users/me/work/app', 0) const API = folder('/Users/me/work/api', 1) /** uv 装的 Python 标准库,⌘ 点 rglob 进去的那份 */ const LIBRARY_DIR = '/Users/me/.local/share/uv/python/cpython-3.12.4-macos-aarch64-none/lib/python3.12' const PATHLIB = FakeUri.file(`${LIBRARY_DIR}/pathlib.py`) as never beforeEach(() => { fake.folders.splice(0, fake.folders.length, APP, API) }) describe('这份文件的问答记在哪', () => { it('工作区里的文件:记在所在的工作区文件夹,键是相对路径;发给模型也写相对路径,不用另外放行目录', () => { expect(locateQa(FakeUri.file('/Users/me/work/api/src/main.py') as never)).toEqual({ folder: API, key: 'src/main.py', promptPath: 'src/main.py' }) }) it('家目录下的工作区外文件(库源码):记在第一个工作区文件夹,键把家目录写成 ~;发给模型写绝对路径,放行它所在的目录', () => { expect(locateQa(PATHLIB)).toEqual({ folder: APP, key: '~/.local/share/uv/python/cpython-3.12.4-macos-aarch64-none/lib/python3.12/pathlib.py', promptPath: `${LIBRARY_DIR}/pathlib.py`, outsideDir: LIBRARY_DIR, }) }) it('家目录外的工作区外文件:键是绝对路径', () => { const requests = FakeUri.file('/opt/homebrew/lib/python3.12/site-packages/requests/api.py') as never expect(locateQa(requests)).toMatchObject({ folder: APP, key: '/opt/homebrew/lib/python3.12/site-packages/requests/api.py', outsideDir: '/opt/homebrew/lib/python3.12/site-packages/requests' }) }) it('没打开工作区文件夹时没有;工作区文件夹本身、不是磁盘上的文件(别的协议)也没有', () => { expect(locateQa(APP.uri)).toBeUndefined() expect(locateQa(new FakeUri('git', '', `${LIBRARY_DIR}/pathlib.py`) as never)).toBeUndefined() fake.folders.splice(0) expect(locateQa(PATHLIB)).toBeUndefined() }) }) describe('表里的键对应哪个文件', () => { it('工作区里的在工作区文件夹下面;工作区外的还原成磁盘上的位置,和打开的那份文件是同一个 Uri', () => { expect(keyUri(API, 'src/main.py').toString()).toBe('file:///Users/me/work/api/src/main.py') expect(keyUri(APP, locateQa(PATHLIB)!.key).toString()).toBe(String(PATHLIB)) expect(keyUri(APP, '/opt/homebrew/a.py').toString()).toBe('file:///opt/homebrew/a.py') }) })