import pkg from '@/package.json'; import { describe, expect, it } from 'vitest'; import { openApiSpec } from './openapi'; describe('OpenAPI spec', () => { it('documents every public endpoint', () => { expect(Object.keys(openApiSpec.paths).sort()).toEqual([ '/api/crew', '/api/explore', '/api/jump', '/api/system', '/api/version', '/api/world', ]); }); it('describes /api/crew as an array of CrewMember and crewFound on ExploreResult', () => { const crew = openApiSpec.paths['/api/crew'].get.responses[200].content['application/json'].schema; expect(crew.type).toBe('array'); expect((crew.items as { $ref: string }).$ref).toBe('#/components/schemas/CrewMember'); const member = openApiSpec.components.schemas.CrewMember.properties; expect(Object.keys(member)).toEqual(['id', 'name', 'role', 'trait_good', 'trait_bad', 'origin']); const exploreResult = openApiSpec.components.schemas.ExploreResult as { allOf: unknown[] }; const extra = (exploreResult.allOf[1] as { properties: Record }).properties; expect(Object.keys(extra)).toEqual(['outcome', 'crewFound']); }); it('describes the system endpoints as SystemState in and out', () => { const system = openApiSpec.paths['/api/system'].get.responses[200].content['application/json'].schema; expect(system.$ref).toBe('#/components/schemas/SystemState'); const explore = openApiSpec.paths['/api/explore'].post; expect(explore.requestBody.content['application/json'].schema.$ref).toBe('#/components/schemas/ExploreRequest'); expect(explore.responses[200].content['application/json'].schema.$ref).toBe('#/components/schemas/ExploreResult'); // ExploreResult extends SystemState with the scan outcome. const exploreResult = openApiSpec.components.schemas.ExploreResult as { allOf: { $ref?: string }[] }; expect(exploreResult.allOf[0].$ref).toBe('#/components/schemas/SystemState'); for (const status of ['400', '404', '409'] as const) { expect(explore.responses[status].content['application/json'].schema.$ref).toBe('#/components/schemas/Error'); } }); it('describes the world endpoints as WorldState in and out', () => { const world = openApiSpec.paths['/api/world'].get.responses[200].content['application/json'].schema; expect(world.$ref).toBe('#/components/schemas/WorldState'); const jump = openApiSpec.paths['/api/jump'].post; expect(jump.requestBody.content['application/json'].schema.$ref).toBe('#/components/schemas/JumpRequest'); expect(jump.responses[200].content['application/json'].schema.$ref).toBe('#/components/schemas/WorldState'); for (const status of ['400', '404', '409'] as const) { expect(jump.responses[status].content['application/json'].schema.$ref).toBe('#/components/schemas/Error'); } }); it('keeps info.version in sync with package.json', () => { expect(openApiSpec.info.title).toBe('SpaceWanderer 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/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 appVersion = openApiSpec.components.schemas.AppVersion.properties; expect(Object.keys(appVersion)).toEqual(['version']); expect(appVersion.version.type).toBe('string'); }); });