/
Evdik
/
Integration_System_labs4
Обзор
Документация
Войти
/
Evdik
/
Integration_System_labs4
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
client.py
667 строк
28 KB
Evdikkom
first
22 ноя 2025, 12:27
22 ноя 2025, 12:27
8ded1dc
Код
Авторство
О чём код?
import pika import json import uuid import logging import time from threading import Thread, Event from queue import Queue from config import Config logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class RabbitMQClient: def __init__(self): self.connection = None self.channel = None self.callback_queue = None self.response_queue = Queue() self.pending_requests = {} self.consumer_thread = None self.stop_event = Event() def connect(self): credentials = pika.PlainCredentials(Config.RABBITMQ_USER, Config.RABBITMQ_PASS) parameters = pika.ConnectionParameters( host=Config.RABBITMQ_HOST, port=Config.RABBITMQ_PORT, credentials=credentials ) self.connection = pika.BlockingConnection(parameters) self.channel = self.connection.channel() # Очередь для ответов result = self.channel.queue_declare(queue='', exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume( queue=self.callback_queue, on_message_callback=self.on_response, auto_ack=True ) # Запускаем поток для обработки ответов self.consumer_thread = Thread(target=self._consume_responses) self.consumer_thread.daemon = True self.consumer_thread.start() def _consume_responses(self): while not self.stop_event.is_set(): try: self.connection.process_data_events(time_limit=1) except Exception as e: logger.error(f"Error in consumer thread: {e}") time.sleep(1) def on_response(self, ch, method, props, body): correlation_id = props.correlation_id if correlation_id in self.pending_requests: response = json.loads(body) self.pending_requests[correlation_id].put(response) def send_request(self, action, data, auth, timeout=30): if not self.connection or self.connection.is_closed: self.connect() correlation_id = str(uuid.uuid4()) response_queue = Queue() self.pending_requests[correlation_id] = response_queue message = { 'id': correlation_id, 'version': 'v1', 'action': action, 'data': data, 'auth': auth } try: self.channel.basic_publish( exchange='', routing_key=Config.REQUEST_QUEUE, properties=pika.BasicProperties( reply_to=self.callback_queue, correlation_id=correlation_id, delivery_mode=2 # persistent ), body=json.dumps(message) ) # Ждем ответа с таймаутом try: response = response_queue.get(timeout=timeout) return response except: return { 'correlation_id': correlation_id, 'status': 'error', 'data': None, 'error': 'Request timeout' } finally: self.pending_requests.pop(correlation_id, None) def close(self): self.stop_event.set() if self.connection: self.connection.close() class GameStoreClient: def __init__(self, api_key): self.client = RabbitMQClient() self.api_key = api_key def create_user(self, username, email, password): return self.client.send_request('create_user', { 'username': username, 'email': email, 'password': password }, self.api_key) def login(self, username, password): return self.client.send_request('login', { 'username': username, 'password': password }, self.api_key) def get_profile(self, user_id=None): data = {} if user_id is not None: data['user_id'] = user_id return self.client.send_request('get_profile', data, self.api_key) def deposit_balance(self, amount, user_id=None): data = {'amount': amount} if user_id is not None: data['user_id'] = user_id return self.client.send_request('deposit_balance', data, self.api_key) def create_order(self, items, user_id=None): data = {'items': items} if user_id is not None: data['user_id'] = user_id return self.client.send_request('create_order', data, self.api_key) def get_games(self, page=1, per_page=10, genre=None, platform=None): return self.client.send_request('get_games', { 'page': page, 'per_page': per_page, 'genre': genre, 'platform': platform }, self.api_key) def get_game(self, game_id): return self.client.send_request('get_game', { 'game_id': game_id }, self.api_key) def create_review(self, game_id, rating, comment=None, user_id=None): data = { 'game_id': game_id, 'rating': rating } if comment is not None: data['comment'] = comment if user_id is not None: data['user_id'] = user_id return self.client.send_request('create_review', data, self.api_key) def get_orders(self, page=1, per_page=10, user_id=None): data = { 'page': page, 'per_page': per_page } if user_id is not None: data['user_id'] = user_id return self.client.send_request('get_orders', data, self.api_key) def health_check(self): return self.client.send_request('health_check', {}, self.api_key) def sales_analytics(self, period='week'): return self.client.send_request('sales_analytics', { 'period': period }, self.api_key) def add_game_keys(self, game_id, keys): return self.client.send_request('add_game_keys', { 'game_id': game_id, 'keys': keys }, self.api_key) def test_rabbitmq_connection(): """1. Тестирование подключения к RabbitMQ""" print("=" * 60) print("1. ТЕСТИРОВАНИЕ ПОДКЛЮЧЕНИЯ К RABBITMQ") print("=" * 60) client = GameStoreClient('client-api-key-56789') try: # Проверка здоровья системы health = client.health_check() print(f"✓ Health Check: {health['status']}") print(f" Database: {health['data']['database']}") print(f" Redis: {health['data']['redis']}") print(f" Timestamp: {health['data']['timestamp']}") # Проверка получения списка игр games = client.get_games(page=1, per_page=3) print(f"✓ Получено игр: {games['data']['total']}") if games['data']['games']: for game in games['data']['games'][:3]: print(f" - {game['title']} (${game['price']})") return True except Exception as e: print(f"✗ Ошибка подключения к RabbitMQ: {e}") return False finally: client.client.close() def test_authentication_flow(): """2. Полный цикл аутентификации""" print("\n" + "=" * 60) print("2. ПОЛНЫЙ ЦИКЛ АУТЕНТИФИКАЦИИ") print("=" * 60) # Используем админский ключ для создания пользователя admin_client = GameStoreClient('admin-api-key-101112') client = GameStoreClient('client-api-key-56789') try: # Создание нового пользователя с уникальным именем test_username = f"test_user_{int(time.time())}" test_email = f"{test_username}@example.com" print("✓ Регистрация нового пользователя...") registration = admin_client.create_user(test_username, test_email, 'securepassword123') if registration['status'] == 'ok': user_id = registration['data']['user_id'] print(f" Пользователь создан: {test_username} (ID: {user_id})") else: print(f" Ошибка регистрации: {registration['error']}") return False # Логин под новым пользователем print("✓ Вход в систему...") login = client.login(test_username, 'securepassword123') if login['status'] == 'ok': user_data = login['data']['user'] token = login['data']['access_token'][:20] + "..." print(f" Успешный вход! Пользователь: {user_data['username']}") print(f" Токен: {token}") print(f" Баланс: ${user_data['balance']}") else: print(f" Ошибка входа: {login['error']}") return False # Получение профиля с указанием user_id print("✓ Получение профиля...") profile = admin_client.get_profile(user_id=user_id) if profile['status'] == 'ok': print(f" Профиль получен: {profile['data']['username']}") print(f" Email: {profile['data']['email']}") print(f" Роль: {profile['data']['role']}") else: print(f" Ошибка получения профиля: {profile['error']}") return False # Пополнение баланса с указанием user_id print("✓ Пополнение баланса...") deposit = admin_client.deposit_balance(500.0, user_id=user_id) if deposit['status'] == 'ok': print(f" Баланс пополнен: ${deposit['data']['new_balance']}") else: print(f" Ошибка пополнения: {deposit['error']}") return False return True except Exception as e: print(f"✗ Ошибка в цикле аутентификации: {e}") return False finally: admin_client.client.close() client.client.close() def test_idempotency(): """3. Тестирование идемпотентности""" print("\n" + "=" * 60) print("3. ТЕСТИРОВАНИЕ ИДЕМПОТЕНТНОСТИ") print("=" * 60) admin_client = GameStoreClient('admin-api-key-101112') try: # Создаем тестового пользователя для этого теста test_username = f"idempotency_user_{int(time.time())}" test_email = f"{test_username}@example.com" registration = admin_client.create_user(test_username, test_email, 'password123') if registration['status'] != 'ok': print("✗ Не удалось создать пользователя для теста") return False user_id = registration['data']['user_id'] print(f"✓ Создан пользователь для теста: ID {user_id}") # Получаем начальный баланс profile = admin_client.get_profile(user_id=user_id) if profile['status'] != 'ok': print("✗ Не удалось получить профиль") return False initial_balance = profile['data']['balance'] print(f"✓ Начальный баланс: ${initial_balance}") # Создаем специального клиента с фиксированным message_id для теста идемпотентности class IdempotencyTestClient: def __init__(self, base_client): self.base_client = base_client self.fixed_message_id = str(uuid.uuid4()) def send_deposit_request(self, amount, user_id): # Используем тот же самый message_id для демонстрации идемпотентности if not self.base_client.client.connection or self.base_client.client.connection.is_closed: self.base_client.client.connect() response_queue = Queue() self.base_client.client.pending_requests[self.fixed_message_id] = response_queue message = { 'id': self.fixed_message_id, # Фиксированный ID для теста 'version': 'v1', 'action': 'deposit_balance', 'data': {'amount': amount, 'user_id': user_id}, 'auth': self.base_client.api_key } try: self.base_client.client.channel.basic_publish( exchange='', routing_key=Config.REQUEST_QUEUE, properties=pika.BasicProperties( reply_to=self.base_client.client.callback_queue, correlation_id=self.fixed_message_id, delivery_mode=2 ), body=json.dumps(message) ) response = response_queue.get(timeout=30) return response finally: self.base_client.client.pending_requests.pop(self.fixed_message_id, None) test_client = IdempotencyTestClient(admin_client) # Первый запрос на пополнение print("✓ Первый запрос на пополнение $100...") deposit1 = test_client.send_deposit_request(100.0, user_id) if deposit1['status'] == 'ok': balance_after_first = deposit1['data']['new_balance'] print(f" Баланс после первого запроса: ${balance_after_first}") else: print(f" Ошибка первого запроса: {deposit1['error']}") return False # Второй запрос с ТЕМ ЖЕ message_id print("✓ Второй запрос с тем же ID сообщения...") deposit2 = test_client.send_deposit_request(100.0, user_id) if deposit2['status'] == 'ok': balance_after_second = deposit2['data']['new_balance'] print(f" Баланс после второго запроса: ${balance_after_second}") # Проверяем, что баланс не изменился (идемпотентность работает) if balance_after_first == balance_after_second: print(" ✓ ИДЕМПОТЕНТНОСТЬ РАБОТАЕТ: Баланс не изменился при повторном запросе!") else: print(" ✗ ИДЕМПОТЕНТНОСТЬ НЕ РАБОТАЕТ: Баланс изменился!") return False else: print(f" Ошибка второго запроса: {deposit2['error']}") return False # Нормальный запрос с новым ID print("✓ Нормальный запрос с новым ID...") deposit3 = admin_client.deposit_balance(50.0, user_id=user_id) if deposit3['status'] == 'ok': final_balance = deposit3['data']['new_balance'] print(f" Финальный баланс: ${final_balance}") # Проверяем, что баланс увеличился на 50 expected_balance = balance_after_first + 50.0 if abs(final_balance - expected_balance) < 0.01: print(" ✓ Нормальные запросы работают корректно!") else: print(f" ✗ Ожидался баланс ${expected_balance}, получен ${final_balance}") return False else: print(f" Ошибка нормального запроса: {deposit3['error']}") return False return True except Exception as e: print(f"✗ Ошибка тестирования идемпотентности: {e}") return False finally: admin_client.client.close() def test_error_handling_and_reliability(): """4. Тестирование обработки ошибок и надежности""" print("\n" + "=" * 60) print("4. ТЕСТИРОВАНИЕ ОБРАБОТКИ ОШИБОК И НАДЕЖНОСТИ") print("=" * 60) admin_client = GameStoreClient('admin-api-key-101112') client = GameStoreClient('client-api-key-56789') try: # Тест 1: Неверные учетные данные print("✓ Тест 1: Неверные учетные данные...") bad_login = client.login('nonexistent_user', 'wrongpassword') if bad_login['status'] == 'error': print(f" ✓ Корректная обработка: {bad_login['error']}") else: print(" ✗ Ожидалась ошибка при неверных учетных данных") return False # Тест 2: Неверный API-ключ print("✓ Тест 2: Неверный API-ключ...") bad_client = GameStoreClient('invalid-api-key') bad_request = bad_client.health_check() if bad_request['status'] == 'error' and 'Authentication failed' in bad_request['error']: print(f" ✓ Корректная обработка: {bad_request['error']}") else: print(" ✗ Ожидалась ошибка аутентификации") return False bad_client.client.close() # Тест 3: Создание заказа без денег print("✓ Тест 3: Создание заказа без достаточного баланса...") # Создаем пользователя с нулевым балансом poor_user = f"poor_user_{int(time.time())}" registration = admin_client.create_user(poor_user, f"{poor_user}@example.com", 'password123') if registration['status'] != 'ok': print(" ✗ Не удалось создать пользователя") return False poor_user_id = registration['data']['user_id'] # Получаем список игр games = client.get_games(page=1, per_page=1) if games['status'] != 'ok' or not games['data']['games']: print(" ✗ Нет доступных игр для теста") return False game_id = games['data']['games'][0]['id'] # Пытаемся купить игру без денег order_result = admin_client.create_order([{'game_id': game_id, 'quantity': 1}], user_id=poor_user_id) if order_result['status'] == 'error' and 'Insufficient balance' in order_result['error']: print(f" ✓ Корректная обработка: {order_result['error']}") else: print(" ✗ Ожидалась ошибка недостаточного баланса") return False # Тест 4: Несуществующая игра print("✓ Тест 4: Запрос несуществующей игры...") nonexistent_game = client.get_game(99999) if nonexistent_game['status'] == 'error': print(f" ✓ Корректная обработка: {nonexistent_game['error']}") else: print(" ✗ Ожидалась ошибка для несуществующей игры") return False # Тест 5: Некорректные данные при создании пользователя print("✓ Тест 5: Некорректные данные при создании пользователя...") # Используем уникальное имя, но пустой email и пароль invalid_username = f"invalid_user_{int(time.time())}" invalid_user = admin_client.create_user(invalid_username, '', '') if invalid_user['status'] == 'error': print(f" ✓ Корректная обработка некорректных данных: {invalid_user['error']}") else: print(" ✗ Ожидалась ошибка валидации") return False # Тест 6: Аналитика продаж (должна работать) print("✓ Тест 6: Проверка работы аналитики...") analytics = admin_client.sales_analytics('week') if analytics['status'] == 'ok': print(f" ✓ Аналитика работает: {analytics['data']['total_orders']} заказов") print(f" Общая выручка: ${analytics['data']['total_revenue']}") else: print(f" ✗ Ошибка аналитики: {analytics['error']}") return False return True except Exception as e: print(f"✗ Ошибка тестирования обработки ошибок: {e}") return False finally: admin_client.client.close() client.client.close() def test_complete_workflow(): """5. Полный рабочий процесс""" print("\n" + "=" * 60) print("5. ПОЛНЫЙ РАБОЧИЙ ПРОЦЕСС") print("=" * 60) admin_client = GameStoreClient('admin-api-key-101112') client = GameStoreClient('client-api-key-56789') try: # Создаем тестового пользователя workflow_user = f"workflow_user_{int(time.time())}" registration = admin_client.create_user(workflow_user, f"{workflow_user}@example.com", 'password123') if registration['status'] != 'ok': print("✗ Не удалось создать пользователя") return False user_id = registration['data']['user_id'] print("✓ Пользователь создан") # Логинимся под новым пользователем login = client.login(workflow_user, 'password123') if login['status'] != 'ok': print("✗ Не удалось войти в систему") return False print("✓ Пользователь авторизован") # Пополняем баланс через админа deposit = admin_client.deposit_balance(5000.0, user_id=user_id) # Увеличили баланс if deposit['status'] != 'ok': print("✗ Не удалось пополнить баланс") return False print("✓ Баланс пополнен до $5000") # Получаем список игр games = client.get_games(page=1, per_page=2) if games['status'] != 'ok' or len(games['data']['games']) < 2: print("✗ Недостаточно игр для теста") return False game1 = games['data']['games'][0] game2 = games['data']['games'][1] # Проверяем, что хватает денег total_price = game1['price'] + game2['price'] if total_price > 5000: # Если не хватает, берем только одну игру game2 = None total_price = game1['price'] print(f"✓ Найдена игра: {game1['title']} (${game1['price']})") else: print(f"✓ Найдены игры: {game1['title']} (${game1['price']}) и {game2['title']} (${game2['price']})") # Создаем заказ if game2: order = admin_client.create_order([ {'game_id': game1['id'], 'quantity': 1}, {'game_id': game2['id'], 'quantity': 1} ], user_id=user_id) else: order = admin_client.create_order([ {'game_id': game1['id'], 'quantity': 1} ], user_id=user_id) if order['status'] == 'ok': print(f"✓ Заказ создан: ID {order['data']['order_id']}") print(f" Сумма: ${order['data']['total_amount']}") print(f" Статус: {order['data']['status']}") else: print(f"✗ Ошибка создания заказа: {order['error']}") return False # Получаем историю заказов orders = admin_client.get_orders(user_id=user_id) if orders['status'] == 'ok': print(f"✓ История заказов: {orders['data']['total']} заказ(ов)") else: print(f"✗ Ошибка получения заказов: {orders['error']}") return False # Создаем отзыв review = admin_client.create_review(game1['id'], 5, "Отличная игра! Очень понравилась.", user_id=user_id) if review['status'] == 'ok': print(f"✓ Отзыв создан: ID {review['data']['review_id']}") else: print(f" Примечание: не удалось создать отзыв: {review['error']}") # Получаем обновленный профиль profile = admin_client.get_profile(user_id=user_id) if profile['status'] == 'ok': print(f"✓ Финальный баланс: ${profile['data']['balance']}") else: print(f"✗ Ошибка получения профиля: {profile['error']}") return False return True except Exception as e: print(f"✗ Ошибка в полном рабочем процессе: {e}") return False finally: admin_client.client.close() client.client.close() def main(): """Главная функция тестирования""" print("🚀 ЗАПУСК ТЕСТИРОВАНИЯ API НА ОСНОВЕ RABBITMQ") print("=" * 60) tests = [ ("Подключение к RabbitMQ", test_rabbitmq_connection), ("Цикл аутентификации", test_authentication_flow), ("Идемпотентность", test_idempotency), ("Обработка ошибок и надежность", test_error_handling_and_reliability), ("Полный рабочий процесс", test_complete_workflow) ] results = [] for test_name, test_func in tests: try: success = test_func() results.append((test_name, success)) if success: print(f"✅ {test_name} - ПРОЙДЕН\n") else: print(f"❌ {test_name} - НЕ ПРОЙДЕН\n") except Exception as e: print(f"❌ {test_name} - ОШИБКА: {e}\n") results.append((test_name, False)) # Итоговый отчет print("=" * 60) print("📊 ИТОГОВЫЙ ОТЧЕТ") print("=" * 60) passed = sum(1 for _, success in results if success) total = len(results) for test_name, success in results: status = "✅ ПРОЙДЕН" if success else "❌ НЕ ПРОЙДЕН" print(f"{test_name}: {status}") print(f"\nИтог: {passed}/{total} тестов пройдено") if passed == total: print("🎉 ВСЕ ТЕСТЫ УСПЕШНО ПРОЙДЕНЫ!") print("Система демонстрирует:") print(" ✓ Надежное подключение к RabbitMQ") print(" ✓ Полный цикл аутентификации") print(" ✓ Корректную работу идемпотентности") print(" ✓ Надежную обработку ошибок") print(" ✓ Полнофункциональный рабочий процесс") else: print(f"⚠️ Пройдено только {passed} из {total} тестов") return passed == total if __name__ == '__main__': success = main() exit(0 if success else 1)