import pkg from '@/package.json'; import { describe, expect, it } from 'vitest'; import { openApiSpec } from './openapi'; describe('OpenAPI spec', () => { it('documents exactly the three public endpoints', () => { expect(Object.keys(openApiSpec.paths).sort()).toEqual([ '/api/airports', '/api/state', '/api/version', ]); }); it('keeps info.version in sync with package.json', () => { expect(openApiSpec.info.title).toBe('Flight Ops API'); expect(openApiSpec.info.version).toBe(pkg.version); }); it('uses an empty server url so "try it" targets the current origin', () => { expect(openApiSpec.servers).toEqual([{ url: '' }]); }); it('describes /api/airports as a GeoJSON FeatureCollection of airport features', () => { const schema = openApiSpec.paths['/api/airports'].get.responses[200].content['application/json'].schema; expect(schema.$ref).toBe('#/components/schemas/AirportFeatureCollection'); const fc = openApiSpec.components.schemas.AirportFeatureCollection.properties; expect(fc.features.items.$ref).toBe('#/components/schemas/AirportFeature'); const feature = openApiSpec.components.schemas.AirportFeature.properties; expect(feature.geometry.$ref).toBe('#/components/schemas/PointGeometry'); expect(feature.properties.$ref).toBe('#/components/schemas/AirportProperties'); const props = openApiSpec.components.schemas.AirportProperties.properties; expect(Object.keys(props).sort()).toEqual(['icao', 'lat', 'lon', 'name']); expect(props.name.type).toBe('string'); expect(props.icao.type).toBe('string'); expect(props.lat.type).toBe('number'); expect(props.lon.type).toBe('number'); const coords = openApiSpec.components.schemas.PointGeometry.properties.coordinates; expect(coords.items.type).toBe('number'); }); it('describes /api/version as { version } with a string version', () => { const schema = openApiSpec.paths['/api/version'].get.responses[200].content['application/json'].schema; expect(schema.$ref).toBe('#/components/schemas/AppVersion'); const props = openApiSpec.components.schemas.AppVersion.properties; expect(Object.keys(props)).toEqual(['version']); expect(props.version.type).toBe('string'); }); it('describes /api/state as a StateSnapshot with account, simState, planes and contracts', () => { const schema = openApiSpec.paths['/api/state'].get.responses[200].content['application/json'].schema; expect(schema.$ref).toBe('#/components/schemas/StateSnapshot'); const snap = openApiSpec.components.schemas.StateSnapshot.properties; expect(Object.keys(snap).sort()).toEqual(['account', 'contracts', 'planes', 'simState']); expect(snap.account.$ref).toBe('#/components/schemas/AccountState'); expect(snap.simState.$ref).toBe('#/components/schemas/SimState'); expect(snap.planes.items.$ref).toBe('#/components/schemas/PlaneState'); expect(snap.contracts.items.$ref).toBe('#/components/schemas/ContractState'); const plane = openApiSpec.components.schemas.PlaneState.properties; expect(Object.keys(plane).sort()).toEqual([ 'contractId', 'fuel', 'id', 'lat', 'locationIcao', 'locationName', 'lon', 'maxFuel', 'model', 'status', ]); const contract = openApiSpec.components.schemas.ContractState.properties; expect(Object.keys(contract).sort()).toEqual([ 'cargoCount', 'cargoType', 'createdSimTime', 'deadlineSimTime', 'destIcao', 'destName', 'id', 'originIcao', 'originName', 'planeId', 'reward', 'status', ]); const sim = openApiSpec.components.schemas.SimState.properties; expect(Object.keys(sim).sort()).toEqual(['clockMs', 'paused', 'timeScale']); }); });