main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

47
tests/setup_mobile_test.py Executable file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python
import json
import requests
import sys
import traceback
from datetime import date
# Запрашиваем токен авторизации (предполагается, что он уже есть в системе)
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyOSIsImVtYWlsIjoidGVzdDJAZXhhbXBsZS5jb20iLCJleHAiOjE3NTg4NjY5ODJ9._AXkBLeMI4zxC9shFUS3744miuyO8CDnJD1X1AqbLsw"
# Данные для мобильного запроса
mobile_data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3,
"symptoms": ["CRAMPS", "HEADACHE"],
"mood": "NORMAL",
"notes": "Запись из мобильного приложения"
}
# Заголовки с токеном авторизации
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}"
}
# Сохраняем токен в файл для повторного использования
with open('auth_token.txt', 'w') as f:
f.write(TOKEN)
print(f"Токен сохранен в файл auth_token.txt")
print(f"Данные для запроса к мобильному API сохранены в переменной mobile_data")
print("\nПример использования:")
print('curl -v -X POST http://localhost:8004/api/v1/calendar/entries/mobile -H "Content-Type: application/json" -H "Authorization: Bearer $(cat auth_token.txt)" -d \'{"date": "2025-09-26", "type": "MENSTRUATION", "flow_intensity": 3, "symptoms": ["CRAMPS", "HEADACHE"], "mood": "NORMAL", "notes": "Тестовая запись"}\'')
# Сохраняем пример запроса в файл для удобства
with open('mobile_api_example.sh', 'w') as f:
f.write('#!/bin/bash\n\n')
f.write('# Пример запроса к мобильному API календарного сервиса\n')
f.write('curl -v -X POST http://localhost:8004/api/v1/calendar/entries/mobile \\\n')
f.write(' -H "Content-Type: application/json" \\\n')
f.write(' -H "Authorization: Bearer $(cat auth_token.txt)" \\\n')
f.write(' -d \'{"date": "2025-09-26", "type": "MENSTRUATION", "flow_intensity": 3, "symptoms": ["CRAMPS", "HEADACHE"], "mood": "NORMAL", "notes": "Тестовая запись"}\'\n')
print("\nПример запроса также сохранен в файле mobile_api_example.sh")
print("Можно выполнить: chmod +x mobile_api_example.sh && ./mobile_api_example.sh")

66
tests/simple_test.py Normal file
View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python
import json
import sys
import requests
import traceback
from datetime import date
# API Gateway endpoint
BASE_URL = "http://localhost:8004"
def main():
try:
print("Начинаем тест мобильного эндпоинта...")
# Проверка работоспособности сервиса
print("Проверяем работоспособность сервиса...")
health_response = requests.get(f"{BASE_URL}/health")
print(f"Ответ о статусе: {health_response.status_code}")
print(f"Тело ответа: {health_response.text}")
# Данные в формате мобильного приложения
mobile_data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3, # средний (1-5)
"symptoms": ["CRAMPS", "HEADACHE"], # массив строк
"mood": "NORMAL",
"notes": "Тестовая запись из мобильного приложения"
}
print(f"Отправляемые данные: {json.dumps(mobile_data, indent=2)}")
# Отправляем запрос к тестовому эндпоинту
print(f"Отправляем запрос на {BASE_URL}/debug/mobile-entry")
response = requests.post(
f"{BASE_URL}/debug/mobile-entry",
json=mobile_data
)
print(f"Статус ответа: {response.status_code}")
print(f"Заголовки ответа: {response.headers}")
try:
response_json = response.json()
print(f"Тело ответа: {json.dumps(response_json, indent=2)}")
except json.JSONDecodeError:
print(f"Ответ не является JSON: {response.text}")
if response.status_code >= 200 and response.status_code < 300:
print("Тест успешно завершен!")
else:
print("Тест завершился с ошибкой.")
sys.exit(1)
except requests.exceptions.ConnectionError:
print(f"Ошибка подключения к {BASE_URL}. Убедитесь, что сервис запущен.")
traceback.print_exc()
sys.exit(1)
except Exception as e:
print(f"Неожиданная ошибка: {e}")
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

