test(behavior): cover config routing and registry seams
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
import { assertEquals } from 'jsr:@std/assert@^1.0.0';
|
||||||
|
import { ApiRouter } from '../ts/api/router.ts';
|
||||||
|
|
||||||
|
class TestResponse {
|
||||||
|
public statusCode = 200;
|
||||||
|
public headers: Record<string, string> = {};
|
||||||
|
public body = '';
|
||||||
|
|
||||||
|
public writeHead(statusCode: number, headers: Record<string, string>): TestResponse {
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.headers = headers;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public end(body = ''): TestResponse {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRouter(): ApiRouter {
|
||||||
|
return new ApiRouter(
|
||||||
|
{} as never,
|
||||||
|
{} as never,
|
||||||
|
{} as never,
|
||||||
|
{} as never,
|
||||||
|
['valid-key'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Deno.test('ApiRouter returns 404 for unknown endpoints', async () => {
|
||||||
|
const router = createRouter();
|
||||||
|
const response = new TestResponse();
|
||||||
|
|
||||||
|
await router.route(
|
||||||
|
{ method: 'GET', headers: {} } as never,
|
||||||
|
response as never,
|
||||||
|
'/does-not-exist',
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals(response.statusCode, 404);
|
||||||
|
assertEquals(JSON.parse(response.body).error.type, 'invalid_request_error');
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test('ApiRouter rejects protected endpoints without a bearer token', async () => {
|
||||||
|
const router = createRouter();
|
||||||
|
const response = new TestResponse();
|
||||||
|
|
||||||
|
await router.route(
|
||||||
|
{ method: 'GET', headers: {} } as never,
|
||||||
|
response as never,
|
||||||
|
'/v1/models',
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals(response.statusCode, 401);
|
||||||
|
assertEquals(JSON.parse(response.body).error.type, 'authentication_error');
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { assertEquals } from 'jsr:@std/assert@^1.0.0';
|
||||||
|
import { ModelRegistry } from '../ts/models/registry.ts';
|
||||||
|
|
||||||
|
Deno.test('ModelRegistry falls back to the built-in catalog when the source is unavailable', async () => {
|
||||||
|
const registry = new ModelRegistry('http://127.0.0.1:9/catalog.json');
|
||||||
|
const catalog = await registry.fetchCatalog(true);
|
||||||
|
|
||||||
|
assertEquals(catalog.version, '1.0');
|
||||||
|
assertEquals(catalog.models.length > 0, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test('ModelRegistry reads catalog entries from a local file source', async () => {
|
||||||
|
const filePath = await Deno.makeTempFile({ suffix: '.json' });
|
||||||
|
await Deno.writeTextFile(
|
||||||
|
filePath,
|
||||||
|
JSON.stringify({
|
||||||
|
version: '1.0',
|
||||||
|
generatedAt: '2026-01-01T00:00:00.000Z',
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen2.5-7B-Instruct',
|
||||||
|
aliases: ['qwen-local'],
|
||||||
|
engine: 'vllm',
|
||||||
|
source: { repo: 'Qwen/Qwen2.5-7B-Instruct' },
|
||||||
|
capabilities: { chat: true },
|
||||||
|
requirements: { minVramGb: 16 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const registry = new ModelRegistry(filePath);
|
||||||
|
const model = await registry.getModel('qwen-local');
|
||||||
|
|
||||||
|
assertEquals(model?.id, 'Qwen/Qwen2.5-7B-Instruct');
|
||||||
|
} finally {
|
||||||
|
await Deno.remove(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { assertEquals } from 'jsr:@std/assert@^1.0.0';
|
||||||
|
import { ModelGrid } from '../ts/modelgrid.ts';
|
||||||
|
import type { IModelGridConfig } from '../ts/interfaces/config.ts';
|
||||||
|
|
||||||
|
Deno.test('ModelGrid normalizes current config defaults', () => {
|
||||||
|
const modelgrid = new ModelGrid();
|
||||||
|
const normalizeConfig = (modelgrid as unknown as {
|
||||||
|
normalizeConfig: (config: Partial<IModelGridConfig>) => IModelGridConfig;
|
||||||
|
}).normalizeConfig.bind(modelgrid);
|
||||||
|
|
||||||
|
const normalized = normalizeConfig({
|
||||||
|
version: '1.0.0',
|
||||||
|
api: {
|
||||||
|
port: 9000,
|
||||||
|
host: '127.0.0.1',
|
||||||
|
apiKeys: ['test-key'],
|
||||||
|
},
|
||||||
|
docker: {
|
||||||
|
networkName: 'modelgrid',
|
||||||
|
runtime: 'docker',
|
||||||
|
},
|
||||||
|
gpus: {
|
||||||
|
autoDetect: true,
|
||||||
|
assignments: {},
|
||||||
|
},
|
||||||
|
containers: [],
|
||||||
|
models: {
|
||||||
|
registryUrl: 'https://example.com/catalog.json',
|
||||||
|
autoDeploy: false,
|
||||||
|
defaultEngine: 'vllm',
|
||||||
|
autoLoad: ['Qwen/Qwen2.5-7B-Instruct'],
|
||||||
|
},
|
||||||
|
cluster: {
|
||||||
|
enabled: false,
|
||||||
|
nodeName: 'modelgrid-local',
|
||||||
|
role: 'standalone',
|
||||||
|
bindHost: '0.0.0.0',
|
||||||
|
gossipPort: 7946,
|
||||||
|
heartbeatIntervalMs: 5000,
|
||||||
|
seedNodes: [],
|
||||||
|
},
|
||||||
|
checkInterval: 15000,
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(normalized.models.registryUrl, 'https://example.com/catalog.json');
|
||||||
|
assertEquals(normalized.models.autoDeploy, false);
|
||||||
|
assertEquals(normalized.models.defaultEngine, 'vllm');
|
||||||
|
assertEquals(normalized.ui.enabled, true);
|
||||||
|
assertEquals(normalized.ui.port, 8081);
|
||||||
|
assertEquals(normalized.ui.assetSource, 'bundle');
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user