From 1c81fa9b3c1a7b57185ff1047dcd523bd70d1ff4 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 29 Mar 2026 08:01:07 +0000 Subject: [PATCH] test: add extended tests to improve coverage to 93% --- tests/unit/test_client_extended.py | 743 +++++++++++++++++++++++++++++ 1 file changed, 743 insertions(+) create mode 100644 tests/unit/test_client_extended.py diff --git a/tests/unit/test_client_extended.py b/tests/unit/test_client_extended.py new file mode 100644 index 0000000..1d3d489 --- /dev/null +++ b/tests/unit/test_client_extended.py @@ -0,0 +1,743 @@ +""" +Extended unit tests for KworkClient to improve coverage. + +These tests cover additional API endpoints and edge cases. +""" + +import pytest +import respx +from httpx import Response + +from kwork_api import KworkClient, KworkAuthError, KworkApiError +from kwork_api.models import ( + NotificationsResponse, + Kwork, + Dialog, + City, + Country, + TimeZone, + Feature, + Badge, + Project, +) + +BASE_URL = "https://api.kwork.ru" + + +class TestClientProperties: + """Test client properties and initialization.""" + + def test_token_property(self): + """Test token property getter.""" + client = KworkClient(token="test_token_123") + assert client.token == "test_token_123" + + def test_token_property_none(self): + """Test token property when no token.""" + client = KworkClient() + assert client.token is None + + def test_cookies_property_empty(self): + """Test cookies property with no cookies.""" + client = KworkClient(token="test") + assert client.cookies == {} + + def test_cookies_property_with_cookies(self): + """Test cookies property returns copy.""" + client = KworkClient(token="test", cookies={"userId": "123", "session": "abc"}) + cookies = client.cookies + assert cookies == {"userId": "123", "session": "abc"} + cookies["modified"] = "value" + assert "modified" not in client.cookies + + def test_credentials_property(self): + """Test credentials property returns token and cookies.""" + client = KworkClient(token="test_token", cookies={"userId": "123"}) + creds = client.credentials + assert creds["token"] == "test_token" + assert creds["cookies"] == {"userId": "123"} + + def test_credentials_property_no_cookies(self): + """Test credentials with no cookies.""" + client = KworkClient(token="test_token") + creds = client.credentials + assert creds["token"] == "test_token" + assert creds["cookies"] is None + + +class TestCatalogAPIExtended: + """Extended tests for CatalogAPI.""" + + @respx.mock + async def test_get_details_extra(self): + """Test get_details_extra endpoint.""" + client = KworkClient(token="test") + + mock_data = { + "id": 456, + "title": "Extra Details Kwork", + "extra_field": "extra_value", + } + + respx.post(f"{BASE_URL}/getKworkDetailsExtra").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.catalog.get_details_extra(456) + + assert result["id"] == 456 + assert result["extra_field"] == "extra_value" + + +class TestProjectsAPIExtended: + """Extended tests for ProjectsAPI.""" + + @respx.mock + async def test_get_payer_orders(self): + """Test getting payer orders.""" + client = KworkClient(token="test") + + mock_data = { + "orders": [ + {"id": 101, "title": "Order 1", "amount": 5000.0, "status": "active"}, + ] + } + + respx.post(f"{BASE_URL}/payerOrders").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.projects.get_payer_orders() + + assert isinstance(result, list) + assert len(result) == 1 + assert isinstance(result[0], Project) + + @respx.mock + async def test_get_worker_orders(self): + """Test getting worker orders.""" + client = KworkClient(token="test") + + mock_data = { + "orders": [ + {"id": 202, "title": "Worker Order", "amount": 3000.0, "status": "completed"}, + ] + } + + respx.post(f"{BASE_URL}/workerOrders").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.projects.get_worker_orders() + + assert isinstance(result, list) + assert len(result) == 1 + + +class TestUserAPI: + """Tests for UserAPI.""" + + @respx.mock + async def test_get_info(self): + """Test getting user info.""" + client = KworkClient(token="test") + + mock_data = { + "userId": 12345, + "username": "testuser", + "email": "test@example.com", + "balance": 50000.0, + } + + respx.post(f"{BASE_URL}/user").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.user.get_info() + + assert result["userId"] == 12345 + assert result["username"] == "testuser" + + @respx.mock + async def test_get_reviews(self): + """Test getting user reviews.""" + client = KworkClient(token="test") + + mock_data = { + "reviews": [ + {"id": 1, "rating": 5, "comment": "Great work!", "author": {"id": 999, "username": "client1"}}, + ], + "pagination": {"current_page": 1, "total_pages": 5}, + } + + respx.post(f"{BASE_URL}/userReviews").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.user.get_reviews(user_id=12345, page=1) + + assert len(result.reviews) == 1 + assert result.reviews[0].rating == 5 + + @respx.mock + async def test_get_favorite_kworks(self): + """Test getting favorite kworks.""" + client = KworkClient(token="test") + + mock_data = { + "kworks": [ + {"id": 100, "title": "Favorite Kwork 1", "price": 2000.0}, + {"id": 101, "title": "Favorite Kwork 2", "price": 3000.0}, + ] + } + + respx.post(f"{BASE_URL}/favoriteKworks").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.user.get_favorite_kworks() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], Kwork) + + +class TestReferenceAPI: + """Tests for ReferenceAPI.""" + + @respx.mock + async def test_get_cities(self): + """Test getting cities list.""" + client = KworkClient(token="test") + + mock_data = { + "cities": [ + {"id": 1, "name": "Москва", "country_id": 1}, + {"id": 2, "name": "Санкт-Петербург", "country_id": 1}, + ] + } + + respx.post(f"{BASE_URL}/cities").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.reference.get_cities() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], City) + + @respx.mock + async def test_get_countries(self): + """Test getting countries list.""" + client = KworkClient(token="test") + + mock_data = { + "countries": [ + {"id": 1, "name": "Россия", "code": "RU"}, + {"id": 2, "name": "Беларусь", "code": "BY"}, + ] + } + + respx.post(f"{BASE_URL}/countries").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.reference.get_countries() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], Country) + + @respx.mock + async def test_get_timezones(self): + """Test getting timezones list.""" + client = KworkClient(token="test") + + mock_data = { + "timezones": [ + {"id": 1, "name": "Europe/Moscow", "offset": "+03:00"}, + {"id": 2, "name": "Europe/Kaliningrad", "offset": "+02:00"}, + ] + } + + respx.post(f"{BASE_URL}/timezones").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.reference.get_timezones() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], TimeZone) + + @respx.mock + async def test_get_features(self): + """Test getting features list.""" + client = KworkClient(token="test") + + mock_data = { + "features": [ + {"id": 1, "name": "Feature 1", "category_id": 5, "price": 1000, "type": "extra"}, + {"id": 2, "name": "Feature 2", "category_id": 5, "price": 2000, "type": "extra"}, + ] + } + + respx.post(f"{BASE_URL}/getAvailableFeatures").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.reference.get_features() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], Feature) + + @respx.mock + async def test_get_public_features(self): + """Test getting public features list.""" + client = KworkClient(token="test") + + mock_data = { + "features": [ + {"id": 10, "name": "Public Feature", "is_public": True, "price": 500, "type": "extra"}, + ] + } + + respx.post(f"{BASE_URL}/getPublicFeatures").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.reference.get_public_features() + + assert isinstance(result, list) + assert len(result) == 1 + + @respx.mock + async def test_get_badges_info(self): + """Test getting badges info.""" + client = KworkClient(token="test") + + mock_data = { + "badges": [ + {"id": 1, "name": "Pro Seller", "icon_url": "https://example.com/badge1.png"}, + {"id": 2, "name": "Verified", "icon_url": "https://example.com/badge2.png"}, + ] + } + + respx.post(f"{BASE_URL}/getBadgesInfo").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.reference.get_badges_info() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], Badge) + + +class TestNotificationsAPI: + """Tests for NotificationsAPI.""" + + @respx.mock + async def test_get_list(self): + """Test getting notifications list.""" + client = KworkClient(token="test") + + mock_data = { + "notifications": [ + {"id": 1, "type": "order", "title": "New Order", "message": "New order received", "is_read": False}, + {"id": 2, "type": "message", "title": "New Message", "message": "You have a new message", "is_read": True}, + ], + "unread_count": 5, + } + + respx.post(f"{BASE_URL}/notifications").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.notifications.get_list() + + assert isinstance(result, NotificationsResponse) + assert result.unread_count == 5 + + @respx.mock + async def test_fetch(self): + """Test fetching notifications.""" + client = KworkClient(token="test") + + mock_data = { + "notifications": [ + {"id": 3, "type": "system", "title": "System Update", "message": "System update available", "is_read": False}, + ], + "unread_count": 1, + } + + respx.post(f"{BASE_URL}/notificationsFetch").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.notifications.fetch() + + assert isinstance(result, NotificationsResponse) + assert len(result.notifications) == 1 + + @respx.mock + async def test_get_dialogs(self): + """Test getting dialogs.""" + client = KworkClient(token="test") + + mock_data = { + "dialogs": [ + {"id": 1, "user_id": 100, "last_message": "Hello", "unread": 2}, + {"id": 2, "user_id": 200, "last_message": "Hi", "unread": 0}, + ] + } + + respx.post(f"{BASE_URL}/dialogs").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.notifications.get_dialogs() + + assert isinstance(result, list) + assert len(result) == 2 + assert isinstance(result[0], Dialog) + + @respx.mock + async def test_get_blocked_dialogs(self): + """Test getting blocked dialogs.""" + client = KworkClient(token="test") + + mock_data = { + "dialogs": [ + {"id": 99, "user_id": 999, "last_message": "Spam", "blocked": True}, + ] + } + + respx.post(f"{BASE_URL}/blockedDialogList").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.notifications.get_blocked_dialogs() + + assert isinstance(result, list) + assert len(result) == 1 + + +class TestOtherAPI: + """Tests for OtherAPI.""" + + @respx.mock + async def test_get_wants(self): + """Test getting wants.""" + client = KworkClient(token="test") + + mock_data = { + "wants": [{"id": 1, "title": "I need a logo"}], + "count": 1, + } + + respx.post(f"{BASE_URL}/myWants").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_wants() + + assert "wants" in result + assert result["count"] == 1 + + @respx.mock + async def test_get_wants_status(self): + """Test getting wants status.""" + client = KworkClient(token="test") + + mock_data = { + "active_wants": 5, + "completed_wants": 10, + } + + respx.post(f"{BASE_URL}/wantsStatusList").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_wants_status() + + assert result["active_wants"] == 5 + + @respx.mock + async def test_get_kworks_status(self): + """Test getting kworks status.""" + client = KworkClient(token="test") + + mock_data = { + "active_kworks": 3, + "total_sales": 50, + } + + respx.post(f"{BASE_URL}/kworksStatusList").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_kworks_status() + + assert result["active_kworks"] == 3 + + @respx.mock + async def test_get_offers(self): + """Test getting offers.""" + client = KworkClient(token="test") + + mock_data = { + "offers": [{"id": 1, "title": "Special offer"}], + } + + respx.post(f"{BASE_URL}/offers").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_offers() + + assert "offers" in result + + @respx.mock + async def test_get_exchange_info(self): + """Test getting exchange info.""" + client = KworkClient(token="test") + + mock_data = { + "usd_rate": 90.5, + "eur_rate": 98.2, + } + + respx.post(f"{BASE_URL}/exchangeInfo").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_exchange_info() + + assert result["usd_rate"] == 90.5 + + @respx.mock + async def test_get_channel(self): + """Test getting channel info.""" + client = KworkClient(token="test") + + mock_data = { + "channel_id": "main", + "name": "Main Channel", + } + + respx.post(f"{BASE_URL}/getChannel").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_channel() + + assert result["channel_id"] == "main" + + @respx.mock + async def test_get_in_app_notification(self): + """Test getting in-app notifications.""" + client = KworkClient(token="test") + + mock_data = { + "notifications": [{"id": 1, "message": "App update"}], + } + + respx.post(f"{BASE_URL}/getInAppNotification").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_in_app_notification() + + assert "notifications" in result + + @respx.mock + async def test_get_security_user_data(self): + """Test getting security user data.""" + client = KworkClient(token="test") + + mock_data = { + "two_factor_enabled": True, + "last_login": "2024-01-01T00:00:00Z", + } + + respx.post(f"{BASE_URL}/getSecurityUserData").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_security_user_data() + + assert result["two_factor_enabled"] is True + + @respx.mock + async def test_is_dialog_allow_true(self): + """Test is_dialog_allow returns True.""" + client = KworkClient(token="test") + + respx.post(f"{BASE_URL}/isDialogAllow").mock( + return_value=Response(200, json={"allowed": True}) + ) + + result = await client.other.is_dialog_allow(12345) + + assert result is True + + @respx.mock + async def test_is_dialog_allow_false(self): + """Test is_dialog_allow returns False.""" + client = KworkClient(token="test") + + respx.post(f"{BASE_URL}/isDialogAllow").mock( + return_value=Response(200, json={"allowed": False}) + ) + + result = await client.other.is_dialog_allow(67890) + + assert result is False + + @respx.mock + async def test_get_viewed_kworks(self): + """Test getting viewed kworks.""" + client = KworkClient(token="test") + + mock_data = { + "kworks": [ + {"id": 500, "title": "Viewed Kwork", "price": 1500.0}, + ] + } + + respx.post(f"{BASE_URL}/viewedCatalogKworks").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_viewed_kworks() + + assert isinstance(result, list) + assert len(result) == 1 + assert isinstance(result[0], Kwork) + + @respx.mock + async def test_get_favorite_categories(self): + """Test getting favorite categories.""" + client = KworkClient(token="test") + + mock_data = { + "categories": [1, 5, 10, 15], + } + + respx.post(f"{BASE_URL}/favoriteCategories").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_favorite_categories() + + assert isinstance(result, list) + assert 1 in result + assert 5 in result + + @respx.mock + async def test_update_settings(self): + """Test updating settings.""" + client = KworkClient(token="test") + + mock_data = { + "success": True, + "updated": {"notifications_enabled": False}, + } + + respx.post(f"{BASE_URL}/updateSettings").mock( + return_value=Response(200, json=mock_data) + ) + + settings = {"notifications_enabled": False, "theme": "dark"} + result = await client.other.update_settings(settings) + + assert result["success"] is True + + @respx.mock + async def test_go_offline(self): + """Test going offline.""" + client = KworkClient(token="test") + + mock_data = { + "success": True, + "status": "offline", + } + + respx.post(f"{BASE_URL}/offline").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.go_offline() + + assert result["success"] is True + assert result["status"] == "offline" + + @respx.mock + async def test_get_actor(self): + """Test getting actor info.""" + client = KworkClient(token="test") + + mock_data = { + "actor_id": 123, + "name": "Test Actor", + } + + respx.post(f"{BASE_URL}/actor").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client.other.get_actor() + + assert result["actor_id"] == 123 + + +class TestClientInternals: + """Tests for internal client methods.""" + + def test_handle_response_success(self): + """Test _handle_response with successful response.""" + import httpx + client = KworkClient(token="test") + + response = httpx.Response(200, json={"success": True, "data": "test"}) + result = client._handle_response(response) + + assert result["success"] is True + assert result["data"] == "test" + + def test_handle_response_error(self): + """Test _handle_response with error response.""" + import httpx + client = KworkClient(token="test") + + response = httpx.Response(400, json={"message": "Bad request"}) + + with pytest.raises(KworkApiError) as exc_info: + client._handle_response(response) + + assert exc_info.value.status_code == 400 + + @respx.mock + async def test_request_method(self): + """Test _request method directly.""" + client = KworkClient(token="test") + + mock_data = {"result": "success"} + + respx.post(f"{BASE_URL}/test-endpoint").mock( + return_value=Response(200, json=mock_data) + ) + + result = await client._request("POST", "/test-endpoint", json={"param": "value"}) + + assert result["result"] == "success" + + async def test_context_manager_creates_client(self): + """Test that context manager creates httpx client.""" + async with KworkClient(token="test") as client: + assert client.token == "test" + + assert client._client is None or client._client.is_closed