Документация API 1.0.2 last modifay 07/06/2026
Базовый URL
https://easysite102.ru/api_yal/api/v1
Аутентификация
Для доступа к защищенным эндпоинтам требуется JWT токен. Токен передается в заголовке:
Authorization: Bearer <token>
Refresh token хранится в HttpOnly cookie.
1. Аутентификация (Auth)
1.1 Регистрация пользователя
POST /auth/register
Создает новый аккаунт пользователя.
Request Body:
{
"email": "user@example.com",
"password": "password123",
"first_name": "Иван",
"last_name": "Иванов"
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| string | ✅ | Email пользователя | |
| password | string | ✅ | Пароль, минимум 6 символов |
| first_name | string | ✅ | Имя |
| last_name | string | ✅ | Фамилия |
Response (201 Created):
{
"token": "jwt_token_string",
"expires_at": "2024-01-01T00:00:00Z",
"user": {
"id": 1,
"email": "user@example.com",
"first_name": "Иван",
"last_name": "Иванов",
"full_name": "Иван Иванов",
"role": "user"
}
}
1.2 Вход в систему
POST /auth/login
Аутентификация пользователя.
Request Body:
{
"email": "user@example.com",
"password": "password123"
}
Response (200 OK):
{
"token": "jwt_token_string",
"expires_at": "2024-01-01T00:00:00Z",
"user": {
"id": 1,
"email": "user@example.com",
"first_name": "Иван",
"last_name": "Иванов",
"full_name": "Иван Иванов",
"role": "user"
}
}
Refresh token устанавливается в HttpOnly cookie.
1.3 Обновление токена
POST /auth/refresh
Обновляет access token с использованием refresh token.
Cookie Required: refresh_token
Request Body (опционально, для мобильных приложений):
{
"refresh_token": "refresh_token_string"
}
Response (200 OK):
{
"token": "new_jwt_token_string",
"expires_at": "2024-01-01T00:00:00Z",
"user": {
"id": 1,
"email": "user@example.com",
"first_name": "Иван",
"last_name": "Иванов",
"full_name": "Иван Иванов",
"role": "user"
}
}
1.4 Выход из системы
POST /auth/logout
Завершает сессию пользователя.
Headers: Authorization: Bearer <token>
Response (200 OK):
{
"message": "Successfully logged out"
}
1.5 Запрос сброса пароля
POST /auth/password-reset/request
Отправляет ссылку для сброса пароля (в тестовом режиме возвращает токен).
Request Body:
{
"email": "user@example.com"
}
Response (200 OK):
{
"message": "Password reset link has been sent to your email",
"token": "reset_token" // только в тестовом режиме
}
1.6 Подтверждение сброса пароля
POST /auth/password-reset/confirm
Устанавливает новый пароль.
Request Body:
{
"token": "reset_token",
"new_password": "new_password123"
}
Response (200 OK):
{
"message": "Password has been successfully reset"
}
1.7 Вход для мобильных приложений
POST /auth/mobile/login
Альтернативный эндпоинт для мобильных клиентов.
Request Body:
{
"email": "user@example.com",
"password": "password123"
}
Response (200 OK):
{
"access_token": "jwt_token",
"refresh_token": "refresh_token_string",
"expires_at": "2024-01-01T00:00:00Z",
"user": {
"id": 1,
"email": "user@example.com",
"first_name": "Иван",
"last_name": "Иванов",
"full_name": "Иван Иванов",
"role": "user"
}
}
2. Аккаунт (Account)
2.1 Получение профиля пользователя
GET /account/profile
Headers: Authorization: Bearer <token>
Response (200 OK):
{
"id": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"email": "user@example.com",
"full_name": "Иван Иванов",
"first_name": "Иван",
"last_name": "Иванов",
"phone": "+79991234567",
"city": "Москва",
"organization_form": "ООО",
"organization_name": "Организация",
"organization_short": "ООО Орг",
"inn": "1234567890",
"personal_inn": "",
"is_active": true,
"is_verified": false,
"role": "user",
"stats": {
"objects_count": 5,
"feedbacks_count": 12,
"comments_count": 34,
"ratings_count": 8,
"appeals_count": 3
}
}
2.2 Получение аккаунта по ID (свой профиль)
GET /account
Headers: Authorization: Bearer <token>
Response (200 OK):
{
"id": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"email": "user@example.com",
"full_name": "Иван Иванов",
"first_name": "Иван",
"last_name": "Иванов",
"phone": "+79991234567",
"city": "Москва",
"is_active": true,
"is_verified": false,
"role": "user"
}
2.3 Обновление аккаунта
PUT /account
Headers: Authorization: Bearer <token>
Request Body:
{
"full_name": "Иван Петрович Иванов",
"first_name": "Иван",
"last_name": "Иванов",
"phone": "+79991234567",
"city": "Санкт-Петербург",
"organization_form": "ИП",
"organization_name": "ИП Иванов",
"organization_short": "ИП Иванов",
"inn": "123456789012",
"personal_inn": "123456789012"
}
Response (200 OK): (тот же формат, что и AccountResponse)
2.4 Смена пароля
POST /account/change-password
Headers: Authorization: Bearer <token>
Request Body:
{
"current_password": "old_password",
"new_password": "new_password123"
}
Response (200 OK):
{
"message": "Password changed successfully"
}
2.5 Удаление аккаунта
DELETE /account
Headers: Authorization: Bearer <token>
Response (200 OK):
{
"message": "Account deleted successfully"
}
2.6 Список аккаунтов (админ/модератор)
GET /admin/accounts
Headers: Authorization: Bearer <token> (требуются права admin)
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| page | int | Номер страницы (default: 1) |
| page_size | int | Размер страницы (default: 20, max: 100) |
| search | string | Поиск по email и имени |
| role | string | Фильтр по роли (user/admin/moderator) |
| is_active | bool | Фильтр по статусу активности |
Response (200 OK):
{
"items": [...],
"total": 100,
"page": 1,
"page_size": 20,
"total_pages": 5
}
2.7 Верификация аккаунта (админ)
PUT /admin/accounts/verify
Headers: Authorization: Bearer <token> (требуются права admin)
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| id | uint | ✅ ID аккаунта |
Request Body:
{
"is_verified": true
}
Response (200 OK):
{
"message": "Account verified successfully"
}
2.8 Обновление статуса аккаунта (админ)
PUT /admin/accounts/status
Headers: Authorization: Bearer <token> (требуются права admin)
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| id | uint | ✅ ID аккаунта |
Request Body:
{
"is_active": true,
"role": "moderator"
}
Response (200 OK):
{
"message": "Account status updated successfully"
}
3. Объекты (Objects)
3.1 Создание объекта
POST /objects
Headers: Authorization: Bearer <token>
Request Body:
{
"short_name": "Кафе Уют",
"long_name": "Кафе Уют на Невском",
"type": "cafe",
"phone": "+78121234567",
"email": "cafe@example.com",
"site": "https://cafe.example.com",
"short_description": "Уютное кафе в центре",
"description": "Подробное описание кафе",
"address": "г. Санкт-Петербург, Невский пр., 1",
"latitude": 59.9343,
"longitude": 30.3351,
"is_active": true,
"is_verified": false
}
Response (201 Created): ObjectResponse
3.2 Получение объекта по ID
GET /objects/{id}
Response (200 OK): ObjectResponse
{
"id": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z",
"deleted_at": null,
"owner_id": 1,
"owner": {
"id": 1,
"email": "user@example.com",
"full_name": "Иван Иванов",
"first_name": "Иван",
"last_name": "Иванов"
},
"short_name": "Кафе Уют",
"long_name": "Кафе Уют на Невском",
"type": "cafe",
"phone": "+78121234567",
"email": "cafe@example.com",
"site": "https://cafe.example.com",
"short_description": "Уютное кафе в центре",
"description": "Подробное описание кафе",
"address": "г. Санкт-Петербург, Невский пр., 1",
"latitude": 59.9343,
"longitude": 30.3351,
"is_active": true,
"is_verified": false,
"feedback_count": 15,
"tourist_rating": {
"id": 1,
"created_at": "...",
"platform": "tourist",
"average_score": 4.5,
"total_votes": 10,
"vote_breakdown": {...}
},
"entrepreneur_rating": {...},
"feedbacks": [...]
}
3.3 Обновление объекта
PUT /objects/{id}
Headers: Authorization: Bearer <token>
Request Body: (все поля опциональны)
{
"short_name": "Кафе Уютное",
"description": "Обновленное описание",
"is_active": false
}
Response (200 OK): ObjectResponse
3.4 Удаление объекта
DELETE /objects/{id}
Headers: Authorization: Bearer <token>
Response (204 No Content)
3.5 Список объектов (с фильтрацией)
GET /objects
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| page | int | Номер страницы (default: 1) |
| page_size | int | Размер страницы (default: 10) |
| type | string | Фильтр по типу объекта |
| q | string | Поиск по названию |
| is_active | bool | Фильтр по активности |
Response (200 OK): ObjectListResponse
3.6 Объекты пользователя
GET /objects/owner/{ownerId}
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| page | int | Номер страницы (default: 1) |
| page_size | int | Размер страницы (default: 10) |
Response (200 OK): ObjectListResponse
3.7 Поиск объектов
GET /objects/search
Query Parameters:
| Параметр | Тип | Обязательное | Описание |
|---|---|---|---|
| q | string | ✅ | Поисковый запрос |
| page | int | Номер страницы (default: 1) | |
| page_size | int | Размер страницы (default: 10) |
Response (200 OK): ObjectListResponse
3.8 Ближайшие объекты
GET /objects/nearby
Query Parameters:
| Параметр | Тип | Обязательное | Описание |
|---|---|---|---|
| lat | float | ✅ | Широта |
| lng | float | ✅ | Долгота |
| radius | float | Радиус в метрах (default: 1000) | |
| page | int | Номер страницы (default: 1) | |
| page_size | int | Размер страницы (default: 10) |
Response (200 OK): ObjectListResponse
3.9 Создание отзыва об объекте
POST /objects/{id}/feedbacks
Headers: Authorization: Bearer <token>
Request Body:
{
"platform": "tourist",
"score": 5,
"text": "Отличное место!"
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| platform | string | ✅ | entrepreneur или tourist |
| score | int | ✅ | Оценка от 1 до 5 |
| text | string | ✅ | Текст отзыва |
Response (201 Created): FeedbackResponse
3.10 Голосование за объект
POST /objects/{id}/ratings
Headers: Authorization: Bearer <token>
Request Body:
{
"platform": "tourist",
"score": 5
}
Response (201 Created): RatingVoteResponse
4. Отзывы (Feedbacks)
4.1 Создание отзыва
POST /feedbacks
Headers: Authorization: Bearer <token>
Request Body:
{
"object_id": 1,
"rating": 5,
"text": "Отличное место!",
"platform": "tourist",
"media_urls": ["https://example.com/photo1.jpg"]
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| object_id | uint | ✅ | ID объекта |
| rating | int | ✅ | Оценка от 1 до 5 |
| text | string | ✅ | Текст отзыва, мин 1, макс 2000 символов |
| platform | string | ✅ | entrepreneur или tourist |
| media_urls | []string | Список URL медиафайлов |
Response (201 Created): FeedbackResponse
4.2 Получение отзыва по ID
GET /feedbacks/{id}
Response (200 OK): FeedbackResponse
4.3 Обновление отзыва
PUT /feedbacks/{id}
Headers: Authorization: Bearer <token>
Request Body: (все поля опциональны)
{
"rating": 4,
"text": "Обновленный текст отзыва"
}
Response (200 OK): FeedbackResponse
4.4 Удаление отзыва
DELETE /feedbacks/{id}
Headers: Authorization: Bearer <token>
Response (204 No Content)
4.5 Список отзывов
GET /feedbacks
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| offset | int | Смещение (default: 0) |
| limit | int | Лимит (default: 20, max: 100) |
Response (200 OK): FeedbackListResponse
4.6 Мои отзывы
GET /feedbacks/my
Headers: Authorization: Bearer <token>
Response (200 OK): FeedbackListResponse
4.7 Отзывы по объекту
GET /feedbacks/object/{objectID}
Response (200 OK): FeedbackListResponse
4.8 Отзывы по платформе
GET /feedbacks/platform/{platform}
| Параметр | Описание |
|---|---|
| platform | entrepreneur или tourist |
Response (200 OK): FeedbackListResponse
4.9 Поиск отзывов
GET /feedbacks/search
Query Parameters:
| Параметр | Тип | Обязательное | Описание |
|---|---|---|---|
| q | string | ✅ | Поисковый запрос |
Response (200 OK): FeedbackListResponse
4.10 Статистика отзывов
GET /feedbacks/stats
Response (200 OK):
{
"total_feedbacks": 1000,
"average_rating": 4.2,
"rating_distribution": {
"1": 50,
"2": 100,
"3": 200,
"4": 300,
"5": 350
},
"platform_stats": {
"tourist": 600,
"entrepreneur": 400
}
}
4.11 Комментарии к отзыву
GET /feedbacks/{id}/comments
Response (200 OK): CommentListResponse
4.12 Добавление комментария к отзыву
POST /feedbacks/{id}/comments
Headers: Authorization: Bearer <token>
Request Body:
{
"text": "Отличный отзыв, согласен!"
}
Response (201 Created): CommentResponse
4.13 Обновление комментария
PUT /feedbacks/{id}/comments/{commentID}
Headers: Authorization: Bearer <token>
Request Body:
{
"text": "Обновленный текст комментария"
}
Response (200 OK):
{
"message": "comment updated successfully"
}
4.14 Удаление комментария
DELETE /feedbacks/{id}/comments/{commentID}
Headers: Authorization: Bearer <token>
Response (204 No Content)
5. Обращения (Appeals)
5.1 Создание обращения
POST /appeals
Headers: Authorization: Bearer <token> (опционально для анонимных)
Request Body:
{
"type": "complaint",
"title": "Проблема с заказом",
"message": "Подробное описание проблемы",
"priority": "high",
"object_id": 1,
"feedback_id": 1,
"comment_id": 1,
"contact_name": "Иван Иванов",
"contact_email": "ivan@example.com",
"contact_phone": "+79991234567",
"attachments": ["url1", "url2"],
"category": "technical",
"labels": ["bug", "urgent"],
"custom_data": {
"order_id": 12345
}
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| type | string | ✅ | complaint, suggestion, wish, question, other |
| title | string | ✅ | Заголовок, 3-255 символов |
| message | string | ✅ | Сообщение, минимум 10 символов |
| priority | string | low, medium, high, critical |
|
| contact_email | string | Должен быть валидным email, если указан |
Response (201 Created): AppealResponse
5.2 Получение обращения по ID
GET /appeals/{id}
Headers: Authorization: Bearer <token> (требуется для просмотра чужих обращений)
Response (200 OK): AppealResponse
5.3 Обновление обращения
PUT /appeals/{id}
Headers: Authorization: Bearer <token>
Request Body:
{
"title": "Обновленный заголовок",
"message": "Обновленное сообщение",
"priority": "critical",
"category": "billing"
}
Response (200 OK): AppealResponse
5.4 Удаление обращения
DELETE /appeals/{id}
Headers: Authorization: Bearer <token>
Response (204 No Content)
5.5 Список обращений (админ/модератор)
GET /appeals
Headers: Authorization: Bearer <token> (требуются права admin/moderator)
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| offset | int | Смещение (default: 0) |
| limit | int | Лимит (default: 20, max: 100) |
| status | string | new, in_progress, resolved, rejected, closed |
| type | string | complaint, suggestion, wish, question, other |
| priority | string | low, medium, high, critical |
| search | string | Поиск по заголовку/сообщению |
Response (200 OK): ListAppealsResponse
5.6 Мои обращения
GET /appeals/me
Headers: Authorization: Bearer <token>
Response (200 OK): ListAppealsResponse
5.7 Обращения пользователя (админ)
GET /appeals/user/{userID}
Headers: Authorization: Bearer <token> (требуются права admin)
Response (200 OK): ListAppealsResponse
5.8 Обновление статуса обращения
PATCH /appeals/{id}/status
Headers: Authorization: Bearer <token> (требуются права admin/moderator)
Request Body:
{
"status": "in_progress",
"comment": "Начата обработка обращения"
}
Response (200 OK):
{
"message": "Status updated successfully"
}
5.9 Назначение ответственного
POST /appeals/{id}/assign
Headers: Authorization: Bearer <token> (требуются права admin/moderator)
Request Body:
{
"assigned_to_id": 5
}
Response (200 OK):
{
"message": "Assigned successfully"
}
5.10 Решение обращения
POST /appeals/{id}/resolve
Headers: Authorization: Bearer <token> (требуются права admin/moderator)
Request Body:
{
"resolution": "Проблема решена путем обновления настроек"
}
Response (200 OK):
{
"message": "Appeal resolved successfully"
}
5.11 История изменений обращения
GET /appeals/{id}/history
Response (200 OK): ListHistoryResponse
5.12 Статистика обращений (админ)
GET /appeals/statistics
Headers: Authorization: Bearer <token> (требуются права admin)
Response (200 OK):
{
"total": 1000,
"by_status": {
"new": 50,
"in_progress": 100,
"resolved": 800,
"rejected": 30,
"closed": 20
},
"by_type": {
"complaint": 400,
"suggestion": 200,
"wish": 150,
"question": 200,
"other": 50
},
"by_priority": {
"low": 200,
"medium": 500,
"high": 250,
"critical": 50
},
"by_category": {...},
"avg_resolve_time_hours": 24.5
}
6. Комментарии (Comments)
6.1 Создание комментария
POST /comments
Headers: Authorization: Bearer <token>
Request Body:
{
"feedback_id": 1,
"text": "Отличный отзыв!",
"parent_id": null
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| feedback_id | uint | ✅ | ID отзыва |
| text | string | ✅ | Текст комментария, 1-5000 символов |
| parent_id | uint | ID родительского комментария |
Response (201 Created): CommentResponse
6.2 Получение комментария по ID
GET /comments/{id}
Response (200 OK): CommentResponse
6.3 Обновление комментария
PUT /comments/{id}
Headers: Authorization: Bearer <token>
Request Body:
{
"text": "Обновленный текст"
}
Response (200 OK): CommentResponse
6.4 Удаление комментария
DELETE /comments/{id}
Headers: Authorization: Bearer <token>
Response (200 OK):
{
"message": "Comment deleted successfully"
}
6.5 Список комментариев (с фильтрацией)
GET /comments
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| page | int | Номер страницы (default: 1) |
| page_size | int | Размер страницы (default: 20, max: 100) |
| feedback_id | uint | Фильтр по отзыву |
| author_id | uint | Фильтр по автору |
| parent_id | uint | Фильтр по родительскому комментарию |
| verified | bool | Фильтр по верификации |
| sort_by | string | Поле сортировки (default: created_at) |
| sort_order | string | asc или desc (default: desc) |
Response (200 OK):
{
"data": [...],
"total": 100,
"page": 1,
"page_size": 20,
"total_pages": 5
}
6.6 Мои комментарии
GET /comments/my
Headers: Authorization: Bearer <token>
Response (200 OK): (тот же формат, что и список)
6.7 Комментарии по отзыву
GET /comments/feedback/{feedbackID}
Response (200 OK): (тот же формат, что и список)
6.8 Ответы на комментарий
GET /comments/replies/{parentID}
Response (200 OK): (тот же формат, что и список)
6.9 Верификация комментария (админ)
PUT /comments/{id}/verify
Headers: Authorization: Bearer <token> (требуются права admin)
Request Body:
{
"verified": true
}
Response (200 OK):
{
"message": "Comment verified successfully"
}
6.10 Статистика комментариев
GET /comments/stats
Response (200 OK):
{
"total_comments": 5000,
"verified_comments": 3000,
"unverified_comments": 2000,
"avg_comments_per_feedback": 5.2,
"most_active_users": [...]
}
7. Рейтинги (Ratings)
7.1 Создание рейтинга
POST /ratings
Headers: Authorization: Bearer <token>
Request Body:
{
"object_id": 1,
"platform": "tourist"
}
Response (201 Created): RatingResponse
7.2 Получение рейтинга по ID
GET /ratings/{id}
Response (200 OK): RatingResponse
7.3 Получение рейтинга объекта по платформе
GET /ratings/object/{objectID}/platform/{platform}
| Параметр | Описание |
|---|---|
| platform | entrepreneur или tourist |
Response (200 OK): RatingResponse
7.4 Список рейтингов
GET /ratings
Query Parameters:
| Параметр | Тип | Описание |
|---|---|---|
| page | int | Номер страницы (default: 1) |
| page_size | int | Размер страницы (default: 20) |
| platform | string | entrepreneur или tourist |
| owner_id | uint | Фильтр по владельцу |
| object_id | uint | Фильтр по объекту |
Response (200 OK): ListRatingsResponse
7.5 Голосование
POST /ratings/{targetID}/vote/{platform}
Headers: Authorization: Bearer <token>
Request Body:
{
"score": 5
}
Response (200 OK): RatingResponse
7.6 Мой голос
GET /ratings/{targetID}/my-vote/{platform}
Headers: Authorization: Bearer <token>
Response (200 OK):
{
"has_voted": true,
"user_score": 5
}
7.7 Обновление моего голоса
PUT /ratings/{targetID}/my-vote/{platform}
Headers: Authorization: Bearer <token>
Request Body:
{
"score": 4
}
Response (200 OK): RatingResponse
7.8 Удаление моего голоса
DELETE /ratings/{targetID}/my-vote/{platform}
Headers: Authorization: Bearer <token>
Response (204 No Content)
7.9 Статистика рейтингов
GET /ratings/stats
Response (200 OK):
{
"total_ratings": 500,
"total_votes": 5000,
"platform_distribution": {...},
"average_by_platform": {...}
}
Коды ошибок
| Код | Описание |
|---|---|
| 200 | Успешный запрос |
| 201 | Успешное создание ресурса |
| 204 | Успешное удаление (нет содержимого) |
| 400 | Ошибка валидации или неверный запрос |
| 401 | Не авторизован |
| 403 | Доступ запрещен |
| 404 | Ресурс не найден |
| 409 | Конфликт (например, пользователь уже существует) |
| 500 | Внутренняя ошибка сервера |
Формат ошибки:
{
"error": "Описание ошибки"
}
Для валидационных ошибок:
{
"error": "Validation failed",
"fields": [
"field email is invalid: required",
"field password is invalid: min"
]
}
8. Тестирование
8.1 Запуск интеграционных тестов
Интеграционные тесты используют in-memory SQLite и mock-репозитории. Запуск:
cd main_dc/yalarba/api_yal
CGO_ENABLED=1 go test ./tests/integration/... -v -count=1
Windows (PowerShell):
$env:CGO_ENABLED=1; go test ./tests/integration/... -v -count=1
Windows (cmd.exe):
set CGO_ENABLED=1 && go test ./tests/integration/... -v -count=1
Требования:
- Go 1.21+
CGO_ENABLED=1(используетсяmattn/go-sqlite3)- Зависимости устанавливаются автоматически через
go mod download
8.2 Структура тестов
tests/
├── integration/
│ ├── account_test.go # Тесты аккаунтов (профиль, обновление, удаление)
│ ├── appeal_test.go # Тесты обращений (CRUD, мои обращения)
│ ├── auth_test.go # Тесты аутентификации (регистрация, логин, refresh, logout, сброс пароля)
│ ├── comment_test.go # Тесты комментариев (CRUD, список, статистика)
│ ├── feedback_test.go # Тесты отзывов (CRUD, список, поиск, статистика)
│ ├── object_test.go # Тесты объектов (CRUD, поиск, nearby)
│ └── rating_test.go # Тесты рейтингов (голосование, статистика)
└── testutils/
├── fixtures.go # DTO и хелперы создания тестовых данных
├── mock_appeal_repository.go # Mock-репозиторий обращений (map-based)
├── mock_object_repository.go # Mock-репозиторий объектов (map-based)
├── setup.go # TestConfig, TestUser, HTTP-клиент
└── test_server.go # Chi-роутер, in-memory SQLite, middleware
8.3 Особенности
- Общий сервер: Все тесты в одном запуске используют один экземпляр
TestServer(синглтон черезsync.Once). - In-memory БД: SQLite
file::memory:?cache=shared, данные не сохраняются между запусками. - Mock-репозитории:
ObjectRepositoryиAppealRepositoryзаменены на map-based реализации (избегают Haversine и jsonb-зависимостей). - Автоочистка: Тестовые пользователи удаляются после каждого теста через
CleanupTestUser. - Bearer-токены: JWT токен создаётся при регистрации и передаётся в
Authorizationзаголовке.
8.4 Диагностика
Для просмотра HTTP-логов сервера:
CGO_ENABLED=1 go test ./tests/integration/... -v -count=1 2>&1 | grep "\[ValitovGazizPC"
Фильтрация по конкретному тесту:
CGO_ENABLED=1 go test ./tests/integration/... -v -count=1 -run "TestAuthFlow/PasswordReset"