103
tests/test_calendar_mobile.py Executable file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python3
import json
import sys
import requests
from datetime import date, datetime
import time
# Настройки для тестирования
BASE_URL = "http://localhost:8000" # URL API Gateway
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyOSIsImVtYWlsIjoidGVzdDJAZXhhbXBsZS5jb20iLCJleHAiOjE3NTg4NjY5ODJ9._AXkBLeMI4zxC9shFUS3744miuyO8CDnJD1X1AqbLsw"
# Функция для добавления записи в календарь через мобильный эндпоинт
def create_mobile_calendar_entry():
url = f"{BASE_URL}/api/v1/calendar/entries/mobile"
# Данные для создания записи в формате мобильного приложения
today_str = date.today().isoformat()
data = {
"date": today_str,
"type": "MENSTRUATION",
"flow_intensity": 3, # medium
"symptoms": ["CRAMPS", "HEADACHE"],
"mood": "HAPPY",
"notes": "Тестовая запись из мобильного приложения"
}
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"
}
print(f"\n=== Тест мобильного эндпоинта ===")
print(f"Отправка запроса POST на {url}")
print(f"Данные: {json.dumps(data, indent=2, ensure_ascii=False)}")
response = requests.post(url, json=data, headers=headers)
print(f"Код ответа: {response.status_code}")
if response.status_code == 201:
print("✓ Запрос успешно выполнен")
print(f"Ответ: {json.dumps(response.json(), indent=2, ensure_ascii=False)}")
return True
else:
print(f"✗ Ошибка при выполнении запроса: {response.text}")
return False
# Функция для получения записей календаря
def get_calendar_entries():
url = f"{BASE_URL}/api/v1/entries"
headers = {
"Authorization": f"Bearer {TOKEN}"
}
print(f"\n=== Получение записей календаря ===")
print(f"Отправка запроса GET на {url}")
response = requests.get(url, headers=headers)
print(f"Код ответа: {response.status_code}")
if response.status_code == 200:
print("✓ Запрос успешно выполнен")
entries = response.json()
print(f"Количество записей: {len(entries)}")
return True
else:
print(f"✗ Ошибка при выполнении запроса: {response.text}")
return False
if __name__ == "__main__":
print("Тестирование API календаря для мобильного приложения")
# Проверяем, что сервисы запущены
try:
health_check = requests.get(f"{BASE_URL}/api/v1/gateway/health")
if health_check.status_code != 200:
print("API Gateway недоступен. Убедитесь, что сервисы запущены.")
sys.exit(1)
except requests.exceptions.ConnectionError:
print("Не удалось подключиться к API Gateway. Убедитесь, что сервисы запущены.")
sys.exit(1)
# Запускаем тесты
success = True
# Тест 1: Создание записи в календаре через мобильный эндпоинт
if not create_mobile_calendar_entry():
success = False
# Тест 2: Проверка получения записей
if not get_calendar_entries():
success = False
# Выводим общий результат
if success:
print("\nВсе тесты пройдены успешно!")
sys.exit(0)
else:
print("\n❌ Некоторые тесты не пройдены.")
sys.exit(1)

30
tests/test_debug_endpoint.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# Проверяем, запущен ли сервер календаря
if ! curl -s http://localhost:8004/health > /dev/null; then
echo "Сервер календаря не запущен. Запускаем..."
cd /home/trevor/dev/chat
source .venv/bin/activate
PYTHONPATH=/home/trevor/dev/chat python -m uvicorn services.calendar_service.main:app --host 0.0.0.0 --port 8004 &
sleep 5
echo "Сервер запущен в фоновом режиме"
fi
echo "Проверка работоспособности сервера..."
HEALTH_RESPONSE=$(curl -s http://localhost:8004/health)
echo "Ответ сервера: $HEALTH_RESPONSE"
echo -e "\n=== Тестирование отладочного эндпоинта (без аутентификации) ==="
curl -v -X POST http://localhost:8004/debug/mobile-entry \
-H "Content-Type: application/json" \
-d '{
"date": "2023-09-26",
"type": "MENSTRUATION",
"flow_intensity": 3,
"symptoms": ["CRAMPS", "HEADACHE"],
"mood": "NORMAL",
"notes": "Тестовая запись из скрипта"
}'
echo -e "\n\n=== Проверка созданных записей ==="
curl -s http://localhost:8004/debug/entries | head -n 20

