Files
Mavis a457b9d96f
CI / Lint, typecheck, test, build (pull_request) Failing after 10s
Phase 1: data pipeline (Postgres+Redis with in-memory fallback, mock telemetry publisher, WebSocket fan-out, hub /debug page)
- 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.
2026-06-02 18:54:46 +00:00

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);
});
});