a457b9d96f
CI / Lint, typecheck, test, build (pull_request) Failing after 10s
- packages/db: postgres.js + ioredis wrapper, StateStore interface with InMemory + Postgres implementations, schema migration runner - apps/api: refactored to use @kerbal-rt/db; live WebSocket hub subscribes to store changes and broadcasts to all clients - apps/tools/mock-telemetry: Node script that generates realistic KSP state and POSTs to /api/v1/ingest at 1Hz (uses same keplerian math as the live-map renderer) - apps/hub: new /debug page that connects to /api/v1/live and shows the live state - ksp/README.md: documents Phase 1c (real kRPC bridge) and the two implementation options - Tests: 17 total (5 kepler math, 5 db store, 5 API health/state, 2 API WebSocket fan-out) End-to-end verified: mock publisher → API → hub /debug page, 11 snapshots/5s over WebSocket, vessels advancing in mean anomaly.
104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { app } from '../src/test-app.js';
|
|
|
|
describe('health', () => {
|
|
it('returns ok with service info', async () => {
|
|
const res = await app.request('/health');
|
|
expect(res.status).toBe(200);
|
|
const body = await res.json();
|
|
expect(body.ok).toBe(true);
|
|
expect(body.service).toBe('kerbal-rt-api');
|
|
expect(body.store).toBe('InMemoryStateStore');
|
|
});
|
|
|
|
it('returns 503 NO_DATA when no snapshot has been ingested', async () => {
|
|
const res = await app.request('/api/v1/state');
|
|
expect(res.status).toBe(503);
|
|
const body = await res.json();
|
|
expect(body.error).toBe(true);
|
|
expect(body.code).toBe('NO_DATA');
|
|
});
|
|
|
|
it('accepts a valid snapshot and returns it on /api/v1/state', async () => {
|
|
const snap = {
|
|
ut: 1,
|
|
capturedAt: '2026-01-01T00:00:00Z',
|
|
activeVesselId: null,
|
|
bodies: [],
|
|
vessels: [],
|
|
groundStations: [],
|
|
};
|
|
const ingest = await app.request('/api/v1/ingest', {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify(snap),
|
|
});
|
|
expect(ingest.status).toBe(200);
|
|
const ingestBody = await ingest.json();
|
|
expect(ingestBody.error).toBe(false);
|
|
expect(ingestBody.data.ok).toBe(true);
|
|
|
|
const state = await app.request('/api/v1/state');
|
|
expect(state.status).toBe(200);
|
|
const stateBody = await state.json();
|
|
expect(stateBody.data.ut).toBe(1);
|
|
});
|
|
|
|
it('rejects malformed snapshots with 400', async () => {
|
|
const res = await app.request('/api/v1/ingest', {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ broken: 'payload' }),
|
|
});
|
|
expect(res.status).toBe(400);
|
|
const body = await res.json();
|
|
expect(body.error).toBe(true);
|
|
expect(body.code).toBe('BAD_PAYLOAD');
|
|
});
|
|
|
|
it('looks up vessels by id', async () => {
|
|
const snap = {
|
|
ut: 1,
|
|
capturedAt: '2026-01-01T00:00:00Z',
|
|
activeVesselId: 'v-1',
|
|
bodies: [],
|
|
vessels: [
|
|
{
|
|
id: 'v-1',
|
|
name: 'Test',
|
|
type: 'Probe',
|
|
owner: 'KASA',
|
|
situation: 'ORBITING',
|
|
status: 'ACTIVE',
|
|
orbit: {
|
|
semiMajorAxis: 7e6,
|
|
eccentricity: 0,
|
|
inclination: 0,
|
|
longitudeOfAscendingNode: 0,
|
|
argumentOfPeriapsis: 0,
|
|
meanAnomalyAtEpoch: 0,
|
|
epoch: 0,
|
|
},
|
|
referenceBodyId: 'kerbin',
|
|
createdAt: '2026-01-01T00:00:00Z',
|
|
retiredAt: null,
|
|
},
|
|
],
|
|
groundStations: [],
|
|
};
|
|
await app.request('/api/v1/ingest', {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify(snap),
|
|
});
|
|
|
|
const found = await app.request('/api/v1/vessels/v-1');
|
|
expect(found.status).toBe(200);
|
|
const body = await found.json();
|
|
expect(body.data.name).toBe('Test');
|
|
|
|
const missing = await app.request('/api/v1/vessels/does-not-exist');
|
|
expect(missing.status).toBe(404);
|
|
});
|
|
});
|