Download src/server/__tests__/title-service.test.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 9.98 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/server/__tests__/title-service.test.ts
- Command line
-
hf download hf://chenbhao/codev/src/server/__tests__/title-service.test.ts
-
curl -L -o title-service.test.ts https://huggingface.co/chenbhao/codev/resolve/main/src/server/__tests__/title-service.test.ts
9.98 kB
| import { afterEach, beforeEach, describe, expect, test } from 'bun:test' | |
| import * as fs from 'fs/promises' | |
| import * as os from 'os' | |
| import * as path from 'path' | |
| import { OPENAI_CODEX_API_ENDPOINT } from '../../services/openaiAuth/client.js' | |
| import { ProviderService } from '../services/providerService.js' | |
| import { deriveTitle, generateTitle, parseGeneratedTitleText, saveAiTitle } from '../services/titleService.js' | |
| import { sessionService } from '../services/sessionService.js' | |
| import { hahaOpenAIOAuthService } from '../services/hahaOpenAIOAuthService.js' | |
| describe('titleService', () => { | |
| let tmpDir: string | |
| let originalConfigDir: string | undefined | |
| let originalFetch: typeof globalThis.fetch | |
| beforeEach(async () => { | |
| originalConfigDir = process.env.CLAUDE_CONFIG_DIR | |
| originalFetch = globalThis.fetch | |
| tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'title-service-test-')) | |
| process.env.CLAUDE_CONFIG_DIR = tmpDir | |
| }) | |
| afterEach(async () => { | |
| globalThis.fetch = originalFetch | |
| hahaOpenAIOAuthService.dispose() | |
| restoreEnv('CLAUDE_CONFIG_DIR', originalConfigDir) | |
| await fs.rm(tmpDir, { recursive: true, force: true }) | |
| }) | |
| test('sends disabled thinking for opted-in providers when desktop thinking is off', async () => { | |
| let requestBody: Record<string, unknown> | null = null | |
| const server = Bun.serve({ | |
| hostname: '127.0.0.1', | |
| port: 0, | |
| async fetch(req) { | |
| requestBody = await req.json() as Record<string, unknown> | |
| return Response.json({ | |
| content: [{ type: 'text', text: '{"title":"Trace ok"}' }], | |
| }) | |
| }, | |
| }) | |
| try { | |
| const providerId = 'zhipu-test' | |
| await fs.mkdir(path.join(tmpDir, 'cc-haha'), { recursive: true }) | |
| await fs.writeFile( | |
| path.join(tmpDir, 'settings.json'), | |
| JSON.stringify({ alwaysThinkingEnabled: false }, null, 2), | |
| ) | |
| await fs.writeFile( | |
| path.join(tmpDir, 'cc-haha', 'providers.json'), | |
| JSON.stringify({ | |
| activeId: providerId, | |
| providers: [ | |
| { | |
| id: providerId, | |
| presetId: 'zhipuglm', | |
| name: 'Zhipu GLM', | |
| apiKey: 'test-key', | |
| baseUrl: `http://127.0.0.1:${server.port}/anthropic`, | |
| apiFormat: 'anthropic', | |
| models: { | |
| main: 'glm-5.1', | |
| haiku: 'glm-4.5-air', | |
| sonnet: 'glm-5-turbo', | |
| opus: 'glm-5.1', | |
| }, | |
| }, | |
| ], | |
| }, null, 2), | |
| ) | |
| await expect(generateTitle('请只回复 trace-ok')).resolves.toBe('Trace ok') | |
| expect(requestBody?.thinking).toEqual({ type: 'disabled' }) | |
| } finally { | |
| server.stop(true) | |
| } | |
| }) | |
| test('sends disabled thinking for DeepSeek title generation when desktop thinking is off', async () => { | |
| let requestBody: Record<string, unknown> | null = null | |
| const server = Bun.serve({ | |
| hostname: '127.0.0.1', | |
| port: 0, | |
| async fetch(req) { | |
| requestBody = await req.json() as Record<string, unknown> | |
| return Response.json({ | |
| content: [{ type: 'text', text: '{"title":"Trace ok"}' }], | |
| }) | |
| }, | |
| }) | |
| try { | |
| const providerId = 'deepseek-test' | |
| await fs.mkdir(path.join(tmpDir, 'cc-haha'), { recursive: true }) | |
| await fs.writeFile( | |
| path.join(tmpDir, 'settings.json'), | |
| JSON.stringify({ alwaysThinkingEnabled: false }, null, 2), | |
| ) | |
| await fs.writeFile( | |
| path.join(tmpDir, 'cc-haha', 'providers.json'), | |
| JSON.stringify({ | |
| activeId: providerId, | |
| providers: [ | |
| { | |
| id: providerId, | |
| presetId: 'deepseek', | |
| name: 'DeepSeek', | |
| apiKey: 'test-key', | |
| baseUrl: `http://127.0.0.1:${server.port}/anthropic`, | |
| apiFormat: 'anthropic', | |
| models: { | |
| main: 'deepseek-v4-pro', | |
| haiku: 'deepseek-v4-pro', | |
| sonnet: 'deepseek-v4-pro', | |
| opus: 'deepseek-v4-pro', | |
| }, | |
| }, | |
| ], | |
| }, null, 2), | |
| ) | |
| await expect(generateTitle('请只回复 trace-ok')).resolves.toBe('Trace ok') | |
| expect(requestBody?.thinking).toEqual({ type: 'disabled' }) | |
| } finally { | |
| server.stop(true) | |
| } | |
| }) | |
| test('derives slash-command titles from command metadata without raw XML tags', () => { | |
| const raw = [ | |
| '<command-message>frontend-design</command-message>', | |
| '<command-name>/frontend-design</command-name>', | |
| '<command-args>@website 重新设计首页</command-args>', | |
| ].join('\n') | |
| expect(deriveTitle(raw)).toBe('/frontend-design @website 重新设计首页') | |
| }) | |
| test('sends cleaned slash-command text to the title model', async () => { | |
| let requestBody: { | |
| messages?: Array<{ content?: string }> | |
| } | null = null | |
| const server = Bun.serve({ | |
| hostname: '127.0.0.1', | |
| port: 0, | |
| async fetch(req) { | |
| requestBody = await req.json() as { | |
| messages?: Array<{ content?: string }> | |
| } | |
| return Response.json({ | |
| content: [{ type: 'text', text: '{"title":"Redesign website"}' }], | |
| }) | |
| }, | |
| }) | |
| try { | |
| const providerId = 'title-clean-test' | |
| await fs.mkdir(path.join(tmpDir, 'cc-haha'), { recursive: true }) | |
| await fs.writeFile( | |
| path.join(tmpDir, 'cc-haha', 'providers.json'), | |
| JSON.stringify({ | |
| activeId: providerId, | |
| providers: [ | |
| { | |
| id: providerId, | |
| presetId: 'anthropic', | |
| name: 'Anthropic', | |
| apiKey: 'test-key', | |
| baseUrl: `http://127.0.0.1:${server.port}/anthropic`, | |
| apiFormat: 'anthropic', | |
| models: { | |
| main: 'claude-sonnet-4-7', | |
| haiku: 'claude-haiku-4-5', | |
| sonnet: 'claude-sonnet-4-7', | |
| opus: 'claude-opus-4-7', | |
| }, | |
| }, | |
| ], | |
| }, null, 2), | |
| ) | |
| await expect(generateTitle([ | |
| '<command-message>frontend-design</command-message>', | |
| '<command-name>/frontend-design</command-name>', | |
| '<command-args>@website 重新设计首页</command-args>', | |
| ].join('\n'))).resolves.toBe('Redesign website') | |
| expect(requestBody?.messages?.[0]?.content).toBe('/frontend-design @website 重新设计首页') | |
| } finally { | |
| server.stop(true) | |
| } | |
| }) | |
| test('generates titles when ChatGPT Official OAuth is active', async () => { | |
| const providerService = new ProviderService() | |
| await providerService.activateProvider('openai-official') | |
| await hahaOpenAIOAuthService.saveTokens({ | |
| accessToken: 'access-for-title', | |
| refreshToken: 'refresh-for-title', | |
| expiresAt: Date.now() + 60 * 60_000, | |
| accountId: 'acct_title', | |
| email: 'title@example.com', | |
| }) | |
| const upstreamCalls: Array<{ | |
| url: string | |
| headers: Record<string, string> | |
| body: Record<string, unknown> | |
| }> = [] | |
| globalThis.fetch = (async (input, init) => { | |
| const headers = new Headers(init?.headers) | |
| upstreamCalls.push({ | |
| url: String(input), | |
| headers: Object.fromEntries(headers.entries()), | |
| body: JSON.parse(String(init?.body)) as Record<string, unknown>, | |
| }) | |
| return new Response([ | |
| 'event: response.completed', | |
| 'data: {"response":{"id":"resp_title","object":"response","created_at":1779118000,"model":"gpt-5.3-codex","status":"completed","output":[{"type":"message","role":"assistant","content":[{"type":"output_text","text":"{\\"title\\":\\"Trace ok\\"}"}]}],"usage":{"input_tokens":3,"output_tokens":2,"total_tokens":5}}}', | |
| '', | |
| ].join('\n'), { | |
| headers: { 'Content-Type': 'text/event-stream' }, | |
| }) | |
| }) as typeof fetch | |
| await expect(generateTitle('请只回复 trace-ok')).resolves.toBe('Trace ok') | |
| expect(upstreamCalls).toHaveLength(1) | |
| expect(upstreamCalls[0].url).toBe(OPENAI_CODEX_API_ENDPOINT) | |
| expect(upstreamCalls[0].headers.authorization).toBe('Bearer access-for-title') | |
| expect(upstreamCalls[0].headers['chatgpt-account-id']).toBe('acct_title') | |
| expect(upstreamCalls[0].body.stream).toBe(true) | |
| }) | |
| test('parses JSON title responses wrapped in markdown fences', () => { | |
| expect(parseGeneratedTitleText('```json\n{"title":"Write bash script"}\n```')) | |
| .toBe('Write bash script') | |
| }) | |
| test('parses escaped JSON title responses', () => { | |
| expect(parseGeneratedTitleText('```json\n{\\"title\\":\\"Write bash script\\"}\n```')) | |
| .toBe('Write bash script') | |
| }) | |
| test('rejects incomplete JSON title fragments instead of using them as titles', () => { | |
| expect(parseGeneratedTitleText('```json\n{\\"title\\":')).toBeNull() | |
| }) | |
| test('normalizes XML-like title model output before persisting it', () => { | |
| expect(parseGeneratedTitleText([ | |
| '<command-message>frontend-design</command-message>', | |
| '<command-name>/frontend-design</command-name>', | |
| '<command-args>@website</command-args>', | |
| ].join(' '))).toBe('/frontend-design @website') | |
| }) | |
| test('does not persist automatic titles over a user custom title', async () => { | |
| const { sessionId } = await sessionService.createSession(os.tmpdir()) | |
| await sessionService.renameSession(sessionId, 'My fixed name') | |
| await expect(saveAiTitle(sessionId, 'Automatic topic')).resolves.toBe(false) | |
| const detail = await sessionService.getSession(sessionId) | |
| expect(detail?.title).toBe('My fixed name') | |
| const found = await sessionService.findSessionFile(sessionId) | |
| expect(found).not.toBeNull() | |
| const content = await fs.readFile(found!.filePath, 'utf-8') | |
| expect(content).not.toContain('"type":"ai-title"') | |
| }) | |
| }) | |
| function restoreEnv(key: string, value: string | undefined) { | |
| if (value === undefined) { | |
| delete process.env[key] | |
| } else { | |
| process.env[key] = value | |
| } | |
| } | |