Download apps/kimi-inspect/src/sessions/api.test.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 8.53 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/apps/kimi-inspect/src/sessions/api.test.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/apps/kimi-inspect/src/sessions/api.test.ts
-
curl -L -o api.test.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/apps/kimi-inspect/src/sessions/api.test.ts
8.53 kB
| /** | |
| * Tests for the v2 session list REST client (`GET /api/v2/sessions`): query | |
| * string serialization, `{ code, msg, data }` envelope parsing, and error | |
| * mapping (business codes on 2xx, infra statuses on non-2xx). | |
| */ | |
| import { describe, expect, it } from 'vitest'; | |
| import { fetchV2SessionGroups, fetchV2SessionsPage } from './api'; | |
| function fakeFetch(status: number, body: unknown) { | |
| const calls: { url: string; init?: RequestInit }[] = []; | |
| const fetchImpl = (async (url: string | URL, init?: RequestInit) => { | |
| calls.push({ url: String(url), init }); | |
| return { ok: status >= 200 && status < 300, status, statusText: 'ERR', json: async () => body }; | |
| }) as unknown as typeof fetch; | |
| return { calls, fetchImpl }; | |
| } | |
| /** The shared REST envelope the server wraps every response in. */ | |
| function okBody(data: unknown) { | |
| return { code: 0, msg: 'success', data, request_id: 'req-1' }; | |
| } | |
| function errBody(code: number, msg: string) { | |
| return { code, msg, data: null, request_id: 'req-1' }; | |
| } | |
| const pageData = { | |
| items: [ | |
| { | |
| id: 's1', | |
| workspace: { id: 'ws1', cwd: '/tmp/proj' }, | |
| meta: { | |
| title: '重构会话', | |
| last_prompt: null, | |
| created_at: 1_700_000_000_000, | |
| updated_at: 1_700_000_100_000, | |
| archived: false, | |
| }, | |
| activity: { status: 'running' }, | |
| git: { | |
| branch: 'main', | |
| pull_request: { number: 12, state: 'draft', url: 'https://example.com/pr/12' }, | |
| }, | |
| }, | |
| { | |
| id: 's2', | |
| workspace: { id: 'ws2', cwd: null }, | |
| meta: { | |
| title: null, | |
| last_prompt: 'hello', | |
| created_at: 1_700_000_000_000, | |
| updated_at: 1_700_000_050_000, | |
| archived: true, | |
| }, | |
| activity: { status: 'idle' }, | |
| }, | |
| // Malformed entries are dropped, not fatal. | |
| { id: 42 }, | |
| ], | |
| has_more: true, | |
| next_page_token: 'tok-next', | |
| }; | |
| describe('fetchV2SessionsPage', () => { | |
| it('serializes filters as repeated query params and maps items to camelCase', async () => { | |
| const { calls, fetchImpl } = fakeFetch(200, okBody(pageData)); | |
| const page = await fetchV2SessionsPage({ | |
| baseUrl: 'http://h:1', | |
| token: 'tok', | |
| workspaceIds: ['ws1', 'ws2'], | |
| statuses: ['approval', 'question'], | |
| updatedAfter: 1_700_000_000_000, | |
| archived: 'all', | |
| sort: 'meta.created_at_desc', | |
| includeGit: true, | |
| pageSize: 20, | |
| pageToken: 'tok-prev', | |
| fetchImpl, | |
| }); | |
| expect(calls).toHaveLength(1); | |
| const url = new URL(calls[0]!.url); | |
| expect(`${url.origin}${url.pathname}`).toBe('http://h:1/api/v2/sessions'); | |
| expect(url.searchParams.getAll('workspace.id')).toEqual(['ws1', 'ws2']); | |
| expect(url.searchParams.getAll('activity.status')).toEqual(['approval', 'question']); | |
| expect(url.searchParams.get('meta.updated_after')).toBe('1700000000000'); | |
| expect(url.searchParams.get('meta.archived')).toBe('all'); | |
| expect(url.searchParams.get('sort')).toBe('meta.created_at_desc'); | |
| expect(url.searchParams.get('include')).toBe('git'); | |
| expect(url.searchParams.get('page_size')).toBe('20'); | |
| expect(url.searchParams.get('page_token')).toBe('tok-prev'); | |
| expect(calls[0]!.init?.headers).toEqual({ authorization: 'Bearer tok' }); | |
| expect(page.items).toHaveLength(2); | |
| const first = page.items[0]!; | |
| expect(first.id).toBe('s1'); | |
| expect(first.workspace).toEqual({ id: 'ws1', cwd: '/tmp/proj' }); | |
| expect(first.meta).toEqual({ | |
| title: '重构会话', | |
| lastPrompt: null, | |
| createdAt: 1_700_000_000_000, | |
| updatedAt: 1_700_000_100_000, | |
| archived: false, | |
| }); | |
| expect(first.activity.status).toBe('running'); | |
| // Draft PRs are not in the v2 enum; an unknown state degrades to null. | |
| expect(first.git?.branch).toBe('main'); | |
| expect(first.git?.pullRequest).toBeNull(); | |
| const second = page.items[1]!; | |
| expect(second.meta.title).toBeNull(); | |
| expect(second.meta.lastPrompt).toBe('hello'); | |
| expect(second.git).toBeUndefined(); | |
| expect(page.hasMore).toBe(true); | |
| expect(page.nextPageToken).toBe('tok-next'); | |
| }); | |
| it('omits the query string and authorization header when nothing is set', async () => { | |
| const { calls, fetchImpl } = fakeFetch(200, okBody(pageData)); | |
| await fetchV2SessionsPage({ baseUrl: 'http://h:1', fetchImpl }); | |
| expect(calls[0]!.url).toBe('http://h:1/api/v2/sessions'); | |
| expect(calls[0]!.init?.headers).toEqual({}); | |
| }); | |
| it('parses a well-formed pull_request', async () => { | |
| const { fetchImpl } = fakeFetch( | |
| 200, | |
| okBody({ | |
| ...pageData, | |
| items: [ | |
| { | |
| ...pageData.items[0], | |
| git: { | |
| branch: 'feat/x', | |
| pull_request: { number: 7, state: 'open', url: 'https://example.com/pr/7' }, | |
| }, | |
| }, | |
| ], | |
| }), | |
| ); | |
| const page = await fetchV2SessionsPage({ baseUrl: 'http://h:1', includeGit: true, fetchImpl }); | |
| expect(page.items[0]!.git?.pullRequest).toEqual({ | |
| number: 7, | |
| state: 'open', | |
| url: 'https://example.com/pr/7', | |
| }); | |
| }); | |
| it('throws the envelope code/msg on a business failure (2xx with code != 0)', async () => { | |
| const { fetchImpl } = fakeFetch(200, errBody(40922, 'page_token is corrupted')); | |
| await expect( | |
| fetchV2SessionsPage({ baseUrl: 'http://h:1', pageToken: 'bad', fetchImpl }), | |
| ).rejects.toThrow(/40922.*page_token is corrupted/); | |
| }); | |
| it('throws the envelope code/msg on a non-2xx response (e.g. 401 from the auth hook)', async () => { | |
| const { fetchImpl } = fakeFetch(401, errBody(40101, 'unauthorized')); | |
| await expect(fetchV2SessionsPage({ baseUrl: 'http://h:1', fetchImpl })).rejects.toThrow( | |
| /40101.*unauthorized/, | |
| ); | |
| }); | |
| it('falls back to the HTTP status when the error body is malformed', async () => { | |
| const { fetchImpl } = fakeFetch(500, null); | |
| await expect(fetchV2SessionsPage({ baseUrl: 'http://h:1', fetchImpl })).rejects.toThrow( | |
| /http_500/, | |
| ); | |
| }); | |
| it('throws on a malformed success payload', async () => { | |
| const { fetchImpl } = fakeFetch(200, okBody({ has_more: false })); | |
| await expect(fetchV2SessionsPage({ baseUrl: 'http://h:1', fetchImpl })).rejects.toThrow( | |
| /unexpected response shape/, | |
| ); | |
| }); | |
| }); | |
| describe('fetchV2SessionGroups', () => { | |
| const groupData = { | |
| groups: [ | |
| { | |
| workspace: { id: 'ws1', cwd: '/tmp/proj' }, | |
| sessions: [pageData.items[0]], | |
| total: 7, | |
| }, | |
| { | |
| workspace: { id: 'ws2', cwd: null }, | |
| sessions: [], | |
| total: 0, | |
| }, | |
| // Malformed groups are dropped, not fatal. | |
| { workspace: { cwd: '/x' }, sessions: [], total: 1 }, | |
| { id: 'nope' }, | |
| ], | |
| total: 3, | |
| has_more: true, | |
| next_page_token: 'tok-groups', | |
| }; | |
| it('requests the by_workspace view and parses groups with per-group totals', async () => { | |
| const { calls, fetchImpl } = fakeFetch(200, okBody(groupData)); | |
| const page = await fetchV2SessionGroups({ | |
| baseUrl: 'http://h:1', | |
| token: 'tok', | |
| statuses: ['running'], | |
| sort: 'meta.updated_at_asc', | |
| pageSize: 10, | |
| groupPageSize: 5, | |
| pageToken: 'tok-prev', | |
| fetchImpl, | |
| }); | |
| const url = new URL(calls[0]!.url); | |
| expect(url.searchParams.get('view')).toBe('by_workspace'); | |
| expect(url.searchParams.get('group.page_size')).toBe('5'); | |
| expect(url.searchParams.get('page_size')).toBe('10'); | |
| expect(url.searchParams.get('sort')).toBe('meta.updated_at_asc'); | |
| expect(url.searchParams.get('page_token')).toBe('tok-prev'); | |
| expect(page.groups).toHaveLength(2); | |
| const first = page.groups[0]!; | |
| expect(first.workspace).toEqual({ id: 'ws1', cwd: '/tmp/proj' }); | |
| expect(first.sessions.map((s) => s.id)).toEqual(['s1']); | |
| expect(first.total).toBe(7); | |
| expect(page.groups[1]!.workspace).toEqual({ id: 'ws2', cwd: null }); | |
| expect(page.hasMore).toBe(true); | |
| expect(page.nextPageToken).toBe('tok-groups'); | |
| }); | |
| it('omits group.page_size when not set and throws on a malformed success payload', async () => { | |
| const { calls, fetchImpl } = fakeFetch(200, okBody(groupData)); | |
| await fetchV2SessionGroups({ baseUrl: 'http://h:1', fetchImpl }); | |
| expect(new URL(calls[0]!.url).searchParams.get('group.page_size')).toBeNull(); | |
| const { fetchImpl: broken } = fakeFetch(200, okBody({ has_more: false })); | |
| await expect(fetchV2SessionGroups({ baseUrl: 'http://h:1', fetchImpl: broken })).rejects.toThrow( | |
| /unexpected response shape/, | |
| ); | |
| }); | |
| }); | |