Files
api-codegen/tests/integration/e2e-generation.test.ts

377 lines
11 KiB
TypeScript
Raw Normal View History

2025-10-28 09:58:44 +03:00
import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test';
import { execa } from 'execa';
import { setupTest } from '../helpers/setup.js';
import { createMockServer, type MockServer } from '../helpers/mock-server.js';
import { FIXTURES } from '../helpers/fixtures.js';
import { join } from 'path';
import { fileExists, readTextFile } from '../../src/utils/file.js';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const CLI_PATH = join(__dirname, '../../src/cli.ts');
describe('E2E Generation', () => {
let tempDir: string;
let cleanup: () => Promise<void>;
let mockServer: MockServer;
beforeAll(() => {
mockServer = createMockServer();
mockServer.start();
});
afterAll(() => {
mockServer.stop();
});
beforeEach(async () => {
const setup = await setupTest();
tempDir = setup.tempDir;
cleanup = setup.cleanup;
mockServer.reset();
});
afterEach(async () => {
await cleanup();
});
describe('полный цикл генерации', () => {
test('CLI генерация → создание файла → импорт → использование', async () => {
2025-10-28 09:58:44 +03:00
const outputPath = join(tempDir, 'output');
// 1. Генерация через CLI
const { exitCode } = await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
expect(exitCode).toBe(0);
// 2. Проверка создания файла
const generatedFile = join(outputPath, 'index.ts');
2025-10-28 09:58:44 +03:00
const exists = await fileExists(generatedFile);
expect(exists).toBe(true);
// 3. Проверка импорта (компиляция TypeScript)
const testFile = join(tempDir, 'test-import.ts');
const testCode = `
import { createApiClient, HttpClient } from '${generatedFile}';
import { getAll } from '${join(outputPath, 'operations', 'get-all.ts')}';
const api = createApiClient(new HttpClient(), {
users: {
getAll,
},
});
2025-10-28 09:58:44 +03:00
console.log('Import successful');
`;
await Bun.write(testFile, testCode);
// Компилируем тестовый файл
const { exitCode: compileExitCode } = await execa('bun', ['build', testFile, '--outdir', tempDir]);
expect(compileExitCode).toBe(0);
}, 60000);
test('генерация из локального файла', async () => {
const outputPath = join(tempDir, 'output');
const { exitCode } = await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.MINIMAL,
'--output',
outputPath,
]);
expect(exitCode).toBe(0);
const generatedFile = join(outputPath, 'MinimalAPI.ts');
const exists = await fileExists(generatedFile);
expect(exists).toBe(true);
}, 30000);
test('повторная генерация (перезапись файлов)', async () => {
const outputPath = join(tempDir, 'output');
// Первая генерация с endpoints
2025-10-28 09:58:44 +03:00
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
2025-10-28 09:58:44 +03:00
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
const generatedFile = join(outputPath, 'operations', 'index.ts');
const staleOperationFile = join(outputPath, 'operations', 'get-all.ts');
2025-10-28 09:58:44 +03:00
const firstContent = await readTextFile(generatedFile);
expect(await fileExists(staleOperationFile)).toBe(true);
2025-10-28 09:58:44 +03:00
// Вторая генерация (перезапись меньшей схемой)
2025-10-28 09:58:44 +03:00
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.MINIMAL,
2025-10-28 09:58:44 +03:00
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
const secondContent = await readTextFile(generatedFile);
// Содержимое должно отличаться
expect(firstContent).not.toBe(secondContent);
// Файл должен существовать
const exists = await fileExists(generatedFile);
expect(exists).toBe(true);
expect(await fileExists(staleOperationFile)).toBe(false);
2025-10-28 09:58:44 +03:00
}, 60000);
test('генерация из HTTP URL', async () => {
const outputPath = join(tempDir, 'output');
// Используем публичный OpenAPI spec
const { exitCode } = await execa('bun', [
'run',
CLI_PATH,
'--input',
'https://petstore3.swagger.io/api/v3/openapi.json',
'--output',
outputPath,
'--mode',
'split',
]);
expect(exitCode).toBe(0);
const generatedFile = join(outputPath, 'index.ts');
const exists = await fileExists(generatedFile);
expect(exists).toBe(true);
// Проверяем что файл не пустой
const content = await readTextFile(join(outputPath, 'http-client.ts'));
expect(content.length).toBeGreaterThan(1000);
}, 60000);
test('флаг --swr больше не поддерживается', async () => {
const outputPath = join(tempDir, 'output');
try {
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
'--output',
outputPath,
'--swr',
]);
throw new Error('Should have thrown');
} catch (error: any) {
expect(error.exitCode).not.toBe(0);
}
}, 30000);
test('REST генерация не содержит SWR хуки', async () => {
const outputPath = join(tempDir, 'output');
const { exitCode } = await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
'--output',
outputPath,
'--mode',
'split',
]);
expect(exitCode).toBe(0);
const generatedFile = join(outputPath, 'http-client.ts');
const content = await readTextFile(generatedFile);
// Проверяем отсутствие импорта useSWR
expect(content).not.toContain('import useSWR from "swr"');
// Проверяем отсутствие use* хуков
expect(content).not.toContain('useGetAll');
expect(content).not.toContain('useGetById');
}, 30000);
2025-10-28 09:58:44 +03:00
});
describe('HTTP запросы с mock сервером', () => {
test('GET запрос без параметров', async () => {
2025-10-28 09:58:44 +03:00
const outputPath = join(tempDir, 'output');
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
// Динамически импортируем сгенерированный API
const generatedFile = join(outputPath, 'index.ts');
const { createApiClient, HttpClient } = await import(generatedFile);
const { getAll } = await import(join(outputPath, 'operations', 'get-all.ts'));
const api = createApiClient(new HttpClient(), {
users: {
getAll,
},
});
const result = await api.users.getAll({});
2025-10-28 09:58:44 +03:00
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(2);
2025-10-28 09:58:44 +03:00
}, 60000);
test('POST запрос с body', async () => {
2025-10-28 09:58:44 +03:00
const outputPath = join(tempDir, 'output');
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
// Динамически импортируем сгенерированный API
const generatedFile = join(outputPath, 'index.ts');
const { createApiClient, HttpClient } = await import(generatedFile);
const { create } = await import(join(outputPath, 'operations', 'create.ts'));
const api = createApiClient(new HttpClient(), {
users: {
create,
},
});
const result = await api.users.create({
email: 'new@example.com',
password: 'password123'
});
2025-10-28 09:58:44 +03:00
expect(result.id).toBe('3');
expect(result.email).toBe('new@example.com');
2025-10-28 09:58:44 +03:00
}, 60000);
test('обработка 404 статуса', async () => {
2025-10-28 09:58:44 +03:00
const outputPath = join(tempDir, 'output');
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.VALID,
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
const testFile = join(tempDir, 'test-404.ts');
const testCode = `
import { createApiClient, HttpClient } from '${join(outputPath, 'index.ts')}';
import { getById } from '${join(outputPath, 'operations', 'get-by-id.ts')}';
const api = createApiClient(new HttpClient(), {
users: {
getById,
},
});
2025-10-28 09:58:44 +03:00
try {
await api.users.getById({ id: '999' });
2025-10-28 09:58:44 +03:00
} catch (error) {
console.log('error');
}
`;
await Bun.write(testFile, testCode);
const { stdout } = await execa('bun', ['run', testFile]);
expect(stdout).toContain('error');
}, 60000);
test('Bearer token authentication', async () => {
2025-10-28 09:58:44 +03:00
const outputPath = join(tempDir, 'output');
await execa('bun', [
'run',
CLI_PATH,
'--input',
FIXTURES.WITH_AUTH,
'--output',
outputPath,
'--mode',
'split',
2025-10-28 09:58:44 +03:00
]);
// Динамически импортируем сгенерированный API
const generatedFile = join(outputPath, 'index.ts');
const { createApiClient, HttpClient } = await import(generatedFile);
const { login } = await import(join(outputPath, 'operations', 'login.ts'));
const { get } = await import(join(outputPath, 'operations', 'get.ts'));
// Создаем HttpClient с securityWorker для добавления Bearer токена
const httpClient = new HttpClient({
securityWorker: (securityData: string | null) => {
if (securityData) {
return {
headers: {
Authorization: `Bearer ${securityData}`
}
};
}
}
});
const api = createApiClient(httpClient, {
auth: {
login,
},
profile: {
get,
},
});
// Логин
const loginResult = await api.auth.login({
email: 'test@example.com',
password: 'password'
});
// Установка токена
httpClient.setSecurityData(loginResult.token);
// Запрос с токеном
const profile = await api.profile.get();
2025-10-28 09:58:44 +03:00
expect(profile.email).toBe('test@example.com');
2025-10-28 09:58:44 +03:00
}, 60000);
});
});