110 lines
3.2 KiB
TypeScript
110 lines
3.2 KiB
TypeScript
|
|
import { assert, assertEquals } from 'jsr:@std/assert@^1.0.0';
|
||
|
|
import { CheckExecutor } from '../ts/check-executor.ts';
|
||
|
|
import { UptimeRunner } from '../ts/runner.ts';
|
||
|
|
import type { TCheckJob } from '../ts/interfaces.ts';
|
||
|
|
|
||
|
|
Deno.test('CheckExecutor: executes successful HTTP check', async () => {
|
||
|
|
const { server, url } = await startServer(() => new Response('ok'));
|
||
|
|
try {
|
||
|
|
const executor = new CheckExecutor('test-runner');
|
||
|
|
const result = await executor.execute({
|
||
|
|
id: 'http-ok',
|
||
|
|
type: 'http',
|
||
|
|
url,
|
||
|
|
expectedStatusCodes: [200],
|
||
|
|
expectedBodyIncludes: 'ok',
|
||
|
|
});
|
||
|
|
|
||
|
|
assertEquals(result.status, 'ok');
|
||
|
|
assertEquals(result.statusCode, 200);
|
||
|
|
assert(result.responseTime !== undefined);
|
||
|
|
} finally {
|
||
|
|
await server.shutdown();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
Deno.test('CheckExecutor: reports HTTP expectation mismatch', async () => {
|
||
|
|
const { server, url } = await startServer(() => new Response('not-ok', { status: 503 }));
|
||
|
|
try {
|
||
|
|
const executor = new CheckExecutor('test-runner');
|
||
|
|
const result = await executor.execute({
|
||
|
|
id: 'http-not-ok',
|
||
|
|
type: 'http',
|
||
|
|
url,
|
||
|
|
expectedStatusCodes: [200],
|
||
|
|
});
|
||
|
|
|
||
|
|
assertEquals(result.status, 'not ok');
|
||
|
|
assertEquals(result.statusCode, 503);
|
||
|
|
} finally {
|
||
|
|
await server.shutdown();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
Deno.test('UptimeRunner: polls checks and submits results', async () => {
|
||
|
|
const targetServer = await startServer(() => new Response('healthy'));
|
||
|
|
const postedResults: unknown[] = [];
|
||
|
|
const checks: TCheckJob[] = [
|
||
|
|
{
|
||
|
|
id: 'target-health',
|
||
|
|
type: 'http',
|
||
|
|
url: targetServer.url,
|
||
|
|
expectedStatusCodes: [200],
|
||
|
|
expectedBodyIncludes: 'healthy',
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
const coordinatorServer = await startServer(async (request) => {
|
||
|
|
const url = new URL(request.url);
|
||
|
|
if (url.pathname === '/api/runner/v1/checks') {
|
||
|
|
assertEquals(request.headers.get('authorization'), 'Bearer test-token');
|
||
|
|
return Response.json({ checks });
|
||
|
|
}
|
||
|
|
if (url.pathname === '/api/runner/v1/results') {
|
||
|
|
postedResults.push(await request.json());
|
||
|
|
return Response.json({ ok: true });
|
||
|
|
}
|
||
|
|
if (url.pathname === '/api/runner/v1/heartbeat') {
|
||
|
|
return Response.json({ ok: true });
|
||
|
|
}
|
||
|
|
return new Response('not found', { status: 404 });
|
||
|
|
});
|
||
|
|
|
||
|
|
try {
|
||
|
|
const runner = new UptimeRunner({
|
||
|
|
instanceUrl: coordinatorServer.url,
|
||
|
|
runnerId: 'test-runner',
|
||
|
|
token: 'test-token',
|
||
|
|
});
|
||
|
|
const result = await runner.runOnce();
|
||
|
|
|
||
|
|
assertEquals(result.checks.length, 1);
|
||
|
|
assertEquals(result.results.length, 1);
|
||
|
|
assertEquals(result.results[0].status, 'ok');
|
||
|
|
assertEquals(postedResults.length, 1);
|
||
|
|
} finally {
|
||
|
|
await coordinatorServer.server.shutdown();
|
||
|
|
await targetServer.server.shutdown();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
async function startServer(
|
||
|
|
handlerArg: Deno.ServeHandler,
|
||
|
|
): Promise<{ server: Deno.HttpServer; url: string }> {
|
||
|
|
let resolveListening: (addr: Deno.NetAddr) => void;
|
||
|
|
const listening = new Promise<Deno.NetAddr>((resolve) => {
|
||
|
|
resolveListening = resolve;
|
||
|
|
});
|
||
|
|
|
||
|
|
const server = Deno.serve({
|
||
|
|
hostname: '127.0.0.1',
|
||
|
|
port: 0,
|
||
|
|
onListen: (addr) => resolveListening(addr),
|
||
|
|
}, handlerArg);
|
||
|
|
const addr = await listening;
|
||
|
|
return {
|
||
|
|
server,
|
||
|
|
url: `http://${addr.hostname}:${addr.port}`,
|
||
|
|
};
|
||
|
|
}
|