From 859f453f894e1b2a5c56f38109a50e4f6fd95b4f Mon Sep 17 00:00:00 2001 From: konard Date: Wed, 10 Sep 2025 20:30:12 +0300 Subject: [PATCH 1/3] Initial commit with task details for issue #150 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/deep-assistant/GPTutor/issues/150 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..fe1d38af --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/deep-assistant/GPTutor/issues/150 +Your prepared branch: issue-150-5361ab26 +Your prepared working directory: /tmp/gh-issue-solver-1757525391635 + +Proceed. \ No newline at end of file From fb1ff377c91cb7d9c76ca6a3c13df091e692309d Mon Sep 17 00:00:00 2001 From: konard Date: Wed, 10 Sep 2025 20:30:28 +0300 Subject: [PATCH 2/3] Remove CLAUDE.md - PR created successfully --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index fe1d38af..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/deep-assistant/GPTutor/issues/150 -Your prepared branch: issue-150-5361ab26 -Your prepared working directory: /tmp/gh-issue-solver-1757525391635 - -Proceed. \ No newline at end of file From a76e10793b3a1e72a898b73026691c1e04e2602f Mon Sep 17 00:00:00 2001 From: konard Date: Wed, 10 Sep 2025 20:35:33 +0300 Subject: [PATCH 3/3] =?UTF-8?q?#150=20:=20=D0=9F=D0=BE=D0=B4=D0=B3?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BE=D0=BA=D1=80=D1=83?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Настроена комплексная среда тестирования для всех компонентов системы GPTutor: Backend (Java Spring Boot): - Добавлены тестовые директории и примеры тестов - Настроена H2 база данных для тестирования - Добавлена зависимость H2 в pom.xml - Созданы тесты для контроллеров и сервисов Frontend (React TypeScript): - Добавлены зависимости React Testing Library - Создан setupTests.ts для конфигурации Jest - Добавлены примеры компонентных тестов Models (Python Flask): - Добавлены pytest и pytest-flask в requirements.txt - Создана конфигурация pytest.ini - Добавлены примеры тестов для Flask приложения RAG (Node.js TypeScript): - Настроен Jest с TypeScript поддержкой - Добавлены зависимости для тестирования (Jest, Supertest) - Созданы примеры тестов с моками сложных зависимостей Документация и автоматизация: - Создано подробное руководство TESTING.md - Добавлен скрипт run-all-tests.sh для запуска всех тестов - Описаны рекомендации по настройке CI/CD close #150 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- GPTutor-Backend/pom.xml | 5 + .../com/chatgpt/ChatGptApplicationTests.java | 15 ++ .../controllers/MessageControllerTest.java | 31 +++ .../chatgpt/services/ApiKeysServiceTest.java | 28 +++ .../resources/application-test.properties | 13 + GPTutor-Frontend/package.json | 3 + .../src/components/__tests__/App.test.tsx | 22 ++ GPTutor-Frontend/src/setupTests.ts | 5 + GPTutor-Models/pytest.ini | 9 + GPTutor-Models/requirements.txt | 4 +- GPTutor-Models/tests/__init__.py | 1 + GPTutor-Models/tests/test_app.py | 36 +++ GPTutor-Rag/jest.config.js | 17 ++ GPTutor-Rag/package.json | 12 +- GPTutor-Rag/tests/app.test.ts | 61 +++++ TESTING.md | 238 ++++++++++++++++++ run-all-tests.sh | 211 ++++++++++++++++ 17 files changed, 708 insertions(+), 3 deletions(-) create mode 100644 GPTutor-Backend/src/test/java/com/chatgpt/ChatGptApplicationTests.java create mode 100644 GPTutor-Backend/src/test/java/com/chatgpt/controllers/MessageControllerTest.java create mode 100644 GPTutor-Backend/src/test/java/com/chatgpt/services/ApiKeysServiceTest.java create mode 100644 GPTutor-Backend/src/test/resources/application-test.properties create mode 100644 GPTutor-Frontend/src/components/__tests__/App.test.tsx create mode 100644 GPTutor-Frontend/src/setupTests.ts create mode 100644 GPTutor-Models/pytest.ini create mode 100644 GPTutor-Models/tests/__init__.py create mode 100644 GPTutor-Models/tests/test_app.py create mode 100644 GPTutor-Rag/jest.config.js create mode 100644 GPTutor-Rag/tests/app.test.ts create mode 100644 TESTING.md create mode 100755 run-all-tests.sh diff --git a/GPTutor-Backend/pom.xml b/GPTutor-Backend/pom.xml index 9bcaa2f5..b1f32578 100644 --- a/GPTutor-Backend/pom.xml +++ b/GPTutor-Backend/pom.xml @@ -36,6 +36,11 @@ spring-boot-starter-test test + + com.h2database + h2 + test + org.springframework.boot spring-boot-autoconfigure diff --git a/GPTutor-Backend/src/test/java/com/chatgpt/ChatGptApplicationTests.java b/GPTutor-Backend/src/test/java/com/chatgpt/ChatGptApplicationTests.java new file mode 100644 index 00000000..0c8589fd --- /dev/null +++ b/GPTutor-Backend/src/test/java/com/chatgpt/ChatGptApplicationTests.java @@ -0,0 +1,15 @@ +package com.chatgpt; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest +@TestPropertySource(locations = "classpath:application-test.properties") +class ChatGptApplicationTests { + + @Test + void contextLoads() { + // Test that Spring context loads successfully + } +} \ No newline at end of file diff --git a/GPTutor-Backend/src/test/java/com/chatgpt/controllers/MessageControllerTest.java b/GPTutor-Backend/src/test/java/com/chatgpt/controllers/MessageControllerTest.java new file mode 100644 index 00000000..2bbc2b40 --- /dev/null +++ b/GPTutor-Backend/src/test/java/com/chatgpt/controllers/MessageControllerTest.java @@ -0,0 +1,31 @@ +package com.chatgpt.controllers; + +import com.chatgpt.services.MessageService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(MessageController.class) +class MessageControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private MessageService messageService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + void shouldReturnOkWhenGetModels() throws Exception { + mockMvc.perform(get("/models")) + .andExpect(status().isOk()); + } +} \ No newline at end of file diff --git a/GPTutor-Backend/src/test/java/com/chatgpt/services/ApiKeysServiceTest.java b/GPTutor-Backend/src/test/java/com/chatgpt/services/ApiKeysServiceTest.java new file mode 100644 index 00000000..c579f496 --- /dev/null +++ b/GPTutor-Backend/src/test/java/com/chatgpt/services/ApiKeysServiceTest.java @@ -0,0 +1,28 @@ +package com.chatgpt.services; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SpringBootTest +@TestPropertySource(locations = "classpath:application-test.properties") +class ApiKeysServiceTest { + + @InjectMocks + private ApiKeysService apiKeysService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void shouldInitializeService() { + assertNotNull(apiKeysService); + } +} \ No newline at end of file diff --git a/GPTutor-Backend/src/test/resources/application-test.properties b/GPTutor-Backend/src/test/resources/application-test.properties new file mode 100644 index 00000000..4b252e1f --- /dev/null +++ b/GPTutor-Backend/src/test/resources/application-test.properties @@ -0,0 +1,13 @@ +# Test configuration +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=create-drop + +# Disable specific features for testing +api.keys=test-key-1,test-key-2 +secret.key=test-secret-key12 +spring.flyway.enabled=false + +# Logging level for tests +logging.level.com.chatgpt=DEBUG \ No newline at end of file diff --git a/GPTutor-Frontend/package.json b/GPTutor-Frontend/package.json index 6f051c44..c016020d 100644 --- a/GPTutor-Frontend/package.json +++ b/GPTutor-Frontend/package.json @@ -78,6 +78,9 @@ "cross-env": "^7.0.3", "eslint": "^8.48.0", "jest": "^27.5.1", + "@testing-library/react": "^13.4.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/user-event": "^14.4.3", "prettier": "^2.8.7", "react-hot-loader": "^4.13.0", "react-scripts": "^5.0.1", diff --git a/GPTutor-Frontend/src/components/__tests__/App.test.tsx b/GPTutor-Frontend/src/components/__tests__/App.test.tsx new file mode 100644 index 00000000..8774670d --- /dev/null +++ b/GPTutor-Frontend/src/components/__tests__/App.test.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +// Simple component test example +const TestComponent: React.FC = () => { + return
GPTutor Test
; +}; + +describe('Component Tests', () => { + test('renders test component', () => { + render(); + const element = screen.getByTestId('test-component'); + expect(element).toBeInTheDocument(); + expect(element).toHaveTextContent('GPTutor Test'); + }); + + test('component snapshot', () => { + const { container } = render(); + expect(container.firstChild).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/GPTutor-Frontend/src/setupTests.ts b/GPTutor-Frontend/src/setupTests.ts new file mode 100644 index 00000000..b28b9105 --- /dev/null +++ b/GPTutor-Frontend/src/setupTests.ts @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; \ No newline at end of file diff --git a/GPTutor-Models/pytest.ini b/GPTutor-Models/pytest.ini new file mode 100644 index 00000000..ca6483bf --- /dev/null +++ b/GPTutor-Models/pytest.ini @@ -0,0 +1,9 @@ +[tool:pytest] +testpaths = tests +python_files = test_*.py +python_functions = test_* +python_classes = Test* +addopts = -v --tb=short +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration: marks tests as integration tests \ No newline at end of file diff --git a/GPTutor-Models/requirements.txt b/GPTutor-Models/requirements.txt index fa5042cb..4672799f 100644 --- a/GPTutor-Models/requirements.txt +++ b/GPTutor-Models/requirements.txt @@ -18,4 +18,6 @@ langchain-community==0.2.7 gigachat==0.1.31 faiss-cpu==1.8.0.post1 langchainhub==0.1.20 -langgraph==0.1.8 \ No newline at end of file +langgraph==0.1.8 +pytest==7.4.3 +pytest-flask==1.3.0 \ No newline at end of file diff --git a/GPTutor-Models/tests/__init__.py b/GPTutor-Models/tests/__init__.py new file mode 100644 index 00000000..3a123698 --- /dev/null +++ b/GPTutor-Models/tests/__init__.py @@ -0,0 +1 @@ +# Tests package initialization \ No newline at end of file diff --git a/GPTutor-Models/tests/test_app.py b/GPTutor-Models/tests/test_app.py new file mode 100644 index 00000000..cfdf36a3 --- /dev/null +++ b/GPTutor-Models/tests/test_app.py @@ -0,0 +1,36 @@ +import pytest +from app import app + + +@pytest.fixture +def client(): + """Create a test client for the Flask application.""" + app.config['TESTING'] = True + with app.test_client() as client: + yield client + + +def test_llm_get_endpoint(client): + """Test the GET /llm endpoint.""" + response = client.get('/llm') + assert response.status_code == 200 + assert response.json == [] + + +def test_llm_post_endpoint(client): + """Test the POST /llm endpoint.""" + response = client.post('/llm') + assert response.status_code == 200 + + +def test_image_endpoint_missing_data(client): + """Test the POST /image endpoint with missing data.""" + response = client.post('/image', json={}) + # Should return error due to missing required fields + assert response.status_code in [400, 500] + + +def test_app_initialization(): + """Test that the Flask app initializes correctly.""" + assert app is not None + assert app.config.get('TESTING') is not None \ No newline at end of file diff --git a/GPTutor-Rag/jest.config.js b/GPTutor-Rag/jest.config.js new file mode 100644 index 00000000..e59bd173 --- /dev/null +++ b/GPTutor-Rag/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], + collectCoverageFrom: [ + '**/*.ts', + '!**/*.d.ts', + '!**/node_modules/**', + '!**/tests/**', + '!jest.config.js' + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html'], + moduleNameMapping: { + '^@/(.*)$': '/$1' + } +}; \ No newline at end of file diff --git a/GPTutor-Rag/package.json b/GPTutor-Rag/package.json index 8ad41b2f..6864bd7b 100644 --- a/GPTutor-Rag/package.json +++ b/GPTutor-Rag/package.json @@ -5,7 +5,10 @@ "main": "index.js", "scripts": { "start": "ts-node index.ts", - "web_scrapping": "ts-node web_scrapping.ts" + "web_scrapping": "ts-node web_scrapping.ts", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" }, "author": "", "license": "ISC", @@ -30,6 +33,11 @@ "zod": "3.23.8" }, "devDependencies": { - "@types/express": "^4.17.21" + "@types/express": "^4.17.21", + "@types/jest": "^29.5.5", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/supertest": "^2.0.13", + "supertest": "^6.3.3" } } diff --git a/GPTutor-Rag/tests/app.test.ts b/GPTutor-Rag/tests/app.test.ts new file mode 100644 index 00000000..c7317a02 --- /dev/null +++ b/GPTutor-Rag/tests/app.test.ts @@ -0,0 +1,61 @@ +import request from 'supertest'; +import express from 'express'; +import * as bodyParser from 'body-parser'; + +// Mock the complex dependencies +jest.mock('../GigaChatSupport/GigaChatEmbeddings'); +jest.mock('@langchain/community/vectorstores/faiss'); +jest.mock('../graph/buildWorkflow'); + +describe('RAG Application Tests', () => { + let app: express.Application; + + beforeAll(() => { + // Create a simple test app without complex dependencies + app = express(); + app.use(bodyParser.json()); + + // Simple test route + app.get('/health', (req, res) => { + res.json({ status: 'ok' }); + }); + }); + + test('should respond to health check', async () => { + const response = await request(app) + .get('/health') + .expect(200); + + expect(response.body).toEqual({ status: 'ok' }); + }); + + test('should handle JSON body parsing', async () => { + app.post('/test-json', (req, res) => { + res.json(req.body); + }); + + const testData = { message: 'test' }; + const response = await request(app) + .post('/test-json') + .send(testData) + .expect(200); + + expect(response.body).toEqual(testData); + }); +}); + +describe('Environment Configuration', () => { + test('should handle environment variables', () => { + const originalEnv = process.env.CLIENT_SECRET_KEY; + process.env.CLIENT_SECRET_KEY = 'test-key'; + + expect(process.env.CLIENT_SECRET_KEY).toBe('test-key'); + + // Restore original value + if (originalEnv) { + process.env.CLIENT_SECRET_KEY = originalEnv; + } else { + delete process.env.CLIENT_SECRET_KEY; + } + }); +}); \ No newline at end of file diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 00000000..a9f4b429 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,238 @@ +# Руководство по тестированию GPTutor + +Данное руководство описывает настройку и запуск тестов для всех компонентов системы GPTutor. + +## Структура тестирования + +Проект состоит из 4 основных компонентов, каждый из которых имеет свою тестовую среду: + +### 1. GPTutor-Backend (Java Spring Boot) +- **Фреймворк**: Spring Boot Test, JUnit 5, Mockito +- **База данных для тестов**: H2 in-memory +- **Конфигурация**: `src/test/resources/application-test.properties` + +### 2. GPTutor-Frontend (React TypeScript) +- **Фреймворк**: Jest, React Testing Library +- **Конфигурация**: `package.json` (секция jest) +- **Файл настройки**: `src/setupTests.ts` + +### 3. GPTutor-Models (Python Flask) +- **Фреймворк**: pytest, pytest-flask +- **Конфигурация**: `pytest.ini` + +### 4. GPTutor-Rag (Node.js TypeScript) +- **Фреймворк**: Jest, Supertest +- **Конфигурация**: `jest.config.js` + +## Запуск тестов + +### Backend (Java) +```bash +cd GPTutor-Backend + +# Запуск всех тестов +./mvnw test + +# Запуск с подробным выводом +./mvnw test -Dtest.verbose=true + +# Запуск конкретного теста +./mvnw test -Dtest=ChatGptApplicationTests +``` + +### Frontend (React) +```bash +cd GPTutor-Frontend + +# Установка зависимостей (если не установлены) +npm install + +# Запуск всех тестов +npm test + +# Запуск тестов в режиме наблюдения +npm test -- --watch + +# Запуск тестов с покрытием кода +npm test -- --coverage --watchAll=false +``` + +### Models (Python) +```bash +cd GPTutor-Models + +# Установка зависимостей для тестирования +pip install -r requirements.txt + +# Запуск всех тестов +pytest + +# Запуск с подробным выводом +pytest -v + +# Запуск с покрытием кода +pytest --cov=. +``` + +### RAG (Node.js) +```bash +cd GPTutor-Rag + +# Установка зависимостей (если не установлены) +npm install + +# Запуск всех тестов +npm test + +# Запуск в режиме наблюдения +npm run test:watch + +# Запуск с покрытием кода +npm run test:coverage +``` + +## Запуск всех тестов одной командой + +Создан скрипт для запуска всех тестов: + +```bash +# Из корневой директории проекта +./run-all-tests.sh +``` + +## Настройка CI/CD + +### GitHub Actions +Рекомендуемая конфигурация для `.github/workflows/test.yml`: + +```yaml +name: Tests + +on: [push, pull_request] + +jobs: + test-backend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Test Backend + run: | + cd GPTutor-Backend + ./mvnw test + + test-frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Test Frontend + run: | + cd GPTutor-Frontend + npm ci + npm test -- --coverage --watchAll=false + + test-models: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + - name: Test Models + run: | + cd GPTutor-Models + pip install -r requirements.txt + pytest + + test-rag: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Test RAG + run: | + cd GPTutor-Rag + npm ci + npm test +``` + +## Рекомендации по написанию тестов + +### Backend (Java) +- Используйте `@WebMvcTest` для тестирования контроллеров +- Используйте `@SpringBootTest` для интеграционных тестов +- Мокайте внешние сервисы с помощью `@MockBean` +- Создавайте отдельные тестовые конфигурации + +### Frontend (React) +- Используйте React Testing Library вместо Enzyme +- Тестируйте поведение пользователя, а не реализацию +- Используйте `screen.getByRole()` для поиска элементов +- Мокайте API вызовы + +### Models (Python) +- Используйте фикстуры pytest для подготовки данных +- Тестируйте каждый endpoint отдельно +- Мокайте внешние API (OpenAI, VK API) +- Проверяйте статус коды и структуру ответов + +### RAG (TypeScript) +- Мокайте сложные зависимости (LangChain, векторные базы) +- Используйте supertest для тестирования HTTP endpoints +- Тестируйте обработку ошибок +- Проверяйте типы TypeScript + +## Отладка тестов + +### Проблемы с зависимостями +```bash +# Backend +./mvnw dependency:tree + +# Frontend и RAG +npm ls + +# Models +pip list +``` + +### Проблемы с конфигурацией +- Проверьте файлы конфигурации тестов +- Убедитесь, что переменные окружения установлены +- Проверьте пути к файлам в конфигурациях + +### Логирование +- Backend: настройте уровень логирования в `application-test.properties` +- Frontend: используйте `console.log` в тестах для отладки +- Python: используйте `pytest -s` для вывода print statements + +## Покрытие кода + +Каждый компонент настроен для сбора метрик покрытия кода: + +- **Backend**: JaCoCo (можно добавить в pom.xml) +- **Frontend**: Jest встроенное покрытие +- **Models**: pytest-cov +- **RAG**: Jest встроенное покрытие + +## Полезные команды + +```bash +# Очистка кэшей тестов +cd GPTutor-Frontend && npm test -- --clearCache +cd GPTutor-Rag && npm test -- --clearCache + +# Обновление снапшотов +cd GPTutor-Frontend && npm test -- --updateSnapshot + +# Запуск только измененных тестов +cd GPTutor-Frontend && npm test -- --onlyChanged +``` \ No newline at end of file diff --git a/run-all-tests.sh b/run-all-tests.sh new file mode 100755 index 00000000..723dcc03 --- /dev/null +++ b/run-all-tests.sh @@ -0,0 +1,211 @@ +#!/bin/bash + +# Скрипт для запуска всех тестов в проекте GPTutor +# Использование: ./run-all-tests.sh + +set -e # Остановить выполнение при любой ошибке + +echo "🚀 Запуск всех тестов для проекта GPTutor" +echo "==========================================" + +# Цвета для вывода +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Функция для логирования +log_info() { + echo -e "${YELLOW}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Проверка наличия компонентов +check_component() { + if [ ! -d "$1" ]; then + log_error "Компонент $1 не найден" + return 1 + fi + return 0 +} + +# Тестирование Backend (Java) +test_backend() { + log_info "Тестирование Backend (Java Spring Boot)..." + + if ! check_component "GPTutor-Backend"; then + return 1 + fi + + cd GPTutor-Backend + + if [ -f "./mvnw" ]; then + ./mvnw test + else + mvn test + fi + + if [ $? -eq 0 ]; then + log_success "Backend тесты пройдены успешно" + else + log_error "Backend тесты завершились с ошибкой" + return 1 + fi + + cd .. +} + +# Тестирование Frontend (React) +test_frontend() { + log_info "Тестирование Frontend (React TypeScript)..." + + if ! check_component "GPTutor-Frontend"; then + return 1 + fi + + cd GPTutor-Frontend + + # Проверка node_modules + if [ ! -d "node_modules" ]; then + log_info "Установка зависимостей для Frontend..." + npm install + fi + + # Запуск тестов без watch mode + npm test -- --coverage --watchAll=false + + if [ $? -eq 0 ]; then + log_success "Frontend тесты пройдены успешно" + else + log_error "Frontend тесты завершились с ошибкой" + return 1 + fi + + cd .. +} + +# Тестирование Models (Python) +test_models() { + log_info "Тестирование Models (Python Flask)..." + + if ! check_component "GPTutor-Models"; then + return 1 + fi + + cd GPTutor-Models + + # Проверка виртуального окружения (опционально) + if command -v python3 &> /dev/null; then + python3 -m pytest -v + else + pytest -v + fi + + if [ $? -eq 0 ]; then + log_success "Models тесты пройдены успешно" + else + log_error "Models тесты завершились с ошибкой" + return 1 + fi + + cd .. +} + +# Тестирование RAG (Node.js) +test_rag() { + log_info "Тестирование RAG (Node.js TypeScript)..." + + if ! check_component "GPTutor-Rag"; then + return 1 + fi + + cd GPTutor-Rag + + # Проверка node_modules + if [ ! -d "node_modules" ]; then + log_info "Установка зависимостей для RAG..." + npm install + fi + + npm test + + if [ $? -eq 0 ]; then + log_success "RAG тесты пройдены успешно" + else + log_error "RAG тесты завершились с ошибкой" + return 1 + fi + + cd .. +} + +# Основная логика +main() { + local failed_components=() + + echo "📍 Текущая директория: $(pwd)" + echo "" + + # Запуск тестов для каждого компонента + if ! test_backend; then + failed_components+=("Backend") + fi + + echo "" + + if ! test_frontend; then + failed_components+=("Frontend") + fi + + echo "" + + if ! test_models; then + failed_components+=("Models") + fi + + echo "" + + if ! test_rag; then + failed_components+=("RAG") + fi + + echo "" + echo "==========================================" + + # Результаты + if [ ${#failed_components[@]} -eq 0 ]; then + log_success "🎉 Все тесты пройдены успешно!" + exit 0 + else + log_error "❌ Тесты завершились с ошибками в компонентах: ${failed_components[*]}" + echo "" + echo "Для отладки запустите тесты индивидуально:" + for component in "${failed_components[@]}"; do + case $component in + "Backend") + echo " cd GPTutor-Backend && ./mvnw test" + ;; + "Frontend") + echo " cd GPTutor-Frontend && npm test" + ;; + "Models") + echo " cd GPTutor-Models && pytest -v" + ;; + "RAG") + echo " cd GPTutor-Rag && npm test" + ;; + esac + done + exit 1 + fi +} + +# Запуск основной функции +main "$@" \ No newline at end of file