2026-08-12 03:58:02 +08:00
|
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
|
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
|
import type { AiModelProvider } from '../lib/aiTargets'
|
|
|
|
|
import { PersonaLanguageShellPanel } from './PersonaLanguageShellPanel'
|
|
|
|
|
|
|
|
|
|
const provider: AiModelProvider = {
|
|
|
|
|
id: 'provider-1',
|
|
|
|
|
name: 'Local provider',
|
|
|
|
|
kind: 'open_ai_compatible',
|
|
|
|
|
base_url: 'http://127.0.0.1:8000/v1',
|
|
|
|
|
models: [{
|
|
|
|
|
id: 'model-1',
|
|
|
|
|
capabilities: { streaming: false, tools: false, vision: false, json_mode: true, reasoning: true },
|
|
|
|
|
}],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('PersonaLanguageShellPanel', () => {
|
|
|
|
|
beforeEach(() => vi.clearAllMocks())
|
|
|
|
|
|
|
|
|
|
it('connects desktop language input to the fail-closed partner planner without persona authority', async () => {
|
|
|
|
|
const planGoal = vi.fn().mockResolvedValue({
|
|
|
|
|
phase: 'planned',
|
|
|
|
|
binding: { requestId: 'request-1' },
|
|
|
|
|
goal: {
|
|
|
|
|
goal: '核验当前事实后规划下一步',
|
|
|
|
|
originalUtterance: '继续开发',
|
|
|
|
|
mode: 'system_direct',
|
|
|
|
|
status: 'needs_research',
|
|
|
|
|
executable: false,
|
|
|
|
|
deliberation: {
|
|
|
|
|
evidenceSources: ['repo://current-main'],
|
|
|
|
|
executionDisposition: { decision: 'RESEARCH', reason: '需要当前证据' },
|
|
|
|
|
},
|
|
|
|
|
confirmation: { required: false, boundaries: [], question: null },
|
|
|
|
|
receiptSummary: '我还不能执行:需要当前证据。',
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
render(
|
|
|
|
|
<PersonaLanguageShellPanel
|
|
|
|
|
locale="zh-CN"
|
|
|
|
|
personaId="ICE-P-ZY001"
|
|
|
|
|
repositoryPaths={['/persona']}
|
|
|
|
|
providers={[provider]}
|
|
|
|
|
planGoal={planGoal}
|
|
|
|
|
/>,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
fireEvent.change(screen.getByLabelText('language goal'), { target: { value: '继续开发' } })
|
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '继续' }))
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(planGoal).toHaveBeenCalledTimes(1))
|
|
|
|
|
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({
|
|
|
|
|
utterance: '继续开发',
|
|
|
|
|
personaId: 'ICE-P-ZY001',
|
|
|
|
|
repositoryPaths: ['/persona'],
|
|
|
|
|
providers: [provider],
|
|
|
|
|
personaAuthorized: false,
|
|
|
|
|
developmentId: 'DEV-20260811-010',
|
2026-08-12 04:20:52 +08:00
|
|
|
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-12.9',
|
2026-08-12 03:58:02 +08:00
|
|
|
}))
|
|
|
|
|
expect(await screen.findByText('我还不能执行:需要当前证据。')).toBeInTheDocument()
|
|
|
|
|
expect(screen.getByText('核验当前事实后规划下一步')).toBeInTheDocument()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('shows the repository gate and never calls a later lifecycle itself', async () => {
|
|
|
|
|
const planGoal = vi.fn().mockResolvedValue({
|
|
|
|
|
phase: 'unbound',
|
|
|
|
|
inspectedRepositoryCount: 1,
|
|
|
|
|
failures: [],
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
render(
|
|
|
|
|
<PersonaLanguageShellPanel
|
|
|
|
|
locale="zh-CN"
|
|
|
|
|
personaId="ICE-P-ZY001"
|
|
|
|
|
repositoryPaths={['/ordinary']}
|
|
|
|
|
providers={[provider]}
|
|
|
|
|
planGoal={planGoal}
|
|
|
|
|
/>,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
fireEvent.change(screen.getByLabelText('language goal'), { target: { value: '继续开发' } })
|
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '继续' }))
|
|
|
|
|
|
|
|
|
|
expect(await screen.findByText('这只说明当前挂载范围没有匹配的完整证据,不能据此判定人格不存在。')).toBeInTheDocument()
|
|
|
|
|
expect(screen.queryByLabelText('reality boundary confirmation')).not.toBeInTheDocument()
|
|
|
|
|
})
|
|
|
|
|
})
|