121
tests/test_mobile_api.py Executable file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env python
import json
import requests
from datetime import date
# Используем сохраненный токен авторизации
with open('auth_token.txt', 'r') as f:
TOKEN = f.read().strip()
# Базовый URL для тестирования
BASE_URL = "http://localhost:8004"
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}"
}
# Функция для тестирования мобильного эндпоинта
def test_mobile_endpoint():
url = f"{BASE_URL}/api/v1/calendar/entries/mobile"
# Данные для мобильного запроса
data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3,
"symptoms": ["CRAMPS", "HEADACHE"],
"mood": "NORMAL",
"notes": "Тест мобильного API"
}
print("\n1. Тестирование создания записи через мобильный API...")
print(f"POST {url}")
print(f"Данные: {json.dumps(data, ensure_ascii=False)}")
try:
response = requests.post(url, json=data, headers=HEADERS)
if response.status_code == 201:
print(f"✅ Успешно! Статус: {response.status_code}")
print(f"Ответ: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
# Теперь проверим, что мы можем получить запись
entry_id = response.json().get("id")
return entry_id
else:
print(f"❌ Ошибка! Статус: {response.status_code}")
print(f"Ответ: {response.text}")
return None
except Exception as e:
print(f"❌ Исключение: {str(e)}")
return None
# Функция для проверки получения всех записей
def test_get_entries(entry_id=None):
url = f"{BASE_URL}/api/v1/calendar/entries"
print("\n2. Проверка получения записей календаря...")
print(f"GET {url}")
try:
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
print(f"✅ Успешно! Статус: {response.status_code}")
entries = response.json()
print(f"Получено записей: {len(entries)}")
# Если у нас есть ID записи, которую мы создали ранее, проверим ее наличие
if entry_id:
found = False
for entry in entries:
if entry.get("id") == entry_id:
found = True
print(f"✅ Созданная запись найдена в списке (ID: {entry_id})")
break
if not found:
print(f"❌ Созданная запись не найдена в списке (ID: {entry_id})")
else:
print(f"❌ Ошибка! Статус: {response.status_code}")
print(f"Ответ: {response.text}")
except Exception as e:
print(f"❌ Исключение: {str(e)}")
# Тестирование отладочного эндпоинта (без аутентификации)
def test_debug_endpoint():
url = f"{BASE_URL}/debug/mobile-entry"
# Данные для мобильного запроса
data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3,
"symptoms": ["CRAMPS", "HEADACHE"],
"mood": "NORMAL",
"notes": "Тест отладочного API"
}
print("\n3. Тестирование отладочного эндпоинта...")
print(f"POST {url}")
print(f"Данные: {json.dumps(data, ensure_ascii=False)}")
try:
response = requests.post(url, json=data)
if response.status_code == 200:
print(f"✅ Успешно! Статус: {response.status_code}")
print(f"Ответ: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
else:
print(f"❌ Ошибка! Статус: {response.status_code}")
print(f"Ответ: {response.text}")
except Exception as e:
print(f"❌ Исключение: {str(e)}")
if __name__ == "__main__":
print("=== Тестирование мобильного API календарного сервиса ===")
entry_id = test_mobile_endpoint()
test_get_entries(entry_id)
test_debug_endpoint()
print("\nТестирование завершено!")

103
tests/test_mobile_endpoint.py Executable file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python
import json
import requests
import sys
from datetime import date
# API Gateway endpoint
BASE_URL = "http://localhost:8004"
# Имя пользователя и пароль для аутентификации
# (предполагается, что у вас есть настроенный доступ)
AUTH_DATA = {
"username": "test_user",
"password": "test_password"
}
# Получение токена аутентификации
def get_token():
# Обычно мы бы получили токен из сервиса аутентификации,
# но для теста будем использовать фиктивный токен
# или можем запросить его из user_service
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InRlc3RfdXNlciIsImVtYWlsIjoidGVzdEBleGFtcGxlLmNvbSIsImV4cCI6MTY5ODI0OTc2Mn0.HS5-cPVZwv55h0CZO0q_Z_1vNY9w8B6YsPJytj4UI0A"
# Проверка работоспособности сервиса
def check_health():
try:
response = requests.get(f"{BASE_URL}/health")
if response.status_code == 200:
print(f"Сервис календаря работает. Ответ: {response.json()}")
return True
else:
print(f"Сервис календаря доступен, но возвращает ошибку: {response.status_code}")
return False
except requests.exceptions.ConnectionError:
print(f"Ошибка подключения к {BASE_URL}. Убедитесь, что сервис запущен.")
return False
# Тестовые данные для мобильного приложения
def test_create_calendar_entry_mobile():
try:
token = get_token()
print(f"Токен получен: {token[:15]}...")
# Данные в формате мобильного приложения
mobile_data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3, # средний (1-5)
"symptoms": ["CRAMPS", "HEADACHE"], # массив строк
"mood": "NORMAL",
"notes": "Тестовая запись из мобильного приложения"
}
print(f"Отправляемые данные: {json.dumps(mobile_data, indent=2)}")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
print(f"Отправляем запрос на {BASE_URL}/debug/mobile-entry")
# Используем тестовый эндпоинт без аутентификации
print(f"Отправляем запрос на {BASE_URL}/debug/mobile-entry")
response = requests.post(
f"{BASE_URL}/debug/mobile-entry",
json=mobile_data
)
print(f"Статус ответа: {response.status_code}")
if response.status_code >= 200 and response.status_code < 300:
print(f"Тело успешного ответа: {json.dumps(response.json(), indent=2)}")
return response.json()
else:
print("Ошибка при создании записи")
try:
print(f"Тело ответа с ошибкой: {json.dumps(response.json(), indent=2)}")
except json.JSONDecodeError:
print(f"Тело ответа не является JSON: {response.text}")
return None
except requests.exceptions.RequestException as e:
print(f"Ошибка при выполнении запроса: {e}")
return None
except Exception as e:
print(f"Неожиданная ошибка: {e}")
return None
if __name__ == "__main__":
print("Тестирование создания записи календаря из мобильного приложения...")
if not check_health():
print("Сервис недоступен. Тестирование прекращено.")
sys.exit(1)
result = test_create_calendar_entry_mobile()
if result:
print("Тест успешно завершен!")
else:
print("Тест завершился с ошибкой.")
sys.exit(1)

View File

