From 871afedbb7642e053ef5e75242158805e05ac3eb Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Tue, 21 Apr 2026 12:44:11 +0000 Subject: [PATCH] test(behavior): cover config routing and registry seams --- test/api-router_test.ts | 57 +++++++++++++++++++++++++++++++++++ test/model-registry_test.ts | 40 ++++++++++++++++++++++++ test/modelgrid-config_test.ts | 51 +++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 test/api-router_test.ts create mode 100644 test/model-registry_test.ts create mode 100644 test/modelgrid-config_test.ts diff --git a/test/api-router_test.ts b/test/api-router_test.ts new file mode 100644 index 0000000..4f2757a --- /dev/null +++ b/test/api-router_test.ts @@ -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 = {}; + public body = ''; + + public writeHead(statusCode: number, headers: Record): 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'); +}); diff --git a/test/model-registry_test.ts b/test/model-registry_test.ts new file mode 100644 index 0000000..67ab886 --- /dev/null +++ b/test/model-registry_test.ts @@ -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); + } +}); diff --git a/test/modelgrid-config_test.ts b/test/modelgrid-config_test.ts new file mode 100644 index 0000000..83bac92 --- /dev/null +++ b/test/modelgrid-config_test.ts @@ -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; + }).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'); +});