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, '--name', 'TestApi', ]); expect(exitCode).toBe(0); // 2. Проверка создания файла const generatedFile = join(outputPath, 'TestApi.ts'); const exists = await fileExists(generatedFile); expect(exists).toBe(true); // 3. Проверка импорта (компиляция TypeScript) const testFile = join(tempDir, 'test-import.ts'); const testCode = ` import { Api } from '${generatedFile}'; const api = new Api(); 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'); const fileName = 'TestApi'; // Первая генерация await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.MINIMAL, '--output', outputPath, '--name', fileName, ]); const generatedFile = join(outputPath, `${fileName}.ts`); const firstContent = await readTextFile(generatedFile); // Вторая генерация (перезапись) await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--name', fileName, ]); const secondContent = await readTextFile(generatedFile); // Содержимое должно отличаться expect(firstContent).not.toBe(secondContent); // Файл должен существовать const exists = await fileExists(generatedFile); expect(exists).toBe(true); }, 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, '--name', 'PetStore', ]); expect(exitCode).toBe(0); const generatedFile = join(outputPath, 'PetStore.ts'); const exists = await fileExists(generatedFile); expect(exists).toBe(true); // Проверяем что файл не пустой const content = await readTextFile(generatedFile); expect(content.length).toBeGreaterThan(1000); }, 60000); test('генерация с флагом --swr', async () => { const outputPath = join(tempDir, 'output'); const { exitCode } = await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--name', 'SwrApi', '--swr', ]); expect(exitCode).toBe(0); const generatedFile = join(outputPath, 'SwrApi.ts'); const content = await readTextFile(generatedFile); // Проверяем наличие импорта useSWR expect(content).toContain('import useSWR from "swr"'); // Проверяем наличие use* хуков для GET запросов expect(content).toContain('useGetAll'); expect(content).toContain('useGetById'); }, 30000); test('генерация без флага --swr не содержит хуки', async () => { const outputPath = join(tempDir, 'output'); const { exitCode } = await execa('bun', [ 'run', CLI_PATH, '--input', FIXTURES.VALID, '--output', outputPath, '--name', 'NoSwrApi', ]); expect(exitCode).toBe(0); const generatedFile = join(outputPath, 'NoSwrApi.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, '--name', 'TestApi', ]); // Динамически импортируем сгенерированный API const generatedFile = join(outputPath, 'TestApi.ts'); const { Api } = await import(generatedFile); const api = new Api(); 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, '--name', 'TestApi', ]); // Динамически импортируем сгенерированный API const generatedFile = join(outputPath, 'TestApi.ts'); const { Api } = await import(generatedFile); const api = new Api(); 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, '--name', 'TestApi', ]); const testFile = join(tempDir, 'test-404.ts'); const testCode = ` import { Api } from '${join(outputPath, 'TestApi.ts')}'; const api = new Api(); try { await api.users.getById('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, '--name', 'AuthApi', ]); // Динамически импортируем сгенерированный API const generatedFile = join(outputPath, 'AuthApi.ts'); const { Api, HttpClient } = await import(generatedFile); // Создаем HttpClient с securityWorker для добавления Bearer токена const httpClient = new HttpClient({ securityWorker: (securityData: string | null) => { if (securityData) { return { headers: { Authorization: `Bearer ${securityData}` } }; } } }); const api = new Api(httpClient); // Логин 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); }); });