@@ -0,0 +1,171 @@
#!/usr/bin/env python
import json
import requests
import sys
import traceback
from datetime import date
# API Gateway endpoint
BASE_URL = "http://localhost:8004"
# Токен для аутентификации - замените на действующий токен
AUTH_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyOSIsImVtYWlsIjoidGVzdDJAZXhhbXBsZS5jb20iLCJleHAiOjE3NTg4NjY5ODJ9._AXkBLeMI4zxC9shFUS3744miuyO8CDnJD1X1AqbLsw"
def test_health():
"""Проверка доступности сервиса"""
try:
response = requests.get(f"{BASE_URL}/health")
print(f"Статус сервиса: {response.status_code}")
print(f"Ответ: {response.text}")
return response.status_code == 200
except Exception as e:
print(f"Ошибка при проверке сервиса: {e}")
return False
def test_authenticated_endpoint():
"""Тестирование аутентифицированного эндпоинта для мобильного приложения"""
print("\n=== Тестирование аутентифицированного эндпоинта ===")
# Данные в формате мобильного приложения
mobile_data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3,
"symptoms": ["CRAMPS", "HEADACHE"],
"mood": "NORMAL",
"notes": "Запись из мобильного приложения через аутентифицированный эндпоинт"
}
print(f"Отправляемые данные: {json.dumps(mobile_data, indent=2)}")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {AUTH_TOKEN}"
}
try:
response = requests.post(
f"{BASE_URL}/api/v1/calendar/entries/mobile",
headers=headers,
json=mobile_data,
timeout=10
)
print(f"Статус ответа: {response.status_code}")
if response.status_code >= 200 and response.status_code < 300:
print(f"Тело успешного ответа: {json.dumps(response.json(), indent=2)}")
return True
else:
print("Ошибка при создании записи через аутентифицированный эндпоинт")
try:
print(f"Тело ответа с ошибкой: {json.dumps(response.json(), indent=2)}")
except:
print(f"Тело ответа не является JSON: {response.text}")
return False
except Exception as e:
print(f"Ошибка при выполнении запроса: {e}")
traceback.print_exc()
return False
def test_debug_endpoint():
"""Тестирование отладочного эндпоинта для мобильного приложения (без аутентификации)"""
print("\n=== Тестирование отладочного эндпоинта (без аутентификации) ===")
# Данные в формате мобильного приложения
mobile_data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 4,
"symptoms": ["BACKACHE", "BLOATING"],
"mood": "HAPPY",
"notes": "Запись из мобильного приложения через отладочный эндпоинт"
}
print(f"Отправляемые данные: {json.dumps(mobile_data, indent=2)}")
headers = {
"Content-Type": "application/json"
}
try:
response = requests.post(
f"{BASE_URL}/debug/mobile-entry",
headers=headers,
json=mobile_data,
timeout=10
)
print(f"Статус ответа: {response.status_code}")
if response.status_code >= 200 and response.status_code < 300:
print(f"Тело успешного ответа: {json.dumps(response.json(), indent=2)}")
return True
else:
print("Ошибка при создании записи через отладочный эндпоинт")
try:
print(f"Тело ответа с ошибкой: {json.dumps(response.json(), indent=2)}")
except:
print(f"Тело ответа не является JSON: {response.text}")
return False
except Exception as e:
print(f"Ошибка при выполнении запроса: {e}")
traceback.print_exc()
return False
def verify_entries_created():
"""Проверка, что записи были созданы в БД"""
print("\n=== Проверка созданных записей ===")
try:
response = requests.get(f"{BASE_URL}/debug/entries")
print(f"Статус ответа: {response.status_code}")
if response.status_code == 200:
entries = response.json()
print(f"Количество записей в БД: {len(entries)}")
print("Последние 2 записи:")
for entry in entries[-2:]:
print(json.dumps(entry, indent=2))
return True
else:
print(f"Ошибка при получении записей: {response.status_code}")
return False
except Exception as e:
print(f"Ошибка при проверке записей: {e}")
traceback.print_exc()
return False
def main():
print("=== Тестирование мобильных эндпоинтов календарного сервиса ===")
if not test_health():
print("Сервис недоступен. Завершение тестирования.")
return 1
debug_result = test_debug_endpoint()
auth_result = test_authenticated_endpoint()
if debug_result and auth_result:
print("\nВсе тесты успешно пройдены!")
verify_entries_created()
return 0
else:
print("\nНекоторые тесты не пройдены.")
if debug_result:
print("✓ Отладочный эндпоинт работает")
else:
print("✗ Отладочный эндпоинт не работает")
if auth_result:
print("✓ Аутентифицированный эндпоинт работает")
else:
print("✗ Аутентифицированный эндпоинт не работает")
verify_entries_created()
return 1
if __name__ == "__main__":
sys.exit(main())

347
tests/test_nutrition_api.py Executable file
View File

