import type { ExtensionToWebview, WebviewToExtension } from './messages' import { describe, expect, it, vi } from 'vitest' import { window } from 'vscode' import { askCommentBody } from '../qa/claudeAsk' import { PreviewEditorProvider } from './previewEditor' vi.mock('vscode', () => ({ commands: { executeCommand: vi.fn() }, window: { showErrorMessage: vi.fn() }, workspace: { onDidChangeTextDocument: () => ({ dispose: () => {} }), onDidChangeConfiguration: () => ({ dispose: () => {} }), }, })) vi.mock('../../logging/logger', () => ({ logger: { add: vi.fn() } })) vi.mock('../../webview/html', () => ({ webviewHtml: () => '', webviewUiRoot: () => ({}) })) vi.mock('../folderStore', () => ({ locate: () => undefined })) vi.mock('./pathPreview', () => ({ pathPreviewOf: () => undefined })) vi.mock('./occurrences', () => ({ occurrencesOf: vi.fn() })) vi.mock('../qa/claudeAsk', () => ({ askCommentBody: vi.fn(), askFailure: (error: Error) => error.message })) function preview(current: () => Promise) { const subscribe = () => ({ dispose: () => {} }) const receive = vi.fn<(listener: (message: WebviewToExtension) => Promise) => { dispose: () => void }>(subscribe) const post = vi.fn<(message: ExtensionToWebview) => void>() const uri = { fsPath: '/workspace/a.ts', scheme: 'file', toString: () => 'file:///workspace/a.ts' } const provider = new PreviewEditorProvider(...[ {}, {}, {}, { subscribe }, async () => [], { imageRoot: () => undefined, onDidChange: subscribe, onDidAnswer: subscribe, profiles: { subscribe, current } }, async () => [], async () => [], { onDidChangeDiffBase: subscribe }, { onDidChange: subscribe }, async () => false, ] as unknown as ConstructorParameters) provider.resolveCustomTextEditor(...[ { uri, isDirty: false }, { webview: { onDidReceiveMessage: receive, postMessage: post }, onDidChangeViewState: subscribe, onDidDispose: subscribe }, ] as unknown as Parameters) const opened = (provider as unknown as { previews: Set<{ sent: unknown, dispose: () => void }> }).previews.values().next().value! opened.sent = { blocks: [{ anchor: 'block', children: [], commentTarget: {}, startLine: 0, endLine: 1 }] } return { send: receive.mock.calls[0]![0], post, dispose: () => opened.dispose() } } describe('重写注释的模型选择失败', () => { it('模型删除后仍发送失败结束消息和错误提示,不调用模型且允许再次尝试', async () => { const error = '文件问答模型不可用:official。请重新选择。' const current = vi.fn<() => Promise>().mockRejectedValue(new Error(error)) const editor = preview(current) try { await expect(editor.send({ type: 'rewriteComment', anchor: 'block' })).resolves.toBeUndefined() expect(editor.post).toHaveBeenLastCalledWith({ type: 'rewriteEnd', anchor: 'block', status: 'failed', error }) expect(window.showErrorMessage).toHaveBeenCalledWith(`代码阅读:${error}`) expect(askCommentBody).not.toHaveBeenCalled() await editor.send({ type: 'rewriteComment', anchor: 'block' }) expect(current).toHaveBeenCalledTimes(2) expect(editor.post.mock.calls.filter(([message]) => message.type === 'rewriteEnd')).toHaveLength(2) } finally { editor.dispose() } }) })