feat: v0.7.0 — server-authoritative sim engine: tick-on-read clock (time-scale + pause via POST /api/sim), contract expiry + market spawn, analytic plane movement/finalization

This commit is contained in:
2026-09-17 12:38:52 +00:00
parent 7fdc43bc7e
commit 96835e935d
22 changed files with 1393 additions and 55 deletions
+28 -1
View File
@@ -3,9 +3,10 @@ import { describe, expect, it } from 'vitest';
import { openApiSpec } from './openapi';
describe('OpenAPI spec', () => {
it('documents exactly the three public endpoints', () => {
it('documents exactly the four public endpoints', () => {
expect(Object.keys(openApiSpec.paths).sort()).toEqual([
'/api/airports',
'/api/sim',
'/api/state',
'/api/version',
]);
@@ -94,5 +95,31 @@ describe('OpenAPI spec', () => {
const sim = openApiSpec.components.schemas.SimState.properties;
expect(Object.keys(sim).sort()).toEqual(['clockMs', 'paused', 'timeScale']);
const contractStatus = openApiSpec.components.schemas.ContractState.properties.status;
expect(contractStatus.enum.sort()).toEqual(['accepted', 'completed', 'expired', 'open']);
});
it('describes POST /api/sim as SimControlsRequest → SimState with a 400 error shape', () => {
const post = openApiSpec.paths['/api/sim'].post;
expect(post.operationId).toBe('setSimControls');
const requestSchema = post.requestBody.content['application/json'].schema;
expect(requestSchema.$ref).toBe('#/components/schemas/SimControlsRequest');
const ok = post.responses[200].content['application/json'].schema;
expect(ok.$ref).toBe('#/components/schemas/SimState');
const bad = post.responses[400].content['application/json'].schema;
expect(bad.$ref).toBe('#/components/schemas/Error');
const controls = openApiSpec.components.schemas.SimControlsRequest.properties;
expect(Object.keys(controls).sort()).toEqual(['paused', 'timeScale']);
expect(controls.timeScale.type).toBe('number');
expect(controls.timeScale.minimum).toBe(1);
expect(controls.timeScale.maximum).toBe(60);
expect(controls.paused.type).toBe('boolean');
const error = openApiSpec.components.schemas.Error.properties;
expect(Object.keys(error)).toEqual(['error']);
});
});