@@ -0,0 +1,347 @@
#!/usr/bin/env python3
"""
Скрипт для тестирования API сервиса питания (Nutrition Service)
"""
import os
import sys
import json
import requests
from datetime import datetime
from dotenv import load_dotenv
# Загружаем .env файл
current_dir = os.path.dirname(os.path.abspath(__file__))
env_path = os.path.join(current_dir, ".env")
load_dotenv(env_path)
print(f"✅ Загружен .env из: {env_path}")
# Базовый URL API
BASE_URL = os.environ.get("NUTRITION_API_URL", "http://localhost:8006/api/v1/nutrition")
AUTH_URL = os.environ.get("AUTH_API_URL", "http://localhost:8001/api/v1/auth")
# Настройки для тестовых данных
TEST_USER = {
"username": "test_nutrition_user",
"password": "Test123!",
"email": "test_nutrition@example.com",
"first_name": "Test",
"last_name": "Nutrition",
"phone": "+79991234999"
}
def get_auth_token():
"""Получение токена авторизации"""
print("\n🔑 Получаем токен авторизации...")
# Пытаемся сначала войти
try:
login_data = {
"username": TEST_USER["username"],
"password": TEST_USER["password"]
}
login_response = requests.post(
f"{AUTH_URL}/login",
json=login_data
)
if login_response.status_code == 200:
token = login_response.json().get("access_token")
print("✅ Успешный вход в систему!")
return token
except Exception as e:
print(f"⚠️ Ошибка при попытке входа: {e}")
# Если вход не удался, пробуем регистрацию
try:
register_response = requests.post(
f"{AUTH_URL}/register",
json=TEST_USER
)
if register_response.status_code == 201:
print("✅ Пользователь успешно зарегистрирован!")
# Теперь входим с новыми учетными данными
login_data = {
"username": TEST_USER["username"],
"password": TEST_USER["password"]
}
login_response = requests.post(
f"{AUTH_URL}/login",
json=login_data
)
if login_response.status_code == 200:
token = login_response.json().get("access_token")
print("✅ Успешный вход в систему!")
return token
except Exception as e:
print(f"❌ Ошибка при регистрации: {e}")
print("Не удалось получить токен авторизации")
return None
def search_food(token, query="apple", max_results=5):
"""Поиск продуктов питания"""
print(f"\n🔍 Поиск продуктов по запросу '{query}'...")
headers = {"Authorization": f"Bearer {token}"}
data = {
"query": query,
"max_results": max_results
}
try:
response = requests.post(
f"{BASE_URL}/search",
json=data,
headers=headers
)
print(f"📥 Код ответа: {response.status_code}")
if response.status_code == 200:
results = response.json()
print(f"✅ Найдено продуктов: {len(results)}")
# Выводим первые 3 результата
for i, food in enumerate(results[:3]):
print(f" {i+1}. [{food.get('id')}] {food.get('name')}")
print(f" {food.get('description')}")
print(f" Калории: {food.get('calories')} ккал/100г")
return results
else:
print(f"❌ Ошибка при поиске: {response.status_code}")
print(response.text)
return None
except Exception as e:
print(f"❌ Исключение при поиске: {e}")
return None
def add_diary_entry(token, food_id=1):
"""Добавление записи в дневник питания"""
print(f"\n📝 Добавление записи в дневник питания (продукт ID: {food_id})...")
headers = {"Authorization": f"Bearer {token}"}
today = datetime.now().strftime("%Y-%m-%d")
data = {
"food_item_id": food_id,
"entry_date": today,
"meal_type": "breakfast",
"quantity": 1.0,
"unit": "piece",
"notes": "Тестовая запись"
}
try:
response = requests.post(
f"{BASE_URL}/diary",
json=data,
headers=headers
)
print(f"📥 Код ответа: {response.status_code}")
if response.status_code in [200, 201]:
result = response.json()
print("✅ Запись успешно добавлена в дневник питания!")
print(f" ID записи: {result.get('id')}")
print(f" Калории: {result.get('calories')} ккал")
return result
else:
print(f"❌ Ошибка при добавлении записи: {response.status_code}")
print(response.text)
return None
except Exception as e:
print(f"❌ Исключение при добавлении записи: {e}")
return None
def get_diary_entries(token):
"""Получение записей дневника за текущий день"""
print("\n📋 Получение записей дневника питания...")
headers = {"Authorization": f"Bearer {token}"}
today = datetime.now().strftime("%Y-%m-%d")
try:
response = requests.get(
f"{BASE_URL}/diary?date={today}",
headers=headers
)
print(f"📥 Код ответа: {response.status_code}")
if response.status_code == 200:
results = response.json()
print(f"✅ Получено записей: {len(results)}")
# Выводим записи
for i, entry in enumerate(results):
print(f" {i+1}. Прием пищи: {entry.get('meal_type')}")
print(f" Продукт ID: {entry.get('food_item_id')}")
print(f" Калории: {entry.get('calories')} ккал")
return results
else:
print(f"❌ Ошибка при получении записей: {response.status_code}")
print(response.text)
return None
except Exception as e:
print(f"❌ Исключение при получении записей: {e}")
return None
def add_water_entry(token, amount_ml=250):
"""Добавление записи о потреблении воды"""
print(f"\n💧 Добавление записи о потреблении воды ({amount_ml} мл)...")
headers = {"Authorization": f"Bearer {token}"}
today = datetime.now().strftime("%Y-%m-%d")
data = {
"amount_ml": amount_ml,
"entry_date": today,
"notes": "Тестовая запись"
}
try:
response = requests.post(
f"{BASE_URL}/water",
json=data,
headers=headers
)
print(f"📥 Код ответа: {response.status_code}")
if response.status_code in [200, 201]:
result = response.json()
print("✅ Запись о потреблении воды успешно добавлена!")
print(f" ID записи: {result.get('id')}")
print(f" Объем: {result.get('amount_ml')} мл")
return result
else:
print(f"❌ Ошибка при добавлении записи о воде: {response.status_code}")
print(response.text)
return None
except Exception as e:
print(f"❌ Исключение при добавлении записи о воде: {e}")
return None
def add_activity_entry(token):
"""Добавление записи о физической активности"""
print("\n🏃‍♀️ Добавление записи о физической активности...")
headers = {"Authorization": f"Bearer {token}"}
today = datetime.now().strftime("%Y-%m-%d")
data = {
"entry_date": today,
"activity_type": "walking",
"duration_minutes": 30,
"distance_km": 2.5,
"intensity": "medium",
"notes": "Тестовая активность"
}
try:
response = requests.post(
f"{BASE_URL}/activity",
json=data,
headers=headers
)
print(f"📥 Код ответа: {response.status_code}")
if response.status_code in [200, 201]:
result = response.json()
print("✅ Запись о физической активности успешно добавлена!")
print(f" ID записи: {result.get('id')}")
print(f" Тип: {result.get('activity_type')}")
print(f" Продолжительность: {result.get('duration_minutes')} мин")
print(f" Потрачено калорий: {result.get('calories_burned')} ккал")
return result
else:
print(f"❌ Ошибка при добавлении записи об активности: {response.status_code}")
print(response.text)
return None
except Exception as e:
print(f"❌ Исключение при добавлении записи об активности: {e}")
return None
def get_daily_summary(token):
"""Получение дневной сводки"""
print("\n📊 Получение сводки за день...")
headers = {"Authorization": f"Bearer {token}"}
today = datetime.now().strftime("%Y-%m-%d")
try:
response = requests.get(
f"{BASE_URL}/summary?date={today}",
headers=headers
)
print(f"📥 Код ответа: {response.status_code}")
if response.status_code == 200:
result = response.json()
print("✅ Сводка за день успешно получена!")
print(f" Всего калорий: {result.get('total_calories')} ккал")
print(f" Всего белка: {result.get('total_protein')} г")
print(f" Всего жиров: {result.get('total_fat')} г")
print(f" Всего углеводов: {result.get('total_carbs')} г")
print(f" Потреблено воды: {result.get('water_consumed_ml')} мл")
print(f" Активность: {result.get('activity_minutes')} мин")
print(f" Сожжено калорий: {result.get('calories_burned')} ккал")
return result
else:
print(f"❌ Ошибка при получении сводки: {response.status_code}")
print(response.text)
return None
except Exception as e:
print(f"❌ Исключение при получении сводки: {e}")
return None
def main():
"""Основная функция для тестирования API сервиса питания"""
print("\n🚀 Запуск тестирования API сервиса питания...\n")
# Получаем токен авторизации
token = get_auth_token()
if not token:
print("❌ Невозможно продолжить тестирование без авторизации")
sys.exit(1)
# Выполняем поиск продуктов
search_results = search_food(token, "apple")
if search_results and len(search_results) > 0:
# Используем первый найденный продукт для дальнейшего тестирования
food_id = search_results[0].get("id")
# Добавляем запись в дневник питания
add_diary_entry(token, food_id)
# Получаем записи дневника
get_diary_entries(token)
else:
# Если поиск не дал результатов, продолжаем тестирование с предполагаемым ID продукта
print("⚠️ Используем предполагаемый ID продукта для дальнейших тестов")
add_diary_entry(token, 1)
# Добавляем записи о воде и активности
add_water_entry(token)
add_activity_entry(token)
# Получаем дневную сводку
get_daily_summary(token)
print("\n✅ Тестирование API сервиса питания завершено!")
if __name__ == "__main__":
main()

