import assert from 'node:assert/strict' import fs from 'node:fs/promises' import http from 'node:http' import os from 'node:os' import path from 'node:path' import test from 'node:test' import { generateApiFiles } from '../src/core/generate.js' const createTempDir = async () => { return fs.mkdtemp(path.join(os.tmpdir(), 'yx-generate-api-')) } const writeJson = async (filePath, value) => { await fs.writeFile(filePath, JSON.stringify(value, null, 2), 'utf8') } const readFile = async (filePath) => { return fs.readFile(filePath, 'utf8') } test('full generation cleans stale auto-generated files and keeps manual files', async () => { const tempDir = await createTempDir() try { const initialSwaggerPath = path.join(tempDir, 'swagger-initial.json') const updatedSwaggerPath = path.join(tempDir, 'swagger-updated.json') const outputDir = path.join(tempDir, 'generated') await writeJson(initialSwaggerPath, { openapi: '3.0.0', paths: { '/api/v1/Alpha/GetThing': { get: { tags: ['Alpha'], summary: 'Get alpha thing', responses: { 200: { description: 'OK' }, }, }, }, '/api/v1/Beta/GetList': { get: { tags: ['Beta'], summary: 'Get beta list', responses: { 200: { description: 'OK' }, }, }, }, }, }) await writeJson(updatedSwaggerPath, { openapi: '3.0.0', paths: { '/api/v1/Alpha/GetThing': { get: { tags: ['Alpha'], summary: 'Get alpha thing', responses: { 200: { description: 'OK' }, }, }, }, }, }) await generateApiFiles({ projectRoot: tempDir, swaggerUrl: initialSwaggerPath, swaggerTimeoutMs: 1000, outputDir, requestImport: '../request', paramStyle: 'object', modules: [], cleanOutput: true, }) await fs.writeFile(path.join(outputDir, 'manual-helper.js'), 'export const keepMe = true\n', 'utf8') await generateApiFiles({ projectRoot: tempDir, swaggerUrl: updatedSwaggerPath, swaggerTimeoutMs: 1000, outputDir, requestImport: '../request', paramStyle: 'object', modules: [], cleanOutput: true, }) await assert.doesNotReject(fs.access(path.join(outputDir, 'alpha.js'))) await assert.rejects(fs.access(path.join(outputDir, 'beta.js'))) await assert.doesNotReject(fs.access(path.join(outputDir, 'manual-helper.js'))) const sharedFile = await readFile(path.join(outputDir, 'shared.js')) assert.doesNotMatch(sharedFile, /from 'qs'/) assert.match(sharedFile, /appendQueryEntries/) } finally { await fs.rm(tempDir, { recursive: true, force: true }) } }) test('partial generation keeps other generated modules even when cleanOutput is enabled', async () => { const tempDir = await createTempDir() try { const swaggerPath = path.join(tempDir, 'swagger.json') const outputDir = path.join(tempDir, 'generated') await writeJson(swaggerPath, { openapi: '3.0.0', paths: { '/api/v1/Alpha/GetThing': { get: { tags: ['Alpha'], responses: { 200: { description: 'OK' }, }, }, }, '/api/v1/Beta/GetList': { get: { tags: ['Beta'], responses: { 200: { description: 'OK' }, }, }, }, }, }) await generateApiFiles({ projectRoot: tempDir, swaggerUrl: swaggerPath, swaggerTimeoutMs: 1000, outputDir, requestImport: '../request', paramStyle: 'object', modules: [], cleanOutput: true, }) await generateApiFiles({ projectRoot: tempDir, swaggerUrl: swaggerPath, swaggerTimeoutMs: 1000, outputDir, requestImport: '../request', paramStyle: 'object', modules: ['Alpha'], cleanOutput: true, }) await assert.doesNotReject(fs.access(path.join(outputDir, 'beta.js'))) } finally { await fs.rm(tempDir, { recursive: true, force: true }) } }) test('generated index includes namespace exports and collision-safe named exports', async () => { const tempDir = await createTempDir() try { const swaggerPath = path.join(tempDir, 'swagger.json') const outputDir = path.join(tempDir, 'generated') await writeJson(swaggerPath, { openapi: '3.0.0', paths: { '/api/v1/Alpha/GetList': { get: { tags: ['Alpha'], responses: { 200: { description: 'OK' }, }, }, }, '/api/v1/Beta/GetList': { get: { tags: ['Beta'], responses: { 200: { description: 'OK' }, }, }, }, '/api/v1/Beta/GetDetail': { get: { tags: ['Beta'], responses: { 200: { description: 'OK' }, }, }, }, }, }) await generateApiFiles({ projectRoot: tempDir, swaggerUrl: swaggerPath, swaggerTimeoutMs: 1000, outputDir, requestImport: '../request', paramStyle: 'object', modules: [], cleanOutput: true, }) const indexContent = await readFile(path.join(outputDir, 'index.js')) assert.match(indexContent, /export \* as alphaApi from ["']\.\/alpha["']/) assert.match(indexContent, /export \* as betaApi from ["']\.\/beta["']/) assert.match(indexContent, /export \{ getListApi as alphaGetListApi \} from ["']\.\/alpha["']/) assert.match(indexContent, /export \{ getListApi as betaGetListApi \} from ["']\.\/beta["']/) assert.match(indexContent, /export \{ getDetailApi \} from ["']\.\/beta["']/) } finally { await fs.rm(tempDir, { recursive: true, force: true }) } }) test('swagger timeout surfaces a clear error message', async () => { const tempDir = await createTempDir() const server = http.createServer((_, response) => { setTimeout(() => { response.writeHead(200, { 'Content-Type': 'application/json' }) response.end(JSON.stringify({ openapi: '3.0.0', paths: {} })) }, 100) }) try { await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)) const { port } = server.address() await assert.rejects( generateApiFiles({ projectRoot: tempDir, swaggerUrl: `http://127.0.0.1:${port}/swagger.json`, swaggerTimeoutMs: 10, outputDir: path.join(tempDir, 'generated'), requestImport: '../request', paramStyle: 'object', modules: [], cleanOutput: true, }), /timed out/, ) } finally { server.close() await fs.rm(tempDir, { recursive: true, force: true }) } })