Files
LiquidCode.Tester/README.md

434 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LiquidCode Tester
Система автоматической проверки решений задач по программированию (Online Judge) для платформы LiquidCode.
## Архитектура
Проект состоит из трёх основных компонентов:
### 1. Gateway (Шлюз)
- **Технология**: ASP.NET Core Web API
- **Функции**:
- Приём запросов на тестирование через REST API
- Скачивание пакетов задач из Polygon
- Маршрутизация запросов к соответствующим Worker'ам по языку программирования
### 2. Worker (Универсальный тестировщик)
- **Технология**: ASP.NET Core Web API + компиляторы всех поддерживаемых языков
- **Функции**:
- Динамический выбор компилятора/интерпретатора на основе языка
- Компиляция пользовательского кода (C++, Java, Kotlin, C#) или подготовка к исполнению (Python)
- Запуск в изолированной среде
- Тестирование на наборе тестов
- Контроль ограничений по времени и памяти
- Отправка статусов на callback URL
### 3. Common (Общая библиотека)
- **Технология**: .NET Class Library
- **Функции**: Общие модели данных для Gateway и Worker
## Поддерживаемые языки
- **C++** (реализовано)
- **Java** (реализовано)
- **Kotlin** (реализовано)
- **C#** (реализовано)
- **Python** (реализовано)
## Модель данных
### SubmitForTesterModel (запрос на тестирование)
```csharp
public record SubmitForTesterModel(
long Id,
long MissionId,
string Language,
string LanguageVersion,
string SourceCode,
string PackageUrl,
string CallbackUrl
);
```
### TesterResponseModel (результат тестирования)
```csharp
public record TesterResponseModel(
long SubmitId,
State State, // Waiting, Compiling, Testing, Done
ErrorCode ErrorCode, // None, CompileError, RuntimeError, etc.
string Message,
int CurrentTest,
int AmountOfTests
);
```
## Быстрый старт
### Локальная разработка (Docker Compose)
```bash
# Сборка и запуск всех сервисов
docker-compose up --build
# Gateway доступен на http://localhost:8080
# Worker доступен на http://localhost:8081
```
### Разработка без Docker
```bash
# Сборка всего решения
dotnet build
# Запуск Gateway
cd src/LiquidCode.Tester.Gateway
dotnet run
# Запуск Worker (в другом терминале)
cd src/LiquidCode.Tester.Worker
dotnet run
```
## Локальное тестирование
Для тестирования системы без внешних зависимостей используйте endpoint `/api/Tester/submit-local`:
### 1. Подготовка тестового пакета
Создайте ZIP файл с тестами в формате:
```
test-package.zip
├── 1.in # Входные данные для теста 1
├── 1.out # Ожидаемый результат для теста 1
├── 2.in # Входные данные для теста 2
├── 2.out # Ожидаемый результат для теста 2
└── ...
```
### 2. Отправка запроса
```bash
curl -X 'POST' \
'http://localhost:8080/api/Tester/submit-local' \
-F 'id=1' \
-F 'missionId=1' \
-F 'language=python' \
-F 'languageVersion=3.11' \
-F 'sourceCode=n = int(input())
print(n * 2)' \
-F 'callbackUrl=log' \
-F 'package=@./test-package.zip'
```
### 3. Просмотр результатов
При использовании `callbackUrl=log` результаты тестирования выводятся в консоль Worker:
```
╔═══════════════════════════════════════════════════════════════╗
║ CALLBACK RESULT ║
╠═══════════════════════════════════════════════════════════════╣
{
"submitId": 1,
"state": "Done",
"errorCode": "None",
"message": "All tests passed",
"currentTest": 3,
"amountOfTests": 3
}
╚═══════════════════════════════════════════════════════════════╝
```
### Специальные значения callbackUrl
- `"log"` - Вывод результатов в логи Worker
- `"console"` - Аналогично "log"
- `"log://"` - Аналогично "log"
- Любой HTTP URL - Отправка результатов на указанный endpoint
## API Endpoints
### Gateway
**POST /api/tester/submit**
Отправка решения с удаленным пакетом тестов:
```json
{
"id": 123,
"missionId": 456,
"language": "C++",
"languageVersion": "17",
"sourceCode": "#include <iostream>\nint main() { ... }",
"packageUrl": "https://example.com/package.zip",
"callbackUrl": "https://example.com/api/submit/callback"
}
```
**POST /api/tester/submit-local**
Отправка решения с локальным пакетом тестов (для отладки):
```bash
curl -X 'POST' \
'http://localhost:8080/api/Tester/submit-local' \
-F 'id=1' \
-F 'missionId=1' \
-F 'language=python' \
-F 'languageVersion=3.11' \
-F 'sourceCode=print("Hello, World!")' \
-F 'callbackUrl=log' \
-F 'package=@/path/to/test-package.zip'
```
**Параметры:**
- `id` - ID сабмита
- `missionId` - ID задачи
- `language` - Язык программирования (C++, Java, Kotlin, C#, Python)
- `languageVersion` - Версия языка (опционально, используется "latest" по умолчанию)
- `sourceCode` - Исходный код решения
- `callbackUrl` - URL для отправки результатов или `"log"` для вывода в консоль
- `package` - ZIP файл с тестами
**GET /api/tester/health**
- Проверка состояния Gateway
### Worker
**POST /api/test**
- Принимает multipart/form-data с метаданными и ZIP файлом пакета
**GET /api/test/health**
- Проверка состояния Worker
## Процесс тестирования
1. **Приём запроса**: Gateway получает запрос с кодом и URL пакета
2. **Скачивание пакета**: Gateway скачивает Polygon пакет
3. **Маршрутизация**: Gateway отправляет запрос в Worker для нужного языка
4. **Парсинг**: Worker распаковывает и парсит пакет (тесты, лимиты)
5. **Компиляция**: Worker выбирает нужный компилятор на основе языка и компилирует код
6. **Тестирование**: Worker последовательно запускает все тесты
7. **Callback**: Worker отправляет статусы на callback URL на каждом этапе
8. **Cleanup**: Worker удаляет временные файлы
## Изоляция и безопасность
### Текущая реализация
- Процессы запускаются с ограничением по времени
- Контроль памяти через PeakWorkingSet64
- Процессы убиваются при превышении лимитов
### Production рекомендации
- Linux namespaces (PID, network, mount)
- cgroups для контроля CPU и памяти
- seccomp для ограничения системных вызовов
- AppArmor/SELinux профили
- chroot окружение
- Отключение доступа к сети для тестируемого кода
## Конфигурация
### Gateway (appsettings.json)
```json
{
"PackageDownloadDirectory": "/tmp/packages",
"Workers": {
"Cpp": "http://localhost:8081",
"Java": "http://localhost:8081",
"Kotlin": "http://localhost:8081",
"CSharp": "http://localhost:8081",
"Python": "http://localhost:8081"
}
}
```
### Worker (appsettings.json)
```json
{
"Cpp": {
"Compiler": "g++",
"CompilerFlags": "-O2 -std=c++17 -Wall",
"Versions": {
"14": {
"Compiler": "g++",
"CompilerFlags": "-O2 -std=c++14 -Wall"
},
"17": {
"Compiler": "g++",
"CompilerFlags": "-O2 -std=c++17 -Wall"
},
"20": {
"Compiler": "g++",
"CompilerFlags": "-O2 -std=c++20 -Wall"
}
}
},
"Java": {
"Compiler": "javac",
"CompilerFlags": "",
"Versions": {
"8": {
"Compiler": "javac",
"CompilerFlags": "-source 8 -target 8"
},
"11": {
"Compiler": "javac",
"CompilerFlags": "-source 11 -target 11"
},
"17": {
"Compiler": "javac",
"CompilerFlags": ""
}
}
},
"Kotlin": {
"Compiler": "kotlinc",
"CompilerFlags": "",
"Versions": {
"1.9": {
"Compiler": "kotlinc",
"CompilerFlags": ""
}
}
},
"CSharp": {
"Compiler": "csc",
"CompilerFlags": "/optimize+",
"Versions": {
"7": {
"Compiler": "csc",
"CompilerFlags": "/optimize+ /langversion:7"
},
"8": {
"Compiler": "csc",
"CompilerFlags": "/optimize+ /langversion:8"
},
"9": {
"Compiler": "csc",
"CompilerFlags": "/optimize+ /langversion:9"
}
}
},
"Python": {
"Executable": "python3",
"Versions": {
"3.8": {
"Executable": "python3.8"
},
"3.9": {
"Executable": "python3.9"
},
"3.10": {
"Executable": "python3.10"
},
"3.11": {
"Executable": "python3.11"
}
}
}
}
```
#### Поддержка версий языков
Система поддерживает указание версии языка программирования через параметр `languageVersion`:
- **"latest"** или **null** - использует версию по умолчанию из основной конфигурации
- **Конкретная версия** (например, "17", "3.11") - использует конфигурацию из секции `Versions`
- **Несуществующая версия** - логируется предупреждение и используется версия по умолчанию
Каждый язык имеет свою конфигурацию с возможностью указания:
- Для компилируемых языков (C++, Java, Kotlin, C#): путь к компилятору и флаги компиляции
- Для интерпретируемых языков (Python): путь к интерпретатору
Пример использования:
```bash
# Использование Python 3.11
curl -X POST 'http://localhost:8080/api/Tester/submit-local' \
-F 'languageVersion=3.11' \
-F 'language=python' \
...
# Использование C++20
curl -X POST 'http://localhost:8080/api/Tester/submit-local' \
-F 'languageVersion=20' \
-F 'language=C++' \
...
```
## Структура проекта
```
LiquidCode.Tester/
├── src/
│ ├── LiquidCode.Tester.Common/ # Общие модели
│ │ └── Models/
│ ├── LiquidCode.Tester.Gateway/ # API Gateway
│ │ ├── Controllers/
│ │ ├── Services/
│ │ └── Dockerfile
│ └── LiquidCode.Tester.Worker/ # Универсальный Worker (все языки)
│ ├── Controllers/
│ ├── Services/
│ │ ├── Compilation/ # Компиляторы
│ │ ├── Execution/ # Исполнители
│ │ └── Factories/ # Фабрики
│ └── Dockerfile
├── compose.yaml # Docker Compose
└── README.md
```
## Требования
- .NET 9.0 SDK
- Docker & Docker Compose (для контейнеризации)
- Компиляторы языков программирования (устанавливаются автоматически в Docker):
- g++ (C++)
- OpenJDK 17 (Java)
- Kotlin compiler (Kotlin)
- Mono (C#)
- Python 3
## Расширение на другие языки
Для добавления поддержки нового языка:
1. Создайте новую реализацию `ICompilationService` для языка (например, `GoCompilationService.cs`)
2. Создайте новую реализацию `IExecutionService` для языка (например, `GoExecutionService.cs`)
3. Зарегистрируйте новые сервисы в `Program.cs`:
```csharp
builder.Services.AddSingleton<GoCompilationService>();
builder.Services.AddSingleton<GoExecutionService>();
```
4. Обновите фабрики (`CompilationServiceFactory` и `ExecutionServiceFactory`), добавив поддержку нового языка
5. Добавьте конфигурацию языка в `appsettings.json` Worker
6. Обновите `Dockerfile` Worker, добавив установку компилятора/runtime
7. Добавьте конфигурацию воркера для нового языка в `appsettings.json` Gateway
8. Обновите `WorkerClientService` Gateway, добавив маппинг языка на URL воркера
## Разработка
### Структура кода
- **Controllers**: REST API endpoints
- **Services**: Бизнес-логика (компиляция, тестирование, callback)
- **Models**: Модели данных
### Логирование
Используется встроенный ILogger ASP.NET Core.
### Тестирование
```bash
dotnet test
```
## Лицензия
[Укажите лицензию]
## Авторы
LiquidCode Team