1e9219d14a
- Project scaffold (Node 22 + TS 5 + pnpm + vitest + ESLint flat config) - Core shell: TCP server, session loop, adapter registry, types - Configuration (zod-validated env) and pino logger - Teltonika adapter: IMEI handshake, frame envelope, CRC-16/IBM, codec dispatch registry, DeviceAuthority seam (AllowAllAuthority default) Codec data parsers (1.5-1.7), Redis publisher (1.8), and downstream tasks remain. 36 tests covering CRC, framing, handshake, device authority, config, and core server. typecheck/lint/test/build all clean.
75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
import * as net from 'node:net';
|
|
import type { Logger } from 'pino';
|
|
import type { Adapter, AdapterContext, Metrics, Position } from '../../src/core/types.js';
|
|
import { startServer } from '../../src/core/server.js';
|
|
|
|
function makeMockContext(): AdapterContext {
|
|
const logger = {
|
|
debug: vi.fn(),
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
child: vi.fn().mockReturnThis(),
|
|
} as unknown as Logger;
|
|
|
|
const metrics: Metrics = {
|
|
inc: vi.fn(),
|
|
observe: vi.fn(),
|
|
};
|
|
|
|
return {
|
|
publish: vi.fn(async (_p: Position) => {}),
|
|
logger,
|
|
metrics,
|
|
};
|
|
}
|
|
|
|
describe('startServer', () => {
|
|
const servers: net.Server[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const server of servers) {
|
|
server.close();
|
|
}
|
|
servers.length = 0;
|
|
});
|
|
|
|
it('invokes handleSession with a real socket when a client connects', async () => {
|
|
const handleSession = vi.fn().mockResolvedValue(undefined);
|
|
|
|
const adapter: Adapter = {
|
|
name: 'test-adapter',
|
|
ports: [0], // port 0 = OS-assigned ephemeral port
|
|
handleSession,
|
|
};
|
|
|
|
const ctx = makeMockContext();
|
|
const server = startServer(0, adapter, ctx);
|
|
servers.push(server);
|
|
|
|
// Wait for the server to start listening
|
|
const port = await new Promise<number>((resolve) => {
|
|
server.on('listening', () => {
|
|
const addr = server.address();
|
|
resolve(typeof addr === 'object' && addr !== null ? addr.port : 0);
|
|
});
|
|
});
|
|
|
|
// Connect a client
|
|
const client = new net.Socket();
|
|
client.connect(port, '127.0.0.1');
|
|
|
|
// Wait for handleSession to be called
|
|
await vi.waitFor(() => expect(handleSession).toHaveBeenCalledOnce(), { timeout: 2000 });
|
|
|
|
const [socketArg, ctxArg] = handleSession.mock.calls[0] as [net.Socket, AdapterContext];
|
|
expect(socketArg).toBeInstanceOf(net.Socket);
|
|
expect(ctxArg).toBeDefined();
|
|
expect(typeof ctxArg.publish).toBe('function');
|
|
expect(ctxArg.logger).toBeDefined();
|
|
|
|
client.destroy();
|
|
});
|
|
});
|