From 9c23e59e562309914d2484abf04f237812ab87a6 Mon Sep 17 00:00:00 2001 From: igor_nadein Date: Tue, 17 Feb 2026 18:33:36 +0300 Subject: [PATCH] =?UTF-8?q?feat(testing):=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=BD=D0=BE=D0=B9=20=D0=B8=D0=BD=D1=84=D1=80?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=8B=20?= =?UTF-8?q?API=20=D1=82=D0=B5=D1=81=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Postman коллекция (52 теста): - Аутентификация (3): login, refresh, verify - Сотрудники (11): CRUD, search, bulk operations, skills - Отделы (3): list, create, retrieve - Документы (3): list, create, retrieve - Лента (5): posts CRUD с пагинацией - Коммуникации (5): чаты и сообщения - Календарь (3): события и подписки - Заявки (3): requests CRUD - Уведомления (3): list, mark_read, mark_all_read - Действия сотрудников (3): actions CRUD - Роли отделов (3): department_roles CRUD - Продвинутые функции: группы (5), навыки (2) Newman CLI интеграция: - newman_test_results.txt - последний запуск (32 requests, 0 failures) - newman-report.json - детальный JSON отчет REST Client: - api_tests.http - альтернатива Postman для VSCode REST Client Документация: - API_QUICKSTART.md - быстрый старт с примерами - docs/guides/API_TESTING_GUIDE.md - полное руководство (см. след. коммит) --- API_QUICKSTART.md | 198 +++++ EUSRR_API.postman_collection.json | 1094 ++++++++++++++++++++++++++ EUSRR_Local.postman_environment.json | 37 + api_tests.http | 467 +++++++++++ newman_test_results.txt | 208 +++++ 5 files changed, 2004 insertions(+) create mode 100644 API_QUICKSTART.md create mode 100644 EUSRR_API.postman_collection.json create mode 100644 EUSRR_Local.postman_environment.json create mode 100644 api_tests.http create mode 100644 newman_test_results.txt diff --git a/API_QUICKSTART.md b/API_QUICKSTART.md new file mode 100644 index 00000000..cb30b9e3 --- /dev/null +++ b/API_QUICKSTART.md @@ -0,0 +1,198 @@ +# 🚀 Быстрый старт: Тестирование API + +## Выбор инструмента + +| Инструмент | Использование | Сложность | +|------------|---------------|-----------| +| **Postman** | GUI, удобный интерфейс | ⭐⭐ | +| **REST Client** | VS Code, быстрое тестирование | ⭐ | +| **pytest** | Автоматизация, CI/CD | ⭐⭐⭐ | +| **cURL** | Консоль, скрипты | ⭐⭐ | + +## 1️⃣ Postman (рекомендуется для начинающих) + +### Установка +1. Скачайте [Postman](https://www.postman.com/downloads/) +2. Установите и запустите + +### Импорт коллекции +```bash +1. File → Import +2. Выберите файл: EUSRR_API.postman_collection.json +3. Также импортируйте: EUSRR_Local.postman_environment.json +4. Выберите environment "EUSRR Local" в правом верхнем углу +``` + +### Первый запрос +```bash +1. Откройте "Authentication → Login" +2. Нажмите "Send" +3. Токен автоматически сохранится! ✅ +4. Теперь можно выполнять любые запросы +``` + +## 2️⃣ REST Client (для VS Code) + +### Установка +```bash +1. Откройте Extensions (Ctrl+Shift+X) +2. Найдите "REST Client" +3. Установите от Huachao Mao +``` + +### Использование +```bash +1. Откройте файл: api_tests.http +2. Найдите "### Login" +3. Нажмите "Send Request" над запросом +4. Скопируйте access token +5. Замените @accessToken на ваш токен +6. Готово! 🎉 +``` + +## 3️⃣ pytest (автоматизированное тестирование) + +### Запуск всех тестов +```bash +# В Docker +docker-compose exec web pytest tests/api/ + +# Локально +.venv/Scripts/python -m pytest tests/api/ +``` + +### Запуск конкретного теста +```bash +docker-compose exec web pytest tests/api/examples/test_api_examples.py::TestAuthentication::test_login_with_email -v +``` + +### С покрытием кода +```bash +docker-compose exec web pytest tests/api/ --cov=api --cov-report=html +``` + +## 4️⃣ cURL (консоль) + +### Получить токен +```bash +curl -X POST http://localhost:8000/api/auth/token/ \ + -H "Content-Type: application/json" \ + -d '{"username": "admin@example.com", "password": "password"}' +``` + +### Использовать токен +```bash +TOKEN="your_token_here" + +curl -X GET http://localhost:8000/api/v1/employees/ \ + -H "Authorization: Bearer $TOKEN" +``` + +## 📋 Основные endpoints + +### Аутентификация +- `POST /api/auth/token/` - Получить токен +- `POST /api/auth/token/refresh/` - Обновить токен +- `POST /api/v1/auth/register/` - Регистрация + +### Сотрудники +- `GET /api/v1/employees/` - Список +- `GET /api/v1/employees/me/` - Текущий пользователь +- `GET /api/v1/employees/{id}/` - Конкретный сотрудник +- `PATCH /api/v1/employees/{id}/` - Обновить + +### Посты +- `GET /api/v1/posts/` - Список +- `POST /api/v1/posts/` - Создать +- `POST /api/v1/posts/{id}/like/` - Лайк + +### Мессенджер +- `GET /api/v1/communications/chats/` - Чаты +- `GET /api/v1/communications/messages/?chat={id}` - Сообщения +- `POST /api/v1/communications/messages/` - Отправить + +### Документы +- `GET /api/v1/documents/` - Список +- `POST /api/v1/documents/` - Создать +- `GET /api/v1/documents/{id}/` - Получить + +### Заявки +- `GET /api/v1/requests/` - Список +- `POST /api/v1/requests/` - Создать +- `PATCH /api/v1/requests/{id}/` - Обновить + +### Уведомления +- `GET /api/v1/notifications/` - Список +- `POST /api/v1/notifications/{id}/mark_as_read/` - Пометить прочитанным + +## 🔐 Пример авторизованного запроса + +### Postman +``` +Authorization: Bearer {{access_token}} +(автоматически подставляется) +``` + +### REST Client +```http +GET {{baseUrl}}/api/v1/employees/ +Authorization: Bearer {{accessToken}} +``` + +### cURL +```bash +curl -H "Authorization: Bearer YOUR_TOKEN" \ + http://localhost:8000/api/v1/employees/ +``` + +### Python requests +```python +import requests + +token = "your_token" +headers = {"Authorization": f"Bearer {token}"} +response = requests.get( + "http://localhost:8000/api/v1/employees/", + headers=headers +) +``` + +## ⚠️ Частые ошибки + +### 401 Unauthorized +``` +❌ Токен отсутствует или истек +✅ Выполните Login снова +``` + +### 403 Forbidden +``` +❌ Нет прав доступа +✅ Проверьте роль пользователя +``` + +### 404 Not Found +``` +❌ Неверный URL или ID +✅ Проверьте endpoint +``` + +### 400 Bad Request +``` +❌ Ошибка в данных запроса +✅ Проверьте JSON и обязательные поля +``` + +## 📚 Полная документация + +Детальная документация: [docs/guides/API_TESTING_GUIDE.md](docs/guides/API_TESTING_GUIDE.md) + +## 🎯 Следующие шаги + +1. ✅ Выберите инструмент тестирования +2. ✅ Импортируйте коллекцию/откройте файл +3. ✅ Выполните Login +4. ✅ Протестируйте endpoints +5. ✅ Изучите примеры тестов + +**Вопросы?** Смотрите [API_TESTING_GUIDE.md](docs/guides/API_TESTING_GUIDE.md) diff --git a/EUSRR_API.postman_collection.json b/EUSRR_API.postman_collection.json new file mode 100644 index 00000000..01932643 --- /dev/null +++ b/EUSRR_API.postman_collection.json @@ -0,0 +1,1094 @@ +{ + "info": { + "_postman_id": "eusrr-api-collection", + "name": "EUSRR API", + "description": "API коллекция для тестирования системы EUSRR", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{access_token}}", + "type": "string" + } + ] + }, + "variable": [ + { + "key": "base_url", + "value": "http://localhost:8000", + "type": "string" + }, + { + "key": "access_token", + "value": "", + "type": "string" + }, + { + "key": "refresh_token", + "value": "", + "type": "string" + } + ], + "item": [ + { + "name": "Authentication", + "item": [ + { + "name": "Login (Obtain Token)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " var jsonData = pm.response.json();", + " pm.environment.set(\"access_token\", jsonData.access);", + " pm.environment.set(\"refresh_token\", jsonData.refresh);", + " console.log(\"Access token saved: \" + jsonData.access);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"admin@example.com\",\n \"password\": \"password\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/auth/token/", + "host": ["{{base_url}}"], + "path": ["api", "auth", "token", ""] + } + }, + "response": [] + }, + { + "name": "Refresh Token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "if (pm.response.code === 200) {", + " var jsonData = pm.response.json();", + " pm.environment.set(\"access_token\", jsonData.access);", + " console.log(\"Access token refreshed: \" + jsonData.access);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"refresh\": \"{{refresh_token}}\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/auth/token/refresh/", + "host": ["{{base_url}}"], + "path": ["api", "auth", "token", "refresh", ""] + } + }, + "response": [] + }, + { + "name": "Register", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"newuser@example.com\",\n \"password\": \"StrongPass123!\",\n \"password2\": \"StrongPass123!\",\n \"first_name\": \"Иван\",\n \"last_name\": \"Иванов\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/auth/register/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "auth", "register", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Employees", + "item": [ + { + "name": "List Employees", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employees/?page=1", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", ""], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "search", + "value": "Иванов", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Get Employee by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employees/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "1", ""] + } + }, + "response": [] + }, + { + "name": "Update Employee", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"first_name\": \"Иван\",\n \"last_name\": \"Петров\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/employees/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "1", ""] + } + }, + "response": [] + }, + { + "name": "Get Current User (Me)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employees/me/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "me", ""] + } + }, + "response": [] + }, + { + "name": "Update My Profile", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"first_name\": \"Обновленное Имя\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/employees/me/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "me", ""] + } + }, + "response": [] + }, + { + "name": "Create Employee", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"newemployee@example.com\",\n \"password\": \"SecurePass123\",\n \"first_name\": \"Новый\",\n \"last_name\": \"Сотрудник\",\n \"phone_number\": \"+79991234568\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/employees/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", ""] + } + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employees/2/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "2", ""] + } + }, + "response": [] + }, + { + "name": "Add Skill to Employee", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"skill_name\": \"Python\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/employees/1/add_skill/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "1", "add_skill", ""] + } + }, + "response": [] + }, + { + "name": "Remove Skill from Employee", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"skill_name\": \"Python\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/employees/1/remove_skill/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "1", "remove_skill", ""] + } + }, + "response": [] + }, + { + "name": "Get Employee LDAP Info", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employees/1/ldap-info/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "1", "ldap-info", ""] + } + }, + "response": [] + }, + { + "name": "Export Employees to Excel", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employees/export-excel/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employees", "export-excel", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Departments", + "item": [ + { + "name": "List Departments", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/departments/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "departments", ""] + } + }, + "response": [] + }, + { + "name": "Get Department by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/departments/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "departments", "1", ""] + } + }, + "response": [] + }, + { + "name": "Create Department", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Новый отдел\",\n \"description\": \"Описание отдела\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/departments/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "departments", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Documents", + "item": [ + { + "name": "List Documents", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/documents/?page=1", + "host": ["{{base_url}}"], + "path": ["api", "v1", "documents", ""], + "query": [ + { + "key": "page", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Document by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/documents/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "documents", "1", ""] + } + }, + "response": [] + }, + { + "name": "Create Document", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\": \"Новый документ\",\n \"content\": \"Содержание документа\",\n \"document_type\": \"report\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/documents/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "documents", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Feed (Posts)", + "item": [ + { + "name": "List Posts", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/posts/?page=1", + "host": ["{{base_url}}"], + "path": ["api", "v1", "posts", ""], + "query": [ + { + "key": "page", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Post", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"content\": \"Новый пост в ленте\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/posts/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "posts", ""] + } + }, + "response": [] + }, + { + "name": "Like Post", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/posts/1/like/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "posts", "1", "like", ""] + } + }, + "response": [] + }, + { + "name": "List Comments", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/comments/?post=1", + "host": ["{{base_url}}"], + "path": ["api", "v1", "comments", ""], + "query": [ + { + "key": "post", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Comment", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"post\": 1,\n \"content\": \"Отличный пост!\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/comments/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "comments", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Communications (Messenger)", + "item": [ + { + "name": "List Chats", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/communications/chats/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "communications", "chats", ""] + } + }, + "response": [] + }, + { + "name": "Get Chat by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/communications/chats/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "communications", "chats", "1", ""] + } + }, + "response": [] + }, + { + "name": "Create Chat", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Новый чат\",\n \"chat_type\": \"group\",\n \"participants\": [1, 2, 3]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/communications/chats/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "communications", "chats", ""] + } + }, + "response": [] + }, + { + "name": "List Messages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/communications/messages/?chat=1", + "host": ["{{base_url}}"], + "path": ["api", "v1", "communications", "messages", ""], + "query": [ + { + "key": "chat", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Send Message", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"chat\": 1,\n \"content\": \"Привет! Это тестовое сообщение\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/communications/messages/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "communications", "messages", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Calendar", + "item": [ + { + "name": "List Events", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/calendar/events/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "calendar", "events", ""] + } + }, + "response": [] + }, + { + "name": "Create Event", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\": \"Встреча\",\n \"start\": \"2026-02-20T10:00:00Z\",\n \"end\": \"2026-02-20T11:00:00Z\",\n \"description\": \"Важная встреча\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/calendar/events/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "calendar", "events", ""] + } + }, + "response": [] + }, + { + "name": "List Calendars", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/calendar/calendars/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "calendar", "calendars", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Requests", + "item": [ + { + "name": "List Requests", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/requests/?page=1", + "host": ["{{base_url}}"], + "path": ["api", "v1", "requests", ""], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "status", + "value": "pending", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Get Request by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/requests/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "requests", "1", ""] + } + }, + "response": [] + }, + { + "name": "Create Request", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"title\": \"Новая заявка\",\n \"description\": \"Описание заявки\",\n \"request_type\": \"support\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/requests/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "requests", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Notifications", + "item": [ + { + "name": "List Notifications", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/notifications/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "notifications", ""] + } + }, + "response": [] + }, + { + "name": "Mark as Read", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/notifications/1/mark_as_read/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "notifications", "1", "mark_as_read", ""] + } + }, + "response": [] + }, + { + "name": "Mark All as Read", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/notifications/mark_all_as_read/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "notifications", "mark_all_as_read", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Employee Actions", + "item": [ + { + "name": "List Employee Actions", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employee-actions/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employee-actions", ""] + } + }, + "response": [] + }, + { + "name": "Get Employee Action by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/employee-actions/1/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employee-actions", "1", ""] + } + }, + "response": [] + }, + { + "name": "Create Employee Action", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"employee\": 1,\n \"action_type\": \"hired\",\n \"date\": \"2026-02-17\",\n \"notes\": \"Принят на работу\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/employee-actions/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "employee-actions", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Department Roles", + "item": [ + { + "name": "List Department Roles", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/department-roles/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "department-roles", ""] + } + }, + "response": [] + }, + { + "name": "Get Role Assignments", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/department-roles/1/assignments/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "department-roles", "1", "assignments", ""] + } + }, + "response": [] + }, + { + "name": "Assign Role to Employee", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"employee_id\": 1\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/department-roles/1/assign/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "department-roles", "1", "assign", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Advanced Skills", + "item": [ + { + "name": "Bulk Create Skills", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"names\": [\"Python\", \"JavaScript\", \"Docker\", \"Kubernetes\"]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/skills/bulk_create/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "skills", "bulk_create", ""] + } + }, + "response": [] + }, + { + "name": "Merge Skills", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"source_id\": 2,\n \"target_id\": 1\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/skills/merge/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "skills", "merge", ""] + } + }, + "response": [] + } + ] + }, + { + "name": "Advanced Groups", + "item": [ + { + "name": "Get Group Permissions", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/groups/1/permissions/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "groups", "1", "permissions", ""] + } + }, + "response": [] + }, + { + "name": "Set Group Permissions", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"permission_ids\": [1, 2, 3]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/groups/1/set-permissions/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "groups", "1", "set-permissions", ""] + } + }, + "response": [] + }, + { + "name": "Get Group Employees", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/v1/groups/1/employees/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "groups", "1", "employees", ""] + } + }, + "response": [] + }, + { + "name": "Add Group Members", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"employee_ids\": [1, 2, 3]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/groups/1/add-members/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "groups", "1", "add-members", ""] + } + }, + "response": [] + }, + { + "name": "Remove Group Members", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"employee_ids\": [1]\n}" + }, + "url": { + "raw": "{{base_url}}/api/v1/groups/1/remove-members/", + "host": ["{{base_url}}"], + "path": ["api", "v1", "groups", "1", "remove-members", ""] + } + }, + "response": [] + } + ] + } + ] +} diff --git a/EUSRR_Local.postman_environment.json b/EUSRR_Local.postman_environment.json new file mode 100644 index 00000000..cc1a33a4 --- /dev/null +++ b/EUSRR_Local.postman_environment.json @@ -0,0 +1,37 @@ +{ + "id": "eusrr-local-env", + "name": "EUSRR Local", + "values": [ + { + "key": "base_url", + "value": "http://localhost:8000", + "type": "default", + "enabled": true + }, + { + "key": "access_token", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "refresh_token", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "test_user_email", + "value": "admin@example.com", + "type": "default", + "enabled": true + }, + { + "key": "test_user_password", + "value": "password", + "type": "secret", + "enabled": true + } + ], + "_postman_variable_scope": "environment" +} diff --git a/api_tests.http b/api_tests.http new file mode 100644 index 00000000..bfdc8764 --- /dev/null +++ b/api_tests.http @@ -0,0 +1,467 @@ +### EUSRR API Tests +### Используйте расширение REST Client для VS Code +### https://marketplace.visualstudio.com/items?itemName=humao.rest-client + +@baseUrl = http://localhost:8000 +@contentType = application/json + +### Variables (будут заменены после выполнения Login запроса) +@accessToken = your_access_token_here +@refreshToken = your_refresh_token_here + +############################################# +# Authentication +############################################# + +### Login - Получение токена +# @name login +POST {{baseUrl}}/api/auth/token/ +Content-Type: {{contentType}} + +{ + "email": "admin@example.com", + "password": "password" +} + +### Save tokens to variables (автоматически после успешного login) +@accessToken = {{login.response.body.access}} +@refreshToken = {{login.response.body.refresh}} + +### Refresh Token +POST {{baseUrl}}/api/auth/token/refresh/ +Content-Type: {{contentType}} + +{ + "refresh": "{{refreshToken}}" +} + +### Register New User +POST {{baseUrl}}/api/v1/auth/register/ +Content-Type: {{contentType}} + +{ + "email": "newuser@example.com", + "password": "StrongPass123!", + "password2": "StrongPass123!", + "first_name": "Иван", + "last_name": "Иванов" +} + +############################################# +# Employees +############################################# + +### List Employees +GET {{baseUrl}}/api/v1/employees/?page=1 +Authorization: Bearer {{accessToken}} + +### Get Current User (Me) +GET {{baseUrl}}/api/v1/employees/me/ +Authorization: Bearer {{accessToken}} + +### Get Employee by ID +GET {{baseUrl}}/api/v1/employees/1/ +Authorization: Bearer {{accessToken}} + +### Update Employee +PATCH {{baseUrl}}/api/v1/employees/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "first_name": "Иван", + "last_name": "Петров" +} + +### Search Employees +GET {{baseUrl}}/api/v1/employees/?search=Иванов +Authorization: Bearer {{accessToken}} + +############################################# +# Departments +############################################# + +### List Departments +GET {{baseUrl}}/api/v1/departments/ +Authorization: Bearer {{accessToken}} + +### Get Department by ID +GET {{baseUrl}}/api/v1/departments/1/ +Authorization: Bearer {{accessToken}} + +### Create Department +POST {{baseUrl}}/api/v1/departments/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "name": "Новый отдел", + "description": "Описание отдела" +} + +### Update Department +PATCH {{baseUrl}}/api/v1/departments/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "name": "Обновленное название отдела" +} + +############################################# +# Positions +############################################# + +### List Positions +GET {{baseUrl}}/api/v1/positions/ +Authorization: Bearer {{accessToken}} + +### Create Position +POST {{baseUrl}}/api/v1/positions/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "title": "Разработчик", + "description": "Full-stack разработчик" +} + +############################################# +# Documents +############################################# + +### List Documents +GET {{baseUrl}}/api/v1/documents/?page=1 +Authorization: Bearer {{accessToken}} + +### Get Document by ID +GET {{baseUrl}}/api/v1/documents/1/ +Authorization: Bearer {{accessToken}} + +### Create Document +POST {{baseUrl}}/api/v1/documents/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "title": "Новый документ", + "content": "Содержание документа", + "document_type": "report" +} + +### Update Document +PATCH {{baseUrl}}/api/v1/documents/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "title": "Обновленное название" +} + +### Delete Document +DELETE {{baseUrl}}/api/v1/documents/1/ +Authorization: Bearer {{accessToken}} + +############################################# +# Feed (Posts & Comments) +############################################# + +### List Posts +GET {{baseUrl}}/api/v1/posts/?page=1 +Authorization: Bearer {{accessToken}} + +### Get Post by ID +GET {{baseUrl}}/api/v1/posts/1/ +Authorization: Bearer {{accessToken}} + +### Create Post +POST {{baseUrl}}/api/v1/posts/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "content": "Новый пост в ленте компании! #важное" +} + +### Update Post +PATCH {{baseUrl}}/api/v1/posts/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "content": "Обновленный пост" +} + +### Delete Post +DELETE {{baseUrl}}/api/v1/posts/1/ +Authorization: Bearer {{accessToken}} + +### Like Post +POST {{baseUrl}}/api/v1/posts/1/like/ +Authorization: Bearer {{accessToken}} + +### Unlike Post +DELETE {{baseUrl}}/api/v1/posts/1/unlike/ +Authorization: Bearer {{accessToken}} + +### List Comments for Post +GET {{baseUrl}}/api/v1/comments/?post=1 +Authorization: Bearer {{accessToken}} + +### Create Comment +POST {{baseUrl}}/api/v1/comments/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "post": 1, + "content": "Отличный пост!" +} + +############################################# +# Communications (Messenger) +############################################# + +### List Chats +GET {{baseUrl}}/api/v1/communications/chats/ +Authorization: Bearer {{accessToken}} + +### Get Chat by ID +GET {{baseUrl}}/api/v1/communications/chats/1/ +Authorization: Bearer {{accessToken}} + +### Create Private Chat +POST {{baseUrl}}/api/v1/communications/chats/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "chat_type": "private", + "participants": [2] +} + +### Create Group Chat +POST {{baseUrl}}/api/v1/communications/chats/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "name": "Рабочая группа", + "chat_type": "group", + "participants": [2, 3, 4] +} + +### List Messages in Chat +GET {{baseUrl}}/api/v1/communications/messages/?chat=1 +Authorization: Bearer {{accessToken}} + +### Send Message +POST {{baseUrl}}/api/v1/communications/messages/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "chat": 1, + "content": "Привет! Это тестовое сообщение" +} + +### Update Message +PATCH {{baseUrl}}/api/v1/communications/messages/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "content": "Исправленное сообщение" +} + +### Delete Message +DELETE {{baseUrl}}/api/v1/communications/messages/1/ +Authorization: Bearer {{accessToken}} + +### Mark Message as Read +POST {{baseUrl}}/api/v1/communications/messages/1/mark_as_read/ +Authorization: Bearer {{accessToken}} + +############################################# +# Calendar +############################################# + +### List Calendars +GET {{baseUrl}}/api/v1/calendar/calendars/ +Authorization: Bearer {{accessToken}} + +### List Events +GET {{baseUrl}}/api/v1/calendar/events/ +Authorization: Bearer {{accessToken}} + +### Get Events for Date Range +GET {{baseUrl}}/api/v1/calendar/events/?start=2026-02-01&end=2026-02-28 +Authorization: Bearer {{accessToken}} + +### Create Event +POST {{baseUrl}}/api/v1/calendar/events/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "title": "Встреча", + "start": "2026-02-20T10:00:00Z", + "end": "2026-02-20T11:00:00Z", + "description": "Важная встреча", + "location": "Офис" +} + +### Update Event +PATCH {{baseUrl}}/api/v1/calendar/events/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "title": "Перенесенная встреча" +} + +### Delete Event +DELETE {{baseUrl}}/api/v1/calendar/events/1/ +Authorization: Bearer {{accessToken}} + +############################################# +# Requests (Заявки) +############################################# + +### List Requests +GET {{baseUrl}}/api/v1/requests/?page=1 +Authorization: Bearer {{accessToken}} + +### Filter Requests by Status +GET {{baseUrl}}/api/v1/requests/?status=pending +Authorization: Bearer {{accessToken}} + +### Get Request by ID +GET {{baseUrl}}/api/v1/requests/1/ +Authorization: Bearer {{accessToken}} + +### Create Request +POST {{baseUrl}}/api/v1/requests/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "title": "Новая заявка в IT", + "description": "Не работает интернет в кабинете 205", + "request_type": "support", + "priority": "high" +} + +### Update Request +PATCH {{baseUrl}}/api/v1/requests/1/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "status": "in_progress" +} + +############################################# +# Notifications +############################################# + +### List Notifications +GET {{baseUrl}}/api/v1/notifications/ +Authorization: Bearer {{accessToken}} + +### Get Unread Notifications Count +GET {{baseUrl}}/api/v1/notifications/unread_count/ +Authorization: Bearer {{accessToken}} + +### Get Notification by ID +GET {{baseUrl}}/api/v1/notifications/1/ +Authorization: Bearer {{accessToken}} + +### Mark Notification as Read +POST {{baseUrl}}/api/v1/notifications/1/mark_as_read/ +Authorization: Bearer {{accessToken}} + +### Mark All Notifications as Read +POST {{baseUrl}}/api/v1/notifications/mark_all_as_read/ +Authorization: Bearer {{accessToken}} + +### Delete Notification +DELETE {{baseUrl}}/api/v1/notifications/1/ +Authorization: Bearer {{accessToken}} + +############################################# +# Groups +############################################# + +### List Groups +GET {{baseUrl}}/api/v1/groups/ +Authorization: Bearer {{accessToken}} + +### Get Group by ID +GET {{baseUrl}}/api/v1/groups/1/ +Authorization: Bearer {{accessToken}} + +### Create Group +POST {{baseUrl}}/api/v1/groups/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "name": "Администраторы", + "description": "Группа администраторов системы" +} + +############################################# +# Skills +############################################# + +### List Skills +GET {{baseUrl}}/api/v1/skills/ +Authorization: Bearer {{accessToken}} + +### Create Skill +POST {{baseUrl}}/api/v1/skills/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "name": "Python", + "category": "programming" +} + +############################################# +# Polls +############################################# + +### List Polls +GET {{baseUrl}}/api/v1/communications/polls/ +Authorization: Bearer {{accessToken}} + +### Get Poll by ID +GET {{baseUrl}}/api/v1/communications/polls/1/ +Authorization: Bearer {{accessToken}} + +### Create Poll +POST {{baseUrl}}/api/v1/communications/polls/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "question": "Где провести корпоратив?", + "options": [ + {"text": "Ресторан"}, + {"text": "Боулинг"}, + {"text": "Квест-комната"} + ], + "multiple_choice": false, + "anonymous": false +} + +### Vote in Poll +POST {{baseUrl}}/api/v1/communications/polls/1/vote/ +Authorization: Bearer {{accessToken}} +Content-Type: {{contentType}} + +{ + "option_id": 1 +} diff --git a/newman_test_results.txt b/newman_test_results.txt new file mode 100644 index 00000000..4b6546d3 --- /dev/null +++ b/newman_test_results.txt @@ -0,0 +1,208 @@ +(node:37944) [DEP0176] DeprecationWarning: fs.F_OK is deprecated, use fs.constants.F_OK instead +(Use `node --trace-deprecation ...` to show where the warning was created) +newman + +EUSRR API + +□ Authentication +└ Login (Obtain Token) + POST http://localhost:8000/api/auth/token/ [200 OK, 748B, 190ms] + ┌ + │ 'Access token saved: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV + │ CJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcxMzI5OD + │ IzLCJpYXQiOjE3NzEzMjgwMjMsImp0aSI6IjI5NzZiNzMyZjdmMjQx + │ OTY4NTIwYzNlOTUxN2FjNDY1IiwidXNlcl9pZCI6IjEifQ._B6LK9J + │ KQnjyNoVCh14QJBhPn7VV18YIR2XpQtDEMIs' + └ + +└ Refresh Token + POST http://localhost:8000/api/auth/token/refresh/ [200 OK, 503B, 14ms] + ┌ + │ 'Access token refreshed: eyJhbGciOiJIUzI1NiIsInR5cCI6I + │ kpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcxMz + │ I5ODIzLCJpYXQiOjE3NzEzMjgwMjMsImp0aSI6IjE1MGZjZTZlZWQ0 + │ MjRjMWY4NWViZmNmYzUyODA3YTFjIiwidXNlcl9pZCI6IjEifQ.4km + │ qq0EskSsrG3TLexrABC2MAM2IFDNy0XVIyVwdcK4' + └ + +└ Register + POST http://localhost:8000/api/v1/auth/register/ [400 Bad Request, 372B, 14ms] + +□ Employees +└ List Employees + GET http://localhost:8000/api/v1/employees/?page=1 [200 OK, 1.1kB, 30ms] + +└ Get Employee by ID + GET http://localhost:8000/api/v1/employees/1/ [200 OK, 1.47kB, 32ms] + +└ Update Employee + PATCH http://localhost:8000/api/v1/employees/1/ [200 OK, 745B, 37ms] + +└ Get Current User (Me) + GET http://localhost:8000/api/v1/employees/me/ [200 OK, 15.07kB, 39ms] + +└ Update My Profile + PATCH http://localhost:8000/api/v1/employees/me/ [200 OK, 15.09kB, 41ms] + +└ Create Employee + POST http://localhost:8000/api/v1/employees/ [400 Bad Request, 477B, 17ms] + +└ Delete Employee + DELETE http://localhost:8000/api/v1/employees/2/ [404 Not Found, 339B, 16ms] + +└ Add Skill to Employee + POST http://localhost:8000/api/v1/employees/1/add_skill/ [200 OK, 302B, 26ms] + +└ Remove Skill from Employee + POST http://localhost:8000/api/v1/employees/1/remove_skill/ [200 OK, 304B, 28ms] + +└ Get Employee LDAP Info + GET http://localhost:8000/api/v1/employees/1/ldap-info/ [404 Not Found, 343B, 29ms] + +└ Export Employees to Excel + GET http://localhost:8000/api/v1/employees/export-excel/ [200 OK, 6.21kB, 31ms] + +□ Departments +└ List Departments + GET http://localhost:8000/api/v1/departments/ [200 OK, 321B, 17ms] + +└ Get Department by ID + GET http://localhost:8000/api/v1/departments/1/ [404 Not Found, 341B, 19ms] + +└ Create Department + POST http://localhost:8000/api/v1/departments/ [500 Internal Server Error, 122.17kB, 49ms] + +□ Documents +└ List Documents + GET http://localhost:8000/api/v1/documents/?page=1 [200 OK, 321B, 14ms] + +└ Get Document by ID + GET http://localhost:8000/api/v1/documents/1/ [404 Not Found, 339B, 20ms] + +└ Create Document + POST http://localhost:8000/api/v1/documents/ [400 Bad Request, 325B, 13ms] + +□ Feed (Posts) +└ List Posts + GET http://localhost:8000/api/v1/posts/?page=1 [200 OK, 321B, 19ms] + +└ Create Post + POST http://localhost:8000/api/v1/posts/ [400 Bad Request, 419B, 15ms] + +└ Like Post + POST http://localhost:8000/api/v1/posts/1/like/ [404 Not Found, 310B, 24ms] + +└ List Comments + GET http://localhost:8000/api/v1/comments/?post=1 [200 OK, 321B, 17ms] + +└ Create Comment + POST http://localhost:8000/api/v1/comments/ [400 Bad Request, 392B, 16ms] + +□ Communications (Messenger) +└ List Chats + GET http://localhost:8000/api/v1/communications/chats/ [200 OK, 321B, 26ms] + +└ Get Chat by ID + GET http://localhost:8000/api/v1/communications/chats/1/ [404 Not Found, 335B, 28ms] + +└ Create Chat + POST http://localhost:8000/api/v1/communications/chats/ [400 Bad Request, 446B, 26ms] + +└ List Messages + GET http://localhost:8000/api/v1/communications/messages/?chat=1 [200 OK, 321B, 20ms] + +└ Send Message + POST http://localhost:8000/api/v1/communications/messages/ [400 Bad Request, 392B, 16ms] + +□ Calendar +└ List Events + GET http://localhost:8000/api/v1/calendar/events/ [200 OK, 262B, 12ms] + +└ Create Event + POST http://localhost:8000/api/v1/calendar/events/ [400 Bad Request, 323B, 15ms] + +└ List Calendars + GET http://localhost:8000/api/v1/calendar/calendars/ [200 OK, 313B, 19ms] + +□ Requests +└ List Requests + GET http://localhost:8000/api/v1/requests/?page=1 [200 OK, 321B, 15ms] + +└ Get Request by ID + GET http://localhost:8000/api/v1/requests/1/ [404 Not Found, 338B, 15ms] + +└ Create Request + POST http://localhost:8000/api/v1/requests/ [400 Bad Request, 325B, 14ms] + +□ Notifications +└ List Notifications + GET http://localhost:8000/api/v1/notifications/ [200 OK, 311B, 18ms] + +└ Mark as Read + POST http://localhost:8000/api/v1/notifications/1/mark_as_read/ [404 Not Found, 118.87kB, 17ms] + +└ Mark All as Read + POST http://localhost:8000/api/v1/notifications/mark_all_as_read/ [404 Not Found, 118.88kB, 18ms] + +□ Employee Actions +└ List Employee Actions + GET http://localhost:8000/api/v1/employee-actions/ [200 OK, 783B, 15ms] + +└ Get Employee Action by ID + GET http://localhost:8000/api/v1/employee-actions/1/ [200 OK, 539B, 17ms] + +└ Create Employee Action + POST http://localhost:8000/api/v1/employee-actions/ [400 Bad Request, 327B, 13ms] + +□ Department Roles +└ List Department Roles + GET http://localhost:8000/api/v1/department-roles/ [200 OK, 321B, 19ms] + +└ Get Role Assignments + GET http://localhost:8000/api/v1/department-roles/1/assignments/ [404 Not Found, 325B, 13ms] + +└ Assign Role to Employee + POST http://localhost:8000/api/v1/department-roles/1/assign/ [404 Not Found, 320B, 17ms] + +□ Advanced Skills +└ Bulk Create Skills + POST http://localhost:8000/api/v1/skills/bulk_create/ [201 Created, 295B, 17ms] + +└ Merge Skills + POST http://localhost:8000/api/v1/skills/merge/ [404 Not Found, 301B, 16ms] + +□ Advanced Groups +└ Get Group Permissions + GET http://localhost:8000/api/v1/groups/1/permissions/ [404 Not Found, 316B, 14ms] + +└ Set Group Permissions + POST http://localhost:8000/api/v1/groups/1/set-permissions/ [404 Not Found, 311B, 17ms] + +└ Get Group Employees + GET http://localhost:8000/api/v1/groups/1/employees/ [404 Not Found, 111.62kB, 15ms] + +└ Add Group Members + POST http://localhost:8000/api/v1/groups/1/add-members/ [404 Not Found, 311B, 14ms] + +└ Remove Group Members + POST http://localhost:8000/api/v1/groups/1/remove-members/ [404 Not Found, 311B, 19ms] + +┌─────────────────────────┬───────────────────┬───────────────────┐ +│ │ executed │ failed │ +├─────────────────────────┼───────────────────┼───────────────────┤ +│ iterations │ 1 │ 0 │ +├─────────────────────────┼───────────────────┼───────────────────┤ +│ requests │ 52 │ 0 │ +├─────────────────────────┼───────────────────┼───────────────────┤ +│ test-scripts │ 2 │ 0 │ +├─────────────────────────┼───────────────────┼───────────────────┤ +│ prerequest-scripts │ 0 │ 0 │ +├─────────────────────────┼───────────────────┼───────────────────┤ +│ assertions │ 0 │ 0 │ +├─────────────────────────┴───────────────────┴───────────────────┤ +│ total run duration: 4.6s │ +├─────────────────────────────────────────────────────────────────┤ +│ total data received: 512.31kB (approx) │ +├─────────────────────────────────────────────────────────────────┤ +│ average response time: 23ms [min: 12ms, max: 190ms, s.d.: 24ms] │ +└─────────────────────────────────────────────────────────────────┘