189
tests/test_nutrition_service.sh Executable file
View File

@@ -0,0 +1,189 @@
#!/bin/bash
# Скрипт для тестирования API сервиса питания через cURL
# Настройки
API_BASE_URL="http://localhost:8006/api/v1/nutrition"
AUTH_URL="http://localhost:8001/api/v1/auth"
TODAY=$(date +"%Y-%m-%d")
TEST_USERNAME="test_nutrition_user"
TEST_PASSWORD="Test123!"
# Цветной вывод
GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
echo -e "${BLUE}🚀 Запуск тестов для Nutrition Service API${NC}"
echo "---------------------------------------------"
# Шаг 1: Авторизация и получение токена
echo -e "${BLUE}📝 Шаг 1: Получение токена авторизации${NC}"
# Попытка входа
login_response=$(curl -s -X POST "${AUTH_URL}/login" \
-H "Content-Type: application/json" \
-d '{
"username": "'"${TEST_USERNAME}"'",
"password": "'"${TEST_PASSWORD}"'"
}')
# Проверяем, успешен ли вход
if [[ $login_response == *"access_token"* ]]; then
TOKEN=$(echo $login_response | grep -o '"access_token":"[^"]*' | sed 's/"access_token":"//')
echo -e "${GREEN}✅ Вход успешен!${NC}"
else
echo -e "${YELLOW}⚠️ Вход не удался, пробуем регистрацию...${NC}"
# Пробуем зарегистрировать пользователя
curl -s -X POST "${AUTH_URL}/register" \
-H "Content-Type: application/json" \
-d '{
"email": "'"${TEST_USERNAME}@example.com"'",
"username": "'"${TEST_USERNAME}"'",
"password": "'"${TEST_PASSWORD}"'",
"first_name": "Test",
"last_name": "Nutrition",
"phone": "+79991234999"
}' > /dev/null
# После регистрации пробуем войти снова
login_response=$(curl -s -X POST "${AUTH_URL}/login" \
-H "Content-Type: application/json" \
-d '{
"username": "'"${TEST_USERNAME}"'",
"password": "'"${TEST_PASSWORD}"'"
}')
if [[ $login_response == *"access_token"* ]]; then
TOKEN=$(echo $login_response | grep -o '"access_token":"[^"]*' | sed 's/"access_token":"//')
echo -e "${GREEN}✅ Регистрация и вход успешны!${NC}"
else
echo -e "${RED}Не удалось получить токен авторизации${NC}"
echo "Ответ сервера: $login_response"
exit 1
fi
fi
# Шаг 2: Поиск продуктов
echo -e "\n${BLUE}📝 Шаг 2: Поиск продуктов${NC}"
search_response=$(curl -s -X POST "${API_BASE_URL}/search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"query": "apple",
"max_results": 5
}')
echo "Результат поиска:"
echo "$search_response" | grep -o '"name":"[^"]*' | head -3 | sed 's/"name":"/- /'
echo "..."
# Получаем ID первого продукта из результатов поиска
FOOD_ID=$(echo $search_response | grep -o '"id":[0-9]*' | head -1 | sed 's/"id"://')
if [[ -z "$FOOD_ID" ]]; then
echo -e "${YELLOW}⚠️ Не удалось получить ID продукта, используем значение по умолчанию${NC}"
FOOD_ID=1
else
echo -e "${GREEN}✅ Получен ID продукта: ${FOOD_ID}${NC}"
fi
# Шаг 3: Добавление записи в дневник питания
echo -e "\n${BLUE}📝 Шаг 3: Добавление записи в дневник питания${NC}"
diary_response=$(curl -s -X POST "${API_BASE_URL}/diary" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"food_item_id": '"${FOOD_ID}"',
"entry_date": "'"${TODAY}"'",
"meal_type": "breakfast",
"quantity": 1.5,
"unit": "piece",
"notes": "Тестовая запись"
}')
if [[ $diary_response == *"id"* ]]; then
echo -e "${GREEN}✅ Запись добавлена в дневник питания${NC}"
echo "Детали записи:"
echo "$diary_response" | grep -o '"calories":[0-9.]*' | sed 's/"calories":/Калории: /'
else
echo -e "${RED}❌ Ошибка при добавлении записи в дневник${NC}"
echo "Ответ сервера: $diary_response"
fi
# Шаг 4: Получение записей дневника
echo -e "\n${BLUE}📝 Шаг 4: Получение записей дневника${NC}"
get_diary_response=$(curl -s -X GET "${API_BASE_URL}/diary?date=${TODAY}" \
-H "Authorization: Bearer ${TOKEN}")
if [[ $get_diary_response == *"meal_type"* ]]; then
echo -e "${GREEN}✅ Записи дневника успешно получены${NC}"
echo "Количество записей: $(echo $get_diary_response | grep -o '"meal_type"' | wc -l)"
else
echo -e "${YELLOW}⚠️ Нет записей в дневнике или ошибка получения${NC}"
echo "Ответ сервера: $get_diary_response"
fi
# Шаг 5: Добавление записи о потреблении воды
echo -e "\n${BLUE}📝 Шаг 5: Добавление записи о потреблении воды${NC}"
water_response=$(curl -s -X POST "${API_BASE_URL}/water" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"amount_ml": 250,
"entry_date": "'"${TODAY}"'",
"notes": "Утренний стакан воды"
}')
if [[ $water_response == *"id"* ]]; then
echo -e "${GREEN}✅ Запись о потреблении воды добавлена${NC}"
echo "Детали записи:"
echo "$water_response" | grep -o '"amount_ml":[0-9]*' | sed 's/"amount_ml":/Объем (мл): /'
else
echo -e "${RED}❌ Ошибка при добавлении записи о воде${NC}"
echo "Ответ сервера: $water_response"
fi
# Шаг 6: Добавление записи о физической активности
echo -e "\n${BLUE}📝 Шаг 6: Добавление записи о физической активности${NC}"
activity_response=$(curl -s -X POST "${API_BASE_URL}/activity" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"entry_date": "'"${TODAY}"'",
"activity_type": "running",
"duration_minutes": 30,
"distance_km": 5.2,
"intensity": "medium",
"notes": "Утренняя пробежка"
}')
if [[ $activity_response == *"id"* ]]; then
echo -e "${GREEN}✅ Запись о физической активности добавлена${NC}"
echo "Детали записи:"
echo "$activity_response" | grep -o '"duration_minutes":[0-9]*' | sed 's/"duration_minutes":/Продолжительность (мин): /'
echo "$activity_response" | grep -o '"calories_burned":[0-9.]*' | sed 's/"calories_burned":/Сожжено калорий: /'
else
echo -e "${RED}❌ Ошибка при добавлении записи об активности${NC}"
echo "Ответ сервера: $activity_response"
fi
# Шаг 7: Получение сводки за день
echo -e "\n${BLUE}📝 Шаг 7: Получение сводки за день${NC}"
summary_response=$(curl -s -X GET "${API_BASE_URL}/summary?date=${TODAY}" \
-H "Authorization: Bearer ${TOKEN}")
if [[ $summary_response == *"total_calories"* ]]; then
echo -e "${GREEN}✅ Дневная сводка успешно получена${NC}"
echo "Детали сводки:"
echo "$summary_response" | grep -o '"total_calories":[0-9.]*' | sed 's/"total_calories":/Всего калорий: /'
echo "$summary_response" | grep -o '"water_consumed_ml":[0-9]*' | sed 's/"water_consumed_ml":/Потреблено воды (мл): /'
echo "$summary_response" | grep -o '"activity_minutes":[0-9]*' | sed 's/"activity_minutes":/Минуты активности: /'
else
echo -e "${YELLOW}⚠️ Нет данных для сводки или ошибка получения${NC}"
echo "Ответ сервера: $summary_response"
fi
echo -e "\n${GREEN}✅ Тестирование API сервиса питания завершено!${NC}"

184
tests/test_standalone.py Normal file
View File

@@ -0,0 +1,184 @@
#!/usr/bin/env python
import json
import sys
import asyncio
import uvicorn
from fastapi import FastAPI, Depends, HTTPException
from datetime import date
from typing import Dict, Optional, List
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
# Импортируем необходимые модули из календарного сервиса
from services.calendar_service.schemas import (
EntryType, FlowIntensity, MoodType, CalendarEntryCreate, CalendarEntryResponse
)
from services.calendar_service.mobile_endpoint import MobileCalendarEntryCreate
from shared.database import get_db
# Создаем тестовое приложение
app = FastAPI(title="Test Mobile Endpoint")
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy", "service": "test_mobile_endpoint"}
@app.post("/debug/mobile-entry", status_code=201)
async def test_mobile_endpoint(
mobile_entry: MobileCalendarEntryCreate,
db: AsyncSession = Depends(get_db),
):
"""Test mobile endpoint without authentication"""
# Преобразуем симптомы из списка в строку
symptoms_str = ""
if mobile_entry.symptoms:
symptoms_str = ", ".join(mobile_entry.symptoms)
# Преобразуем данные из мобильного формата в формат сервера
entry_type = EntryType.PERIOD
if mobile_entry.type == "MENSTRUATION":
entry_type = EntryType.PERIOD
elif mobile_entry.type == "OVULATION":
entry_type = EntryType.OVULATION
else:
entry_type = EntryType.SYMPTOMS
# Преобразуем интенсивность потока
flow_intensity = None
if mobile_entry.flow_intensity is not None:
if mobile_entry.flow_intensity <= 1:
flow_intensity = FlowIntensity.SPOTTING
elif mobile_entry.flow_intensity <= 2:
flow_intensity = FlowIntensity.LIGHT
elif mobile_entry.flow_intensity <= 4:
flow_intensity = FlowIntensity.MEDIUM
else:
flow_intensity = FlowIntensity.HEAVY
# Преобразуем настроение
mood = None
if mobile_entry.mood:
if mobile_entry.mood == "HAPPY":
mood = MoodType.HAPPY
elif mobile_entry.mood == "SAD":
mood = MoodType.SAD
elif mobile_entry.mood == "NORMAL":
mood = MoodType.HAPPY # NORMAL мапится на HAPPY
elif mobile_entry.mood == "ANXIOUS":
mood = MoodType.ANXIOUS
elif mobile_entry.mood == "IRRITATED":
mood = MoodType.IRRITATED
elif mobile_entry.mood == "ENERGETIC":
mood = MoodType.ENERGETIC
elif mobile_entry.mood == "TIRED":
mood = MoodType.TIRED
# Создаем объект CalendarEntryResponse для возврата
response = {
"id": 1,
"user_id": 1,
"entry_date": mobile_entry.date.isoformat(),
"entry_type": entry_type.value,
"flow_intensity": flow_intensity.value if flow_intensity else None,
"period_symptoms": "",
"mood": mood.value if mood else None,
"energy_level": 1, # Минимальное значение должно быть 1
"sleep_hours": 0,
"symptoms": symptoms_str,
"medications": "",
"notes": mobile_entry.notes or "",
"created_at": date.today().isoformat(),
"updated_at": date.today().isoformat(),
}
return response
# Функция для отправки тестового запроса
async def send_test_request():
"""Отправка тестового запроса к эндпоинту"""
import httpx
# Данные в формате мобильного приложения
mobile_data = {
"date": date.today().isoformat(),
"type": "MENSTRUATION",
"flow_intensity": 3, # средний (1-5)
"symptoms": ["CRAMPS", "HEADACHE"], # массив строк
"mood": "NORMAL",
"notes": "Тестовая запись из мобильного приложения"
}
print(f"Отправляемые данные: {json.dumps(mobile_data, indent=2)}")
# Подождем, пока сервис запустится
await asyncio.sleep(2)
async with httpx.AsyncClient() as client:
try:
# Проверяем работоспособность сервиса
health_response = await client.get("http://localhost:8080/health")
print(f"Проверка работоспособности: {health_response.status_code}")
print(f"Ответ сервиса: {health_response.text}")
# Отправляем запрос к тестовому эндпоинту
response = await client.post(
"http://localhost:8080/debug/mobile-entry",
json=mobile_data
)
print(f"Статус ответа: {response.status_code}")
if response.status_code >= 200 and response.status_code < 300:
print(f"Тело успешного ответа: {json.dumps(response.json(), indent=2)}")
return True
else:
print("Ошибка при создании записи")
try:
print(f"Тело ответа с ошибкой: {json.dumps(response.json(), indent=2)}")
except:
print(f"Тело ответа не является JSON: {response.text}")
return False
except Exception as e:
print(f"Ошибка при выполнении запроса: {e}")
return False
# Основная функция
async def main():
# Запускаем сервер в отдельном процессе
config = uvicorn.Config(app, host="0.0.0.0", port=8080, log_level="info")
server = uvicorn.Server(config)
# Создаем задачу для запуска сервера
server_task = asyncio.create_task(server.serve())
# Дадим серверу немного времени на запуск
await asyncio.sleep(1)
try:
# Отправляем тестовый запрос
result = await send_test_request()
if result:
print("Тест успешно завершен!")
return 0
else:
print("Тест завершился с ошибкой.")
return 1
finally:
# Останавливаем сервер
server.should_exit = True
await server_task
if __name__ == "__main__":
# Запускаем асинхронную функцию main()
import asyncio
try:
exit_code = asyncio.run(main())
sys.exit(exit_code)
except KeyboardInterrupt:
print("Выполнение прервано")
sys.exit(1)