feat: v0.8.0 — player-actions API: accept/dispatch/buy/sell/refuel endpoints (tick-first, full-snapshot responses), spawn coverage rule, test create-contract hook
This commit is contained in:
+67
-1
@@ -3,9 +3,14 @@ import { describe, expect, it } from 'vitest';
|
||||
import { openApiSpec } from './openapi';
|
||||
|
||||
describe('OpenAPI spec', () => {
|
||||
it('documents exactly the four public endpoints', () => {
|
||||
it('documents exactly the nine public endpoints (4 read + 5 player actions)', () => {
|
||||
expect(Object.keys(openApiSpec.paths).sort()).toEqual([
|
||||
'/api/airports',
|
||||
'/api/contracts/{id}/accept',
|
||||
'/api/market/buy-plane',
|
||||
'/api/market/sell-plane',
|
||||
'/api/planes/{planeId}/dispatch',
|
||||
'/api/planes/{planeId}/refuel',
|
||||
'/api/sim',
|
||||
'/api/state',
|
||||
'/api/version',
|
||||
@@ -66,6 +71,7 @@ describe('OpenAPI spec', () => {
|
||||
const plane = openApiSpec.components.schemas.PlaneState.properties;
|
||||
expect(Object.keys(plane).sort()).toEqual([
|
||||
'contractId',
|
||||
'departureSimTime',
|
||||
'fuel',
|
||||
'id',
|
||||
'lat',
|
||||
@@ -122,4 +128,64 @@ describe('OpenAPI spec', () => {
|
||||
const error = openApiSpec.components.schemas.Error.properties;
|
||||
expect(Object.keys(error)).toEqual(['error']);
|
||||
});
|
||||
|
||||
it('documents the five action endpoints: snapshot on 200, Error on their 4xx paths', () => {
|
||||
// The spec object is a literal type; these helpers view it as plain JSON.
|
||||
const post = (p: string) =>
|
||||
((openApiSpec.paths as Record<string, { post?: unknown }>)[p]?.post ?? {}) as {
|
||||
responses: Record<string, unknown>;
|
||||
requestBody?: { content: Record<string, { schema: { $ref: string } }> };
|
||||
parameters?: Array<{ name: string }>;
|
||||
};
|
||||
|
||||
// Every action returns the full StateSnapshot on success (response convention).
|
||||
for (const p of [
|
||||
'/api/contracts/{id}/accept',
|
||||
'/api/planes/{planeId}/dispatch',
|
||||
'/api/market/buy-plane',
|
||||
'/api/market/sell-plane',
|
||||
'/api/planes/{planeId}/refuel',
|
||||
]) {
|
||||
const ok = post(p).responses['200'] as { content: Record<string, { schema: { $ref: string } }> };
|
||||
expect(ok.content['application/json'].schema.$ref).toBe('#/components/schemas/StateSnapshot');
|
||||
}
|
||||
|
||||
// Error shapes per endpoint.
|
||||
const errorCodes = (p: string) => Object.keys(post(p).responses).filter((k) => k !== '200').sort();
|
||||
expect(errorCodes('/api/contracts/{id}/accept')).toEqual(['404', '409']);
|
||||
expect(errorCodes('/api/planes/{planeId}/dispatch')).toEqual(['400', '404', '409']);
|
||||
expect(errorCodes('/api/market/buy-plane')).toEqual(['400', '409']);
|
||||
expect(errorCodes('/api/market/sell-plane')).toEqual(['400', '404', '409']);
|
||||
expect(errorCodes('/api/planes/{planeId}/refuel')).toEqual(['404', '409']);
|
||||
|
||||
// Request bodies: dispatch/buy/sell take their dedicated schemas; accept/refuel take none.
|
||||
expect(post('/api/planes/{planeId}/dispatch').requestBody?.content['application/json'].schema.$ref).toBe(
|
||||
'#/components/schemas/DispatchRequest',
|
||||
);
|
||||
expect(post('/api/market/buy-plane').requestBody?.content['application/json'].schema.$ref).toBe(
|
||||
'#/components/schemas/BuyPlaneRequest',
|
||||
);
|
||||
expect(post('/api/market/sell-plane').requestBody?.content['application/json'].schema.$ref).toBe(
|
||||
'#/components/schemas/SellPlaneRequest',
|
||||
);
|
||||
expect(post('/api/contracts/{id}/accept').requestBody).toBeUndefined();
|
||||
expect(post('/api/planes/{planeId}/refuel').requestBody).toBeUndefined();
|
||||
|
||||
// Path parameters are declared on the parameterized routes.
|
||||
const paramNames = (p: string) => post(p).parameters?.map((x) => x.name);
|
||||
expect(paramNames('/api/contracts/{id}/accept')).toEqual(['id']);
|
||||
expect(paramNames('/api/planes/{planeId}/dispatch')).toEqual(['planeId']);
|
||||
expect(paramNames('/api/planes/{planeId}/refuel')).toEqual(['planeId']);
|
||||
|
||||
// Request schema shapes.
|
||||
const dispatch = openApiSpec.components.schemas.DispatchRequest;
|
||||
expect(dispatch.required).toEqual(['contractId']);
|
||||
expect(dispatch.properties.contractId.type).toBe('string');
|
||||
const buy = openApiSpec.components.schemas.BuyPlaneRequest;
|
||||
expect(buy.required).toEqual(['model']);
|
||||
expect(buy.properties.model.type).toBe('string');
|
||||
const sell = openApiSpec.components.schemas.SellPlaneRequest;
|
||||
expect(sell.required).toEqual(['planeId']);
|
||||
expect(sell.properties.planeId.type).toBe('string');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user