feat: generate OpenAPI without peer repositories
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 2m28s
Conventional commits / commits (pull_request) Successful in 25s
Backend security / security (push) Successful in 6s
Backend verify / verify (push) Successful in 2m16s

This commit was merged in pull request #19.
This commit is contained in:
2026-09-15 20:45:39 +03:00
parent 016782657a
commit 32c12fb10d
19 changed files with 317 additions and 151 deletions
+37 -30
View File
@@ -1,22 +1,12 @@
import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { readFile } from 'node:fs/promises';
import { execFileSync } from 'node:child_process';
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { resolve } from 'node:path';
import { AppModule } from '../src/app.module';
import { configureApp } from '../src/main';
function canonicalize(value: unknown): unknown {
if (Array.isArray(value)) return value.map(canonicalize);
if (value && typeof value === 'object') {
return Object.fromEntries(
Object.entries(value)
.sort(([left], [right]) => left.localeCompare(right))
.map(([key, nestedValue]) => [key, canonicalize(nestedValue)]),
);
}
return value;
}
import { createOpenApiDocument } from '../src/openapi';
describe('OpenAPI contract (integration)', () => {
let app: INestApplication;
@@ -32,21 +22,38 @@ describe('OpenAPI contract (integration)', () => {
afterAll(() => app.close());
it('matches the committed frontend OpenAPI snapshot', async () => {
const runtimeDocument = SwaggerModule.createDocument(
app,
new DocumentBuilder()
.setTitle('WealthLedger API')
.setVersion('v1')
.build(),
);
const snapshot = JSON.parse(
await readFile(
resolve(__dirname, '../../frontend/src/shared/api/spec/openapi.json'),
'utf8',
),
) as unknown;
expect(canonicalize(runtimeDocument)).toEqual(canonicalize(snapshot));
it('generates the runtime contract without server startup or deployment configuration', async () => {
const directory = await mkdtemp(resolve(tmpdir(), 'wealthledger-openapi-'));
try {
const networkGuard = resolve(directory, 'no-network.cjs');
await writeFile(
networkGuard,
`
const net = require('node:net');
const forbidden = () => { throw new Error('Schema generation must not use the network'); };
net.Server.prototype.listen = forbidden;
net.Socket.prototype.connect = forbidden;
`,
);
execFileSync(
process.execPath,
[
'--require',
networkGuard,
resolve(__dirname, '../scripts/generate-openapi.cjs'),
],
{
cwd: directory,
env: { PATH: process.env.PATH },
timeout: 30_000,
},
);
const generated = JSON.parse(
await readFile(resolve(directory, 'openapi.json'), 'utf8'),
) as unknown;
expect(generated).toEqual(createOpenApiDocument(app));
} finally {
await rm(directory, { recursive: true, force: true });
}
});
});