132 lines
3.5 KiB
TypeScript
132 lines
3.5 KiB
TypeScript
import { assertEquals } from 'jsr:@std/assert@^1.0.0';
|
|
import { EventEmitter } from 'node:events';
|
|
import { AuthMiddleware } from '../ts/api/middleware/auth.ts';
|
|
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;
|
|
}
|
|
}
|
|
|
|
class TestRequest extends EventEmitter {
|
|
public method: string;
|
|
public headers: Record<string, string>;
|
|
public destroyed = false;
|
|
public paused = false;
|
|
|
|
constructor(method: string, headers: Record<string, string>) {
|
|
super();
|
|
this.method = method;
|
|
this.headers = headers;
|
|
}
|
|
|
|
public pause(): this {
|
|
this.paused = true;
|
|
return this;
|
|
}
|
|
|
|
public destroy(): this {
|
|
this.destroyed = true;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
function createRouter(): ApiRouter {
|
|
return new ApiRouter(
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
['valid-key'],
|
|
{
|
|
authMiddleware: new AuthMiddleware(['valid-key']),
|
|
sanityMiddleware: {
|
|
validateChatRequest() {
|
|
return { valid: true };
|
|
},
|
|
sanitizeChatRequest(body: Record<string, unknown>) {
|
|
return body;
|
|
},
|
|
validateEmbeddingsRequest() {
|
|
return { valid: true };
|
|
},
|
|
sanitizeEmbeddingsRequest(body: Record<string, unknown>) {
|
|
return body;
|
|
},
|
|
} as never,
|
|
chatHandler: {
|
|
async handleChatCompletion() {
|
|
throw new Error('chat handler should not run in this test');
|
|
},
|
|
} as never,
|
|
modelsHandler: {
|
|
async handleListModels() {
|
|
throw new Error('models handler should not run in this test');
|
|
},
|
|
} as never,
|
|
embeddingsHandler: {
|
|
async handleEmbeddings() {
|
|
throw new Error('embeddings handler should not run in this test');
|
|
},
|
|
} as never,
|
|
},
|
|
);
|
|
}
|
|
|
|
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');
|
|
});
|
|
|
|
Deno.test('ApiRouter returns 413 for oversized request bodies', async () => {
|
|
const router = createRouter();
|
|
const request = new TestRequest('POST', {
|
|
authorization: 'Bearer valid-key',
|
|
});
|
|
const response = new TestResponse();
|
|
|
|
const routePromise = router.route(request as never, response as never, '/v1/chat/completions');
|
|
request.emit('data', 'x'.repeat(10 * 1024 * 1024 + 1));
|
|
await routePromise;
|
|
|
|
assertEquals(response.statusCode, 413);
|
|
assertEquals(request.paused, true);
|
|
assertEquals(request.destroyed, true);
|
|
assertEquals(JSON.parse(response.body).error.message, 'Request body too large');
|
|
});
|