File size: 3,498 Bytes
4e23b01 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import { describe, expect, it } from 'vitest';
import {
capabilityStatusSchema,
listCapabilitiesResponseSchema,
} from '../src/protocol/rest-capability';
import { sharedAuthHeaders, sharedServer } from './helpers/sharedServer';
interface Envelope<T> {
code: number;
msg: string;
data: T;
request_id: string;
}
describe('server-v2 /api/v1 capabilities', () => {
async function getJson<T>(path: string): Promise<{ status: number; body: Envelope<T> }> {
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(`${sharedServer().base}${path}`, {
method: 'POST',
headers: sharedAuthHeaders({ 'content-type': 'application/json' }),
body: '{}',
} as never);
return { status: res.status, body: (await res.json()) as Envelope<T> };
}
it('lists both built-in capabilities with the documented shape', async () => {
const { body } = await getJson<unknown>('/api/v1/capabilities');
expect(body.code).toBe(0);
const parsed = listCapabilitiesResponseSchema.parse(body.data);
const ids = parsed.capabilities.map((c) => c.id).toSorted();
expect(ids).toEqual(['kimi-cu', 'kimi-webbridge']);
for (const capability of parsed.capabilities) {
expect(capabilityStatusSchema.parse(capability)).toBeTruthy();
expect(capability.install.running).toBe(false);
}
const kimiCu = parsed.capabilities.find((c) => c.id === 'kimi-cu');
if (process.platform === 'darwin' || (process.platform === 'win32' && process.arch === 'x64')) {
expect(kimiCu?.supported).toBe(true);
} else {
expect(kimiCu?.supported).toBe(false);
expect(kimiCu?.state).toBe('unsupported');
}
const webbridge = parsed.capabilities.find((c) => c.id === 'kimi-webbridge');
expect(webbridge?.supported).toBe(true);
expect(webbridge?.steps.find((s) => s.id === 'skill')?.state).toBe('missing');
expect(webbridge?.steps.find((s) => s.id === 'extension')?.optional).toBe(true);
});
it('gets a single capability and 40418s on an unknown id', async () => {
const { body } = await getJson<unknown>('/api/v1/capabilities/kimi-webbridge');
expect(body.code).toBe(0);
expect(capabilityStatusSchema.parse(body.data).id).toBe('kimi-webbridge');
const missing = await getJson<unknown>('/api/v1/capabilities/nope');
expect(missing.body.code).toBe(40418);
expect(missing.body.data).toBeNull();
});
it('installs 40418 on an unknown id without side effects', async () => {
const { body } = await postJson<unknown>('/api/v1/capabilities/nope:install');
expect(body.code).toBe(40418);
});
it('rejects bare ids and unknown actions with 40001', async () => {
const bare = await postJson<unknown>('/api/v1/capabilities/kimi-cu');
expect(bare.body.code).toBe(40001);
const bogus = await postJson<unknown>('/api/v1/capabilities/kimi-cu:uninstall');
expect(bogus.body.code).toBe(40001);
});
it.skipIf(process.platform === 'darwin' || (process.platform === 'win32' && process.arch === 'x64'))(
'rejects kimi-cu install on unsupported platforms with 40925',
async () => {
const { body } = await postJson<unknown>('/api/v1/capabilities/kimi-cu:install');
expect(body.code).toBe(40925);
},
);
});
|