import unittest import requests import uuid BASE_URL = "http://localhost:8090" def get_error_message(response: requests.Response) -> str: """Извлекает сообщение об ошибке из ответа сервера или возвращает понятную заглушку.""" try: if response.headers.get('Content-Type', '').startswith('application/json'): data = response.json() return data.get("error", "No 'error' field in JSON response") else: return f"Non-JSON response: {response.text[:200]}" except Exception as e: return f"Failed to parse error: {e} | Raw: {response.text[:200]}" class TestGameShopAPI(unittest.TestCase): valid_token = None test_nickname = "autotest_user" test_password = "secure_password_123" test_email = "autotest@example.com" @classmethod def setUpClass(cls): """Регистрируем пользователя один раз перед всеми тестами.""" super().setUpClass() register_payload = { "nickname": cls.test_nickname, "password": cls.test_password, "email": cls.test_email } try: response = requests.post(f"{BASE_URL}/register/", json=register_payload, timeout=10) if response.status_code != 200: error_msg = get_error_message(response) raise RuntimeError(f"Registration failed ({response.status_code}): {error_msg}") data = response.json() cls.valid_token = data.get("success") if not cls.valid_token: raise ValueError("Response missing 'success' field with token") uuid.UUID(cls.valid_token) except Exception as e: raise unittest.SkipTest(f"Не удалось зарегистрировать пользователя: {e}") @classmethod def tearDownClass(cls): """Удаляем пользователя после всех тестов.""" if cls.valid_token: try: headers = {"Authorization": "Bearer " + cls.valid_token} response = requests.post(f"{BASE_URL}/unregister/", headers=headers, timeout=10) if response.status_code != 200: error_msg = get_error_message(response) print(f"⚠️ Внимание: unregister завершился с кодом {response.status_code}: {error_msg}") except Exception as e: print(f"⚠️ Ошибка при unregister: {e}") super().tearDownClass() def test_1_get_shop_products(self): """GET /shop/ — должен вернуть непустой список товаров""" response = requests.get(f"{BASE_URL}/shop/") if response.status_code != 200: error_msg = get_error_message(response) self.fail(f"GET /shop/ failed ({response.status_code}): {error_msg}") data = response.json() self.assertIn("success", data, f"Ответ не содержит 'success'. Получено: {data}") products = data["success"] self.assertIsInstance(products, list, f"'success' должен быть списком, получено: {type(products)}") self.assertGreater(len(products), 0, "Список товаров пуст") product = products[0] required = {"id", "name", "count", "reserved", "picture_url", "description", "type", "cost"} missing = required - set(product.keys()) self.assertFalse(missing, f"В товаре отсутствуют поля: {missing}. Товар: {product}") self.assertIsInstance(product["cost"], (int, float), f"'cost' должен быть числом, получено: {type(product['cost'])}") def test_2_registration_returns_valid_token(self): """Проверка, что токен — валидный UUID""" self.assertIsNotNone(self.valid_token, "Токен не был получен при регистрации") try: uuid.UUID(self.valid_token) except ValueError: self.fail(f"Токен не является валидным UUID: {self.valid_token}") def test_3_basket_success(self): """POST /basket/ с валидным токеном → возвращает '[]'""" headers = {"Authorization": "Bearer " + self.valid_token} response = requests.post(f"{BASE_URL}/basket/", headers=headers) if response.status_code != 200: error_msg = get_error_message(response) self.fail(f"POST /basket/ failed ({response.status_code}): {error_msg}") data = response.json() self.assertIn("success", data, f"Ответ не содержит 'success'. Получено: {data}") self.assertEqual(data["success"], [], f"Ожидалась пустая корзина ([]), получено: {data['success']}") def test_4_history_success(self): """POST /history/ с валидным токеном → возвращает '[]'""" headers = {"Authorization": "Bearer " + self.valid_token} response = requests.post(f"{BASE_URL}/history/", headers=headers) if response.status_code != 200: error_msg = get_error_message(response) self.fail(f"POST /history/ failed ({response.status_code}): {error_msg}") data = response.json() self.assertIn("success", data, f"Ответ не содержит 'success'. Получено: {data}") self.assertEqual(data["success"], [], f"Ожидалась пустая история ([]), получено: {data['success']}") def test_5_basket_invalid_token(self): """POST /basket/ с невалидным токеном → ошибка 500""" headers = {"Authorization": "Bearer " + "invalid-token-12345"} response = requests.post(f"{BASE_URL}/basket/", headers=headers) if response.status_code != 500: error_msg = get_error_message(response) self.fail(f"Ожидался статус 500 для невалидного токена, получен {response.status_code}. Ошибка: {error_msg}") data = response.json() self.assertIn("error", data, f"Ответ должен содержать 'error', получено: {data}") self.assertIsInstance(data["error"], str, f"'error' должен быть строкой, получено: {type(data['error'])}") self.assertGreater(len(data["error"]), 0, "Сообщение об ошибке пустое") def test_6_history_invalid_token(self): """POST /history/ с невалидным токеном → ошибка 500""" headers = {"Authorization": "Bearer " + "invalid-token-12345"} response = requests.post(f"{BASE_URL}/history/", headers=headers) if response.status_code != 500: error_msg = get_error_message(response) self.fail(f"Ожидался статус 500 для невалидного токена, получен {response.status_code}. Ошибка: {error_msg}") data = response.json() self.assertIn("error", data, f"Ответ должен содержать 'error', получено: {data}") def test_7_login_success(self): """POST /login/ — успешная аутентификация зарегистрированного пользователя""" login_payload = { "email": self.test_email, "password": self.test_password } response = requests.post(f"{BASE_URL}/login/", json=login_payload) if response.status_code != 200: error_msg = get_error_message(response) self.fail(f"POST /login/ failed ({response.status_code}): {error_msg}") data = response.json() self.assertIn("success", data, f"Ответ не содержит 'OK'. Получено: {data}") login_token = data["success"] try: uuid.UUID(login_token) except ValueError: self.fail(f"Токен от /login/ не является валидным UUID: {login_token}") # Проверка, что токен работает basket_resp = requests.post(f"{BASE_URL}/basket/", headers={"Authorization": "Bearer " + login_token}) if basket_resp.status_code != 200: error_msg = get_error_message(basket_resp) self.fail(f"Токен от /login/ не работает: {error_msg}") def test_8_user_success(self): """POST /user/ с валидным токеном → возвращает данные пользователя""" headers = {"Authorization": "Bearer " + self.valid_token} response = requests.post(f"{BASE_URL}/user/", headers=headers) if response.status_code != 200: error_msg = get_error_message(response) self.fail(f"POST /user/ failed ({response.status_code}): {error_msg}") data = response.json() self.assertIn("success", data, f"Ответ не содержит 'success'. Получено: {data}") user_data = data["success"][0] required_fields = {"id", "nickname", "email", "token", "money", "histories_id"} missing = required_fields - set(user_data.keys()) self.assertFalse(missing, f"В данных пользователя отсутствуют поля: {missing}") self.assertEqual(user_data["nickname"], self.test_nickname) self.assertEqual(user_data["email"], self.test_email) self.assertIsInstance(float(user_data["money"]), float) self.assertIsInstance(user_data["histories_id"], list) def test_9_logout_success(self): """POST /logout/ с валидным токеном — успешный выход""" headers = {"Authorization": "Bearer " + self.valid_token} response = requests.post(f"{BASE_URL}/logout/", headers=headers) if response.status_code != 200: error_msg = get_error_message(response) self.fail(f"POST /logout/ failed ({response.status_code}): {error_msg}") data = response.json() self.assertIn("success", data, f"Ответ не содержит 'OK'. Получено: {data}") self.assertEqual(data["success"], self.valid_token, "В ответе должен быть тот же токен") def test_10_all_scenarios_completed(self): """Финальная проверка: все тесты пройдены""" pass if __name__ == "__main__": unittest.main(verbosity=2)