Something went wrong. Try again.
Proxy util for accessing TLE server methods via more standard conventions.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152import test from 'node:test';import assert from 'node:assert/strict';import { createRequire } from 'node:module';
import argumentMap from '../argumentMap.json' with { type: 'json' };
// The spec is ~5MB; load it via require so tsc doesn't try to infer a type from it.const require = createRequire(import.meta.url);
type RequestBodySchema = { properties?: Record<string, unknown>; required?: string[];};
type SpecDoc = { paths: Record< string, { post?: { requestBody?: { content?: Record<string, { schema?: RequestBodySchema }>; }; }; } >;};
const spec = require('@tlecommunity/api-spec/build/spec.json') as SpecDoc;
// Spec paths never need an argument map entry. Entries are `/module/method` refs// (the spec path with the leading `/v2` stripped), e.g. `/empire/change_password`.const EXEMPT_PATHS: string[] = [];
type Argument = { name: string; required: boolean };
const positional = argumentMap.positional as Record<string, Argument[]>;const noah = new Set(argumentMap.namedOnlyArgumentHash as string[]);const nolfp = new Set(argumentMap.namedOnlyArgumentListFirstPositionHash as string[]);const nolsp = new Set(argumentMap.namedOnlyArgumentListSecondPositionHash as string[]);const mapKeys = new Set<string>([...Object.keys(positional), ...noah, ...nolfp, ...nolsp]);
type SpecEntry = { props: string[]; required: Set<string>; hasBody: boolean };
// Build `/module/method` -> request-body shape, validating the spec's own layout as we go.const specRefs = new Map<string, SpecEntry>();for (const [path, item] of Object.entries(spec.paths)) { assert.ok(path.startsWith('/v2/'), `spec path ${path} does not start with /v2/`); assert.ok(item.post, `spec path ${path} has no POST operation`);
const ref = path.slice('/v2'.length); const schema = item.post.requestBody?.content?.['application/json']?.schema; specRefs.set(ref, { props: schema?.properties ? Object.keys(schema.properties) : [], required: new Set(schema?.required ?? []), hasBody: schema !== undefined, });}
function assertClean(label: string, violations: string[]) { assert.equal( violations.length, 0, `${violations.length} ${label}:\n${violations.map((v) => ` - ${v}`).join('\n')}` );}
test('every argumentMap key exists in the spec', () => { const violations: string[] = []; for (const key of mapKeys) { if (!specRefs.has(key)) violations.push(key); } assertClean('argumentMap keys missing from the spec', violations);});
test('argumentMap sections are mutually exclusive', () => { const violations: string[] = []; for (const key of mapKeys) { const sections = [ key in positional ? 'positional' : null, noah.has(key) ? 'namedOnlyArgumentHash' : null, nolfp.has(key) ? 'namedOnlyArgumentListFirstPositionHash' : null, nolsp.has(key) ? 'namedOnlyArgumentListSecondPositionHash' : null, ].filter(Boolean); if (sections.length > 1) violations.push(`${key} in ${sections.join(' + ')}`); } assertClean('argumentMap keys in more than one section', violations);});
test('every positional argument is a spec request-body property', () => { const violations: string[] = []; for (const [key, args] of Object.entries(positional)) { const entry = specRefs.get(key); if (!entry) continue; // covered by "every argumentMap key exists in the spec" for (const arg of args) { if (!entry.props.includes(arg.name)) violations.push(`${key} :: ${arg.name}`); } } assertClean('positional arguments absent from the spec request body', violations);});
test('positional required flags match the spec', () => { const violations: string[] = []; for (const [key, args] of Object.entries(positional)) { const entry = specRefs.get(key); if (!entry) continue; for (const arg of args) { if (!entry.props.includes(arg.name)) continue; // covered by the previous test const specRequired = entry.required.has(arg.name); if (arg.required !== specRequired) { violations.push( `${key} :: ${arg.name} (argumentMap required=${arg.required}, spec required=${specRequired})` ); } } } assertClean('positional required flags disagreeing with the spec', violations);});
test('every spec request-body property is covered by the positional map', () => { const violations: string[] = []; for (const [key, args] of Object.entries(positional)) { const entry = specRefs.get(key); if (!entry || !entry.hasBody) continue; const argNames = new Set(args.map((a) => a.name)); for (const prop of entry.props) { if (!argNames.has(prop)) violations.push(`${key} :: ${prop}`); } } assertClean('spec request-body properties missing from the positional map', violations);});
test('every spec path is mapped or exempt', () => { const exempt = new Set(EXEMPT_PATHS); const violations: string[] = []; for (const key of specRefs.keys()) { if (!mapKeys.has(key) && !exempt.has(key)) violations.push(key); } assertClean( 'spec paths with no argumentMap entry (add them to argumentMap.json or EXEMPT_PATHS)', violations );});
test('EXEMPT_PATHS entries are real, unmapped spec paths', () => { const violations: string[] = []; for (const key of EXEMPT_PATHS) { if (!specRefs.has(key)) violations.push(`${key} (not a spec path)`); else if (mapKeys.has(key)) violations.push(`${key} (also present in argumentMap)`); } assertClean('stale EXEMPT_PATHS entries', violations);});