100 lines
4.9 KiB
TypeScript
100 lines
4.9 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
import { headLines } from './pathAskContext'
|
||
import { PATH_ASK_HEAD_LINES, pathAskFollowUp, pathAskPrompt } from './pathAskPrompt'
|
||
|
||
const FILE = 'app/[[...section]]/page.tsx'
|
||
const FOLDER = 'app/[[...section]]'
|
||
|
||
describe('问路径时发给模型的话', () => {
|
||
it('文件:写明是文件,带相对路径和开头', () => {
|
||
const prompt = pathAskPrompt({
|
||
kind: 'file',
|
||
path: FILE,
|
||
head: { lines: ['export default function Page() {', ' return null', '}'], truncated: false },
|
||
}, '这个名字是什么意思')
|
||
expect(prompt).toContain(`这是一个文件,相对路径:${FILE}`)
|
||
expect(prompt).toContain('1 | export default function Page() {')
|
||
expect(prompt).toContain('3 | }')
|
||
expect(prompt).toContain('问题:这个名字是什么意思')
|
||
expect(prompt).toContain('只读,不要改任何东西')
|
||
expect(prompt).not.toContain('剩下的需要时自己读')
|
||
})
|
||
|
||
it('文件开头超过上限时说明只贴了开头,相对路径仍在', () => {
|
||
const lines = Array.from({ length: PATH_ASK_HEAD_LINES }, (_, index) => `line ${index}`)
|
||
const prompt = pathAskPrompt({
|
||
kind: 'file',
|
||
path: FILE,
|
||
head: { lines, truncated: true },
|
||
}, '?')
|
||
expect(prompt).toContain(`这是一个文件,相对路径:${FILE}`)
|
||
expect(prompt).toContain(`上面只贴了开头 ${PATH_ASK_HEAD_LINES} 行`)
|
||
expect(prompt).toContain(`80 | line ${PATH_ASK_HEAD_LINES - 1}`)
|
||
expect(prompt).not.toContain('line 80')
|
||
})
|
||
|
||
it('工作区外的库文件:写明在项目外、给绝对路径(相对路径它读不到)', () => {
|
||
const library = '/Users/me/.local/share/uv/python/cpython-3.12/lib/python3.12/pathlib.py'
|
||
const prompt = pathAskPrompt({ kind: 'file', path: library, outside: true, head: { lines: ['"""Object-oriented filesystem paths."""'], truncated: true } }, 'rglob 怎么实现的')
|
||
expect(prompt).toContain(`这是项目外的一个文件(比如依赖库的源码),绝对路径:${library}`)
|
||
expect(prompt).not.toContain('相对路径:')
|
||
})
|
||
|
||
it('文件夹:写明是文件夹,带相对路径,只列这一层的名字', () => {
|
||
const prompt = pathAskPrompt({
|
||
kind: 'folder',
|
||
path: FOLDER,
|
||
children: [
|
||
{ name: 'components', kind: 'folder' },
|
||
{ name: 'page.tsx', kind: 'file' },
|
||
],
|
||
}, '[[...section]] 是啥意思')
|
||
expect(prompt).toContain(`这是一个文件夹,相对路径:${FOLDER}`)
|
||
expect(prompt).toContain('- 文件夹 components\n- 文件 page.tsx')
|
||
expect(prompt).toContain('只列了这一层的名字')
|
||
expect(prompt).toContain('问题:[[...section]] 是啥意思')
|
||
expect(prompt).not.toContain('export default')
|
||
})
|
||
})
|
||
|
||
describe('问路径时回答里的代码位置', () => {
|
||
it('在 git 仓库里:要求写成从仓库根算起的链接,带例子;接着问时再说一遍', () => {
|
||
const subject = { kind: 'folder' as const, path: FOLDER, repoRoot: '/work/shop', children: [] }
|
||
const prompt = pathAskPrompt(subject, '这里放什么')
|
||
expect(prompt).toContain('提到代码位置时写成 Markdown 链接,点了能跳过去。路径从仓库根 /work/shop 算起,开头不带 / 或 ./:')
|
||
expect(prompt).toContain('[文件名:起-止](路径#L起-L止)')
|
||
expect(prompt).toContain('例如:[OrderService.cancel](backend/app/orders/service.py#OrderService.cancel)')
|
||
expect(prompt).not.toContain('提到文件时写相对项目根目录的路径')
|
||
const followUp = pathAskFollowUp(subject, '再说说')
|
||
expect(followUp).toMatch(/^接着问(还是 app\/\[\[\.\.\.section\]\] 这个文件夹,需要时重新读):再说说\n\n提到代码位置时写成 Markdown 链接/)
|
||
})
|
||
|
||
it('不在 git 仓库里(库源码):写行内代码的绝对路径,不写链接', () => {
|
||
const library = '/Users/me/.local/share/uv/python/cpython-3.12/lib/python3.12/pathlib.py'
|
||
const prompt = pathAskPrompt({ kind: 'file', path: library, outside: true, head: { lines: [], truncated: false } }, '?')
|
||
expect(prompt).toContain('问的代码不在 git 仓库里,提到代码位置时写行内代码:绝对路径加行号')
|
||
expect(prompt).not.toContain('Markdown 链接')
|
||
})
|
||
})
|
||
|
||
describe('文件开头截取', () => {
|
||
it('超过行数上限时只留前 80 行,并标明没贴完', () => {
|
||
const text = Array.from({ length: 100 }, (_, index) => `L${index}`).join('\n')
|
||
expect(headLines(text, false, PATH_ASK_HEAD_LINES)).toEqual({
|
||
lines: Array.from({ length: PATH_ASK_HEAD_LINES }, (_, index) => `L${index}`),
|
||
truncated: true,
|
||
})
|
||
})
|
||
|
||
it('字节没读完时丢掉被截断的最后一行', () => {
|
||
expect(headLines('alpha\nbeta-cut', true, PATH_ASK_HEAD_LINES)).toEqual({
|
||
lines: ['alpha'],
|
||
truncated: true,
|
||
})
|
||
})
|
||
|
||
it('空文件没有行', () => {
|
||
expect(headLines('', false, PATH_ASK_HEAD_LINES)).toEqual({ lines: [], truncated: false })
|
||
})
|
||
})
|