Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/kap-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"build": "tsdown",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "vitest run",
"test:bench": "vitest run --config vitest.bench.config.ts",
"clean": "rm -rf dist"
},
"dependencies": {
Expand Down
33 changes: 17 additions & 16 deletions packages/kap-server/test/apiSurface.snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { afterEach, describe, expect, it } from 'vitest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import { startServer, type RunningServer } from '../src';
import { TEST_HOST_IDENTITY } from './helpers/hostIdentity';
Expand All @@ -25,7 +25,19 @@ describe('API surface snapshot', () => {
let home: string | undefined;
let server: RunningServer | undefined;

afterEach(async () => {
beforeAll(async () => {
home = mkdtempSync(join(tmpdir(), 'kimi-server-v2-api-surface-'));
server = await startServer({
hostIdentity: TEST_HOST_IDENTITY,
host: '127.0.0.1',
port: 0,
homeDir: home,
logLevel: 'silent',
debugEndpoints: true,
});
});

afterAll(async () => {
if (server !== undefined) {
try {
await server.close();
Expand All @@ -40,20 +52,9 @@ describe('API surface snapshot', () => {
});

it('matches the documented v2 route table and meta endpoints', async () => {
home = mkdtempSync(join(tmpdir(), 'kimi-server-v2-api-surface-'));

server = await startServer({
hostIdentity: TEST_HOST_IDENTITY,
host: '127.0.0.1',
port: 0,
homeDir: home,
logLevel: 'silent',
debugEndpoints: true,
});

const base = `http://${server.host}:${server.port}`;
const base = `http://${server!.host}:${server!.port}`;

const openApiRes = await fetch(`${base}/openapi.json`, { headers: authHeaders(server) } as never);
const openApiRes = await fetch(`${base}/openapi.json`, { headers: authHeaders(server as RunningServer) } as never);
expect(openApiRes.status).toBe(200);
const openApi = (await openApiRes.json()) as {
paths?: Record<string, Record<string, unknown>>;
Expand All @@ -73,7 +74,7 @@ describe('API surface snapshot', () => {

const meta: Array<[string, string, number]> = [];
for (const endpoint of META_ENDPOINTS) {
const res = await fetch(`${base}${endpoint}`, { headers: authHeaders(server) } as never);
const res = await fetch(`${base}${endpoint}`, { headers: authHeaders(server as RunningServer) } as never);
meta.push(['GET', endpoint, res.status]);
}
meta.sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1]) || a[2] - b[2]);
Expand Down
6 changes: 3 additions & 3 deletions packages/kap-server/test/approvals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { ISessionApprovalService, ensureMainAgent, getLiveSessionById } from '@moonshot-ai/agent-core-v2';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import { type RunningServer, startServer } from '../src/start';
import { TEST_HOST_IDENTITY } from './helpers/hostIdentity';
Expand Down Expand Up @@ -43,7 +43,7 @@ describe('server-v2 /api/v1/sessions/{sid}/approvals', () => {
let home: string | undefined;
let base: string;

beforeEach(async () => {
beforeAll(async () => {
home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-approvals-'));
server = await startServer({
hostIdentity: TEST_HOST_IDENTITY,
Expand All @@ -55,7 +55,7 @@ describe('server-v2 /api/v1/sessions/{sid}/approvals', () => {
base = `http://127.0.0.1:${server.port}`;
});

afterEach(async () => {
afterAll(async () => {
if (server !== undefined) {
await server.close();
server = undefined;
Expand Down
28 changes: 14 additions & 14 deletions packages/kap-server/test/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { IConfigService } from '@moonshot-ai/agent-core-v2';
import { authSummarySchema, type AuthSummary } from '@moonshot-ai/agent-core-v2/app/authLegacy/authLegacy';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import { type RunningServer, startServer } from '../src/start';
import { TEST_HOST_IDENTITY } from './helpers/hostIdentity';
Expand All @@ -21,11 +22,19 @@ describe('server-v2 GET /api/v1/auth', () => {
let home: string | undefined;
let base: string;

beforeEach(async () => {
beforeAll(async () => {
home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-auth-'));
server = await startServer({
hostIdentity: TEST_HOST_IDENTITY,
host: '127.0.0.1',
port: 0,
homeDir: home,
logLevel: 'silent',
});
base = `http://127.0.0.1:${server.port}`;
});

afterEach(async () => {
afterAll(async () => {
if (server !== undefined) {
await server.close();
server = undefined;
Expand All @@ -37,17 +46,8 @@ describe('server-v2 GET /api/v1/auth', () => {
});

async function boot(toml?: string): Promise<void> {
if (toml !== undefined) {
await writeFile(join(home as string, 'config.toml'), toml, 'utf-8');
}
server = await startServer({
hostIdentity: TEST_HOST_IDENTITY,
host: '127.0.0.1',
port: 0,
homeDir: home,
logLevel: 'silent',
});
base = `http://127.0.0.1:${server.port}`;
await writeFile(join(home as string, 'config.toml'), toml ?? '', 'utf-8');
await (server as RunningServer).core.accessor.get(IConfigService).reload();
}

async function getAuth(): Promise<AuthSummary> {
Expand Down
24 changes: 10 additions & 14 deletions packages/kap-server/test/authMiddleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import { type RunningServer, startServer } from '../src/start';
import { TEST_HOST_IDENTITY } from './helpers/hostIdentity';
Expand All @@ -11,11 +11,12 @@ describe('server-v2 /api/v1 bearer auth', () => {
let server: RunningServer | undefined;
let home: string | undefined;

beforeEach(async () => {
beforeAll(async () => {
home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-auth-middleware-'));
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
});

afterEach(async () => {
afterAll(async () => {
if (server !== undefined) {
await server.close();
server = undefined;
Expand All @@ -27,22 +28,19 @@ describe('server-v2 /api/v1 bearer auth', () => {
});

it('allows healthz without a token', async () => {
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
const res = await server.app.inject({ method: 'GET', url: '/api/v1/healthz' });
const res = await server!.app.inject({ method: 'GET', url: '/api/v1/healthz' });
expect(res.statusCode).toBe(200);
});

it('rejects /api/v1/auth without a token with 40101', async () => {
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
const res = await server.app.inject({ method: 'GET', url: '/api/v1/auth' });
const res = await server!.app.inject({ method: 'GET', url: '/api/v1/auth' });
expect(res.statusCode).toBe(401);
const body = res.json() as Record<string, unknown>;
expect(body['code']).toBe(40101);
});

it('rejects /api/v1/auth with a wrong token', async () => {
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
const res = await server.app.inject({
const res = await server!.app.inject({
method: 'GET',
url: '/api/v1/auth',
headers: { authorization: 'Bearer wrong-token' },
Expand All @@ -53,9 +51,8 @@ describe('server-v2 /api/v1 bearer auth', () => {
});

it('accepts /api/v1/auth with the persistent token', async () => {
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
const token = server.authTokenService.getToken();
const res = await server.app.inject({
const token = server!.authTokenService.getToken();
const res = await server!.app.inject({
method: 'GET',
url: '/api/v1/auth',
headers: { authorization: `Bearer ${token}` },
Expand All @@ -66,8 +63,7 @@ describe('server-v2 /api/v1 bearer auth', () => {
});

it('requires auth for /openapi.json', async () => {
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
const res = await server.app.inject({ method: 'GET', url: '/openapi.json' });
const res = await server!.app.inject({ method: 'GET', url: '/openapi.json' });
expect(res.statusCode).toBe(401);
});
});
16 changes: 12 additions & 4 deletions packages/kap-server/test/authWiring.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mkdtemp, readFile, rm, stat } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { WebSocket, type RawData } from 'ws';

import { type RunningServer, startServer } from '../src/start';
Expand Down Expand Up @@ -58,19 +58,26 @@ describe('production auth wiring', () => {
let base: string;
const sockets: WebSocket[] = [];

beforeEach(async () => {
beforeAll(async () => {
home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-auth-wiring-'));
await boot();
});

async function boot(): Promise<void> {
server = await startServer({ hostIdentity: TEST_HOST_IDENTITY, host: '127.0.0.1', port: 0, homeDir: home, logLevel: 'silent' });
base = `http://127.0.0.1:${server.port}`;
});
}

afterEach(async () => {
afterEach(() => {
for (const ws of sockets.splice(0)) {
try {
ws.close();
} catch {
}
}
});

afterAll(async () => {
if (server !== undefined) {
await server.close();
server = undefined;
Expand All @@ -92,6 +99,7 @@ describe('production auth wiring', () => {
server = undefined;
const after = await stat(p);
expect(after.mode & 0o777).toBe(0o600);
await boot();
});

it('gates HTTP: 200 with the token, 401 without', async () => {
Expand Down
45 changes: 6 additions & 39 deletions packages/kap-server/test/capabilities.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { describe, expect, it } from 'vitest';

import {
capabilityStatusSchema,
listCapabilitiesResponseSchema,
} from '../src/protocol/rest-capability';
import { type RunningServer, startServer } from '../src/start';
import { TEST_HOST_IDENTITY } from './helpers/hostIdentity';
import { authHeaders } from './helpers/auth';
import { sharedAuthHeaders, sharedServer } from './helpers/sharedServer';

interface Envelope<T> {
code: number;
Expand All @@ -20,44 +14,17 @@ interface Envelope<T> {
}

describe('server-v2 /api/v1 capabilities', () => {
let server: RunningServer | undefined;
let home: string | undefined;
let base: string;

beforeEach(async () => {
home = await mkdtemp(join(tmpdir(), 'kimi-server-v2-capabilities-'));
server = await startServer({
hostIdentity: TEST_HOST_IDENTITY,
host: '127.0.0.1',
port: 0,
homeDir: home,
logLevel: 'silent',
});
base = `http://127.0.0.1:${server.port}`;
});

afterEach(async () => {
if (server !== undefined) {
await server.close();
server = undefined;
}
if (home !== undefined) {
await rm(home, { recursive: true, force: true, maxRetries: 3, retryDelay: 25 } as never);
home = undefined;
}
});

async function getJson<T>(path: string): Promise<{ status: number; body: Envelope<T> }> {
const res = await fetch(`${base}${path}`, {
headers: authHeaders(server as RunningServer),
const res = await fetch(`${sharedServer().base}${path}`, {
headers: sharedAuthHeaders(),
} as never);
return { status: res.status, body: (await res.json()) as Envelope<T> };
}

async function postJson<T>(path: string): Promise<{ status: number; body: Envelope<T> }> {
const res = await fetch(`${base}${path}`, {
const res = await fetch(`${sharedServer().base}${path}`, {
method: 'POST',
headers: authHeaders(server as RunningServer, { 'content-type': 'application/json' }),
headers: sharedAuthHeaders({ 'content-type': 'application/json' }),
body: '{}',
} as never);
return { status: res.status, body: (await res.json()) as Envelope<T> };
Expand Down
Loading
Loading