Download packages/kap-server/test/boot.test.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/kap-server/test/boot.test.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/kap-server/test/boot.test.ts
-
curl -L -o boot.test.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/kap-server/test/boot.test.ts
15.7 kB
| import { mkdtemp, rm, writeFile } from 'node:fs/promises'; | |
| import { createServer, type Server } from 'node:net'; | |
| import { tmpdir } from 'node:os'; | |
| import { join } from 'node:path'; | |
| import { Writable } from 'node:stream'; | |
| import { pino } from 'pino'; | |
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |
| import { | |
| IBootstrapService, | |
| IFileSystemStorageService, | |
| IHostRequestHeaders, | |
| InMemoryStorageService, | |
| IOAuthToolkit, | |
| ITelemetryService, | |
| noopTelemetryService, | |
| } from '@moonshot-ai/agent-core-v2'; | |
| import { listLiveServerInstances } from '../src/instanceRegistry'; | |
| import { listenWithPortRetry, type RunningServer, startServer } from '../src/start'; | |
| import { TEST_HOST_IDENTITY } from './helpers/hostIdentity'; | |
| import { authedFetch } from './helpers/auth'; | |
| describe('server-v2 boot', () => { | |
| let server: RunningServer | undefined; | |
| let home: string | undefined; | |
| afterEach(async () => { | |
| if (server !== undefined) { | |
| await server.close(); | |
| server = undefined; | |
| } | |
| if (home !== undefined) { | |
| await rm(home, { recursive: true, force: true }); | |
| home = undefined; | |
| } | |
| }); | |
| it('boots agent-core-v2 and serves the basic /api/v1 routes', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-')); | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| }); | |
| const base = `http://127.0.0.1:${server.port}`; | |
| const healthz = await fetch(`${base}/api/v1/healthz`); | |
| expect(healthz.status).toBe(200); | |
| const healthBody = await healthz.json() as { | |
| code: number; | |
| data: { ok: boolean }; | |
| request_id: string; | |
| }; | |
| expect(healthBody.code).toBe(0); | |
| expect(healthBody.data.ok).toBe(true); | |
| expect(typeof healthBody.request_id).toBe('string'); | |
| const meta = await authedFetch(server, base, '/api/v1/meta'); | |
| expect(meta.status).toBe(200); | |
| const metaBody = await meta.json() as { | |
| code: number; | |
| data: { server_id: string; server_version: string; capabilities: Record<string, boolean> }; | |
| }; | |
| expect(metaBody.code).toBe(0); | |
| expect(typeof metaBody.data.server_id).toBe('string'); | |
| expect(typeof metaBody.data.server_version).toBe('string'); | |
| expect(metaBody.data.capabilities).toBeDefined(); | |
| const auth = await authedFetch(server, base, '/api/v1/auth'); | |
| expect(auth.status).toBe(200); | |
| const authBody = await auth.json() as { | |
| code: number; | |
| data: { models_ready: boolean; providers_count: number }; | |
| }; | |
| expect(authBody.code).toBe(0); | |
| expect(typeof authBody.data.models_ready).toBe('boolean'); | |
| expect(authBody.data.providers_count).toBeGreaterThanOrEqual(0); | |
| const oauthPoll = await authedFetch(server, base, '/api/v1/oauth/login'); | |
| expect(oauthPoll.status).toBe(200); | |
| const oauthBody = await oauthPoll.json() as { code: number; data: null }; | |
| expect(oauthBody.code).toBe(0); | |
| expect(oauthBody.data).toBeNull(); | |
| }); | |
| it('reports opts.serverVersion as server_version instead of the package version', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-version-')); | |
| server = await startServer({ | |
| hostIdentity: { | |
| productName: 'test-host', | |
| version: '9.9.9-host', | |
| platform: 'test_platform', | |
| }, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| serverVersion: '9.9.9-host', | |
| }); | |
| const base = `http://127.0.0.1:${server.port}`; | |
| const meta = await authedFetch(server, base, '/api/v1/meta'); | |
| const metaBody = await meta.json() as { | |
| code: number; | |
| data: { server_version: string }; | |
| }; | |
| expect(metaBody.data.server_version).toBe('9.9.9-host'); | |
| const [instance] = await listLiveServerInstances(home); | |
| expect(instance?.serverVersion).toBe('9.9.9-host'); | |
| const defaults = server.core.accessor.get(IHostRequestHeaders); | |
| expect(defaults.headers['User-Agent']).toBe('test-host/9.9.9-host'); | |
| expect(server.core.accessor.get(IBootstrapService).clientIdentity).toEqual({ | |
| productName: 'test-host', | |
| version: '9.9.9-host', | |
| platform: 'test_platform', | |
| }); | |
| }); | |
| it('seeds default Kimi identity headers from hostIdentity that opts.seeds can override', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-ua-')); | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| }); | |
| const defaults = server.core.accessor.get(IHostRequestHeaders); | |
| expect(defaults.headers['User-Agent']).toBe('test-host/0.0.0-test'); | |
| expect(defaults.headers['X-Msh-Version']).toBe('0.0.0-test'); | |
| expect(defaults.headers['X-Msh-Platform']).toBe('test_platform'); | |
| await server.close(); | |
| server = undefined; | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| seeds: [[IHostRequestHeaders, { headers: { 'User-Agent': 'custom-host/9.9' } }]], | |
| }); | |
| const overridden = server.core.accessor.get(IHostRequestHeaders); | |
| expect(overridden.headers['User-Agent']).toBe('custom-host/9.9'); | |
| }); | |
| it('seeds explicit skill dirs into the core scope when skillDirs is provided', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-skills-')); | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| skillDirs: ['/skills/explicit'], | |
| }); | |
| expect(server.core.accessor.get(IBootstrapService).args.skillDirs).toEqual([ | |
| '/skills/explicit', | |
| ]); | |
| await server.close(); | |
| server = undefined; | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| }); | |
| expect(server.core.accessor.get(IBootstrapService).args.skillDirs).toBeUndefined(); | |
| }); | |
| it('does not shut down a host-injected telemetry service when server telemetry is disabled', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-host-telemetry-')); | |
| await writeFile(join(home, 'config.toml'), 'telemetry = false\n', 'utf8'); | |
| const shutdown = vi.fn(async () => {}); | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| seeds: [[ITelemetryService, { ...noopTelemetryService, shutdown }]], | |
| }); | |
| await server.close(); | |
| server = undefined; | |
| expect(shutdown).not.toHaveBeenCalled(); | |
| }); | |
| it('completes server cleanup when owned telemetry shutdown fails', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-telemetry-failure-')); | |
| const storage = new InMemoryStorageService(); | |
| const write = storage.write.bind(storage); | |
| vi.spyOn(storage, 'write').mockImplementation(async (scope, key, data, options) => { | |
| if (scope === 'telemetry') throw new Error('telemetry storage unavailable'); | |
| await write(scope, key, data, options); | |
| }); | |
| const auth = { | |
| _serviceBrand: undefined, | |
| getCachedAccessToken: async () => { | |
| throw new Error('telemetry auth unavailable'); | |
| }, | |
| } as unknown as IOAuthToolkit; | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| telemetry: true, | |
| seeds: [ | |
| [IFileSystemStorageService, storage], | |
| [IOAuthToolkit, auth], | |
| ], | |
| }); | |
| const core = server.core; | |
| core.accessor.get(ITelemetryService).track2('session_ended', { reason: 'exit' }); | |
| await server.close(); | |
| server = undefined; | |
| expect(() => core.accessor.get(IBootstrapService)).toThrow(); | |
| expect(await listLiveServerInstances(home)).toEqual([]); | |
| }); | |
| it('logs process-level exceptions without exiting and removes the handlers on close', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-')); | |
| const lines: string[] = []; | |
| const stream = new Writable({ | |
| write(chunk, _encoding, callback) { | |
| lines.push(String(chunk)); | |
| callback(); | |
| }, | |
| }); | |
| const rejectionBefore = process.listeners('unhandledRejection'); | |
| const exceptionBefore = process.listeners('uncaughtException'); | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logger: pino({ level: 'error' }, stream), | |
| }); | |
| expect(process.listenerCount('unhandledRejection')).toBe(rejectionBefore.length + 1); | |
| expect(process.listenerCount('uncaughtException')).toBe(exceptionBefore.length + 1); | |
| const onUncaughtException = process | |
| .listeners('uncaughtException') | |
| .find((listener) => !exceptionBefore.includes(listener)) as | |
| | ((error: Error) => void) | |
| | undefined; | |
| const onUnhandledRejection = process | |
| .listeners('unhandledRejection') | |
| .find((listener) => !rejectionBefore.includes(listener)) as | |
| | ((reason: unknown) => void) | |
| | undefined; | |
| expect(onUncaughtException).toBeDefined(); | |
| expect(onUnhandledRejection).toBeDefined(); | |
| onUncaughtException?.(new Error('synthetic uncaught')); | |
| onUnhandledRejection?.(new Error('synthetic rejection')); | |
| const output = lines.join(''); | |
| expect(output).toContain('"msg":"uncaughtException"'); | |
| expect(output).toContain('"msg":"unhandledRejection"'); | |
| const healthz = await fetch(`http://127.0.0.1:${server.port}/api/v1/healthz`); | |
| expect(healthz.status).toBe(200); | |
| await server.close(); | |
| server = undefined; | |
| expect(process.listenerCount('unhandledRejection')).toBe(rejectionBefore.length); | |
| expect(process.listenerCount('uncaughtException')).toBe(exceptionBefore.length); | |
| }); | |
| it('does not leave process handlers installed when startup fails', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-')); | |
| const emptyAssets = await mkdtemp(join(tmpdir(), 'kimi-server-v2-assets-')); | |
| const rejectionBefore = process.listenerCount('unhandledRejection'); | |
| const exceptionBefore = process.listenerCount('uncaughtException'); | |
| try { | |
| await expect( | |
| startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port: 0, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| webAssetsDir: emptyAssets, | |
| }), | |
| ).rejects.toThrow('web assets'); | |
| expect(process.listenerCount('unhandledRejection')).toBe(rejectionBefore); | |
| expect(process.listenerCount('uncaughtException')).toBe(exceptionBefore); | |
| } finally { | |
| await rm(emptyAssets, { recursive: true, force: true }); | |
| } | |
| }); | |
| }); | |
| function silentLogger() { | |
| return pino({ level: 'silent' }); | |
| } | |
| function addrInUse(): NodeJS.ErrnoException { | |
| const err = new Error('listen EADDRINUSE') as NodeJS.ErrnoException; | |
| err.code = 'EADDRINUSE'; | |
| return err; | |
| } | |
| function listenOnPort(host: string, port: number): Promise<Server> { | |
| return new Promise((resolve, reject) => { | |
| const server = createServer(); | |
| server.once('error', reject); | |
| server.listen({ host, port }, () => resolve(server)); | |
| }); | |
| } | |
| function closeNetServer(server: Server): Promise<void> { | |
| return new Promise((resolve) => server.close(() => resolve())); | |
| } | |
| async function allocateAdjacentFreePair( | |
| host = '127.0.0.1', | |
| ): Promise<{ port: number; next: number }> { | |
| for (let i = 0; i < 30; i++) { | |
| const a = await listenOnPort(host, 0); | |
| const address = a.address(); | |
| const port = typeof address === 'object' && address !== null ? address.port : 0; | |
| await closeNetServer(a); | |
| if (port <= 0 || port >= 65535) continue; | |
| const probe = await listenOnPort(host, port + 1).catch(() => null); | |
| if (probe === null) continue; | |
| await closeNetServer(probe); | |
| return { port, next: port + 1 }; | |
| } | |
| throw new Error('could not allocate an adjacent free port pair'); | |
| } | |
| describe('listenWithPortRetry', () => { | |
| it('returns the requested port when the first listen succeeds', async () => { | |
| const attempts: number[] = []; | |
| const result = await listenWithPortRetry({ | |
| listen: async (_host, port) => { | |
| attempts.push(port); | |
| return `http://127.0.0.1:${String(port)}`; | |
| }, | |
| host: '127.0.0.1', | |
| port: 5000, | |
| logger: silentLogger(), | |
| }); | |
| expect(result.port).toBe(5000); | |
| expect(attempts).toEqual([5000]); | |
| }); | |
| it('retries with port+1 on EADDRINUSE until a bind succeeds', async () => { | |
| const attempts: number[] = []; | |
| const result = await listenWithPortRetry({ | |
| listen: async (_host, port) => { | |
| attempts.push(port); | |
| if (port < 5002) throw addrInUse(); | |
| return `http://127.0.0.1:${String(port)}`; | |
| }, | |
| host: '127.0.0.1', | |
| port: 5000, | |
| logger: silentLogger(), | |
| }); | |
| expect(result.port).toBe(5002); | |
| expect(result.address).toBe('http://127.0.0.1:5002'); | |
| expect(attempts).toEqual([5000, 5001, 5002]); | |
| }); | |
| it('does not retry on non-EADDRINUSE errors', async () => { | |
| const attempts: number[] = []; | |
| const boom = Object.assign(new Error('listen EACCES'), { code: 'EACCES' }); | |
| await expect( | |
| listenWithPortRetry({ | |
| listen: async (_host, port) => { | |
| attempts.push(port); | |
| throw boom; | |
| }, | |
| host: '127.0.0.1', | |
| port: 5000, | |
| logger: silentLogger(), | |
| }), | |
| ).rejects.toBe(boom); | |
| expect(attempts).toEqual([5000]); | |
| }); | |
| it('throws after exhausting maxRetries', async () => { | |
| const attempts: number[] = []; | |
| await expect( | |
| listenWithPortRetry({ | |
| listen: async (_host, port) => { | |
| attempts.push(port); | |
| throw addrInUse(); | |
| }, | |
| host: '127.0.0.1', | |
| port: 5000, | |
| logger: silentLogger(), | |
| maxRetries: 3, | |
| }), | |
| ).rejects.toMatchObject({ code: 'EADDRINUSE' }); | |
| expect(attempts).toEqual([5000, 5001, 5002, 5003]); | |
| }); | |
| it('does not walk ports when the requested port is 0 (ephemeral)', async () => { | |
| const attempts: number[] = []; | |
| const result = await listenWithPortRetry({ | |
| listen: async (_host, port) => { | |
| attempts.push(port); | |
| return 'http://127.0.0.1:54321'; | |
| }, | |
| host: '127.0.0.1', | |
| port: 0, | |
| logger: silentLogger(), | |
| }); | |
| expect(result.port).toBe(0); | |
| expect(attempts).toEqual([0]); | |
| }); | |
| }); | |
| describe('server-v2 boot — port retry', () => { | |
| let server: RunningServer | undefined; | |
| let home: string | undefined; | |
| afterEach(async () => { | |
| if (server !== undefined) { | |
| await server.close(); | |
| server = undefined; | |
| } | |
| if (home !== undefined) { | |
| await rm(home, { recursive: true, force: true }); | |
| home = undefined; | |
| } | |
| }); | |
| it('retries on port+1 and advertises the bound port in the instance registry', async () => { | |
| home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-port-retry-')); | |
| const { port, next } = await allocateAdjacentFreePair(); | |
| const occupant = await listenOnPort('127.0.0.1', port); | |
| try { | |
| server = await startServer({ | |
| hostIdentity: TEST_HOST_IDENTITY, | |
| host: '127.0.0.1', | |
| port, | |
| homeDir: home, | |
| logLevel: 'silent', | |
| }); | |
| expect(server.port).toBeGreaterThanOrEqual(next); | |
| const [instance] = await listLiveServerInstances(home); | |
| expect(instance?.port).toBe(server.port); | |
| } finally { | |
| await closeNetServer(occupant); | |
| } | |
| }); | |
| }); | |