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; 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 () => { const outputPath = join(tempDir, 'output'); // 1. Генерация через CLI const { exitCode } = await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--mode', 'split', ]); expect(exitCode).toBe(0); // 2. Проверка создания файла const generatedFile = join(outputPath, 'index.ts'); 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, }, }); 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 await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--mode', 'split', ]); const generatedFile = join(outputPath, 'operations', 'index.ts'); const staleOperationFile = join(outputPath, 'operations', 'get-all.ts'); const firstContent = await readTextFile(generatedFile); expect(await fileExists(staleOperationFile)).toBe(true); // Вторая генерация (перезапись меньшей схемой) await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.MINIMAL, '--output', outputPath, '--mode', 'split', ]); 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); }, 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); }); describe('HTTP запросы с mock сервером', () => { test('GET запрос без параметров', async () => { const outputPath = join(tempDir, 'output'); await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--mode', 'split', ]); // Динамически импортируем сгенерированный 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({}); expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(2); }, 60000); test('POST запрос с body', async () => { const outputPath = join(tempDir, 'output'); await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--mode', 'split', ]); // Динамически импортируем сгенерированный 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' }); expect(result.id).toBe('3'); expect(result.email).toBe('new@example.com'); }, 60000); test('обработка 404 статуса', async () => { const outputPath = join(tempDir, 'output'); await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--mode', 'split', ]); 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, }, }); try { await api.users.getById({ id: '999' }); } 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 () => { const outputPath = join(tempDir, 'output'); await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.WITH_AUTH, '--output', outputPath, '--mode', 'split', ]); // Динамически импортируем сгенерированный 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(); expect(profile.email).toBe('test@example.com'); }, 60000); }); });