fix: update tests for new login endpoint

This commit is contained in:
root 2026-03-29 21:49:02 +00:00
parent 7c392a822b
commit e478807aa4
8 changed files with 148 additions and 61 deletions

View File

@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}

97
Untitled.ipynb Normal file
View File

@ -0,0 +1,97 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "f28552f1-618c-4853-92e2-566554a2de2c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import asyncio\n",
"from kwork_api import KworkClient\n",
"from dotenv import load_dotenv\n",
"import os\n",
"\n",
"load_dotenv('tests/e2e/.env')"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "953d142e-a575-41b7-927d-8cd1546d2747",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"KworkAuthError: Login failed: 404\n"
]
}
],
"source": [
"try:\n",
" client = await KworkClient.login(\n",
" username=os.getenv('KWORK_USERNAME'),\n",
" password=os.getenv('KWORK_PASSWORD')\n",
" )\n",
" print(f\"✅ Logged in as: {client.token[:20]}...\")\n",
"except Exception as e:\n",
" print(e)\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "655aa71e-5645-4c7a-aadd-5b044a0713c9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'8AQhyzQRcTJ6v81maCNa'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"os.getenv('KWORK_PASSWORD')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -81,8 +81,6 @@ class KworkClient:
""" """
BASE_URL = "https://api.kwork.ru" BASE_URL = "https://api.kwork.ru"
LOGIN_URL = "https://kwork.ru/signIn"
TOKEN_URL = "https://kwork.ru/getWebAuthToken"
def __init__( def __init__(
self, self,
@ -241,38 +239,41 @@ class KworkClient:
try: try:
async with client._get_httpx_client() as http_client: async with client._get_httpx_client() as http_client:
# Step 1: Login to get session cookies # Step 1: Login to get session cookies and token
# Kwork uses /api/user/login with JSON body
login_data = { login_data = {
"login_or_email": username, "l_username": username,
"password": password, "l_password": password,
"jlog": 1,
"recaptcha_pass_token": "",
"track_client_id": False,
"smart-token": "",
"l_remember_me": "1",
} }
response = await http_client.post( response = await http_client.post(
cls.LOGIN_URL, "https://kwork.ru/api/user/login",
data=login_data, json=login_data,
headers={"Referer": "https://kwork.ru/"}, headers={
"Accept": "application/json, text/plain, */*",
"X-Requested-With": "XMLHttpRequest",
},
) )
if response.status_code != 200: if response.status_code != 200:
raise KworkAuthError(f"Login failed: {response.status_code}") raise KworkAuthError(f"Login failed: {response.status_code}")
# Extract cookies response_data = response.json()
# Extract userId from response or cookies
cookies = dict(response.cookies) cookies = dict(response.cookies)
user_id = response_data.get("user_id") or cookies.get("userId")
if "userId" not in cookies: if not user_id:
raise KworkAuthError("Login failed: no userId in cookies") raise KworkAuthError("Login failed: no userId in response")
# Step 2: Get web auth token # Extract web_auth_token from response
token_response = await http_client.post( web_token = response_data.get("web_auth_token") or cookies.get("web_auth_token")
cls.TOKEN_URL,
json={},
)
if token_response.status_code != 200:
raise KworkAuthError(f"Token request failed: {token_response.status_code}")
token_data = token_response.json()
web_token = token_data.get("web_auth_token")
if not web_token: if not web_token:
raise KworkAuthError("No web_auth_token in response") raise KworkAuthError("No web_auth_token in response")
@ -1174,9 +1175,7 @@ class KworkClient:
""" """
return await self.client._request("POST", "/actor") return await self.client._request("POST", "/actor")
async def validate_text( async def validate_text(self, text: str, context: str | None = None) -> ValidationResponse:
self, text: str, context: str | None = None
) -> ValidationResponse:
""" """
Проверить текст на соответствие требованиям Kwork. Проверить текст на соответствие требованиям Kwork.

View File

@ -8,7 +8,7 @@ import pytest
import respx import respx
from httpx import Response from httpx import Response
from kwork_api import KworkApiError, KworkAuthError, KworkClient from kwork_api import KworkApiError, KworkAuthError, KworkClient, KworkNetworkError
from kwork_api.models import CatalogResponse, ValidationResponse from kwork_api.models import CatalogResponse, ValidationResponse
@ -18,22 +18,11 @@ class TestAuthentication:
@respx.mock @respx.mock
async def test_login_success(self): async def test_login_success(self):
"""Test successful login.""" """Test successful login."""
import httpx
# Mock login endpoint # Mock login endpoint
login_route = respx.post("https://kwork.ru/signIn") login_route = respx.post("https://kwork.ru/api/user/login").mock(
login_route.mock(
return_value=httpx.Response(
200,
headers={"Set-Cookie": "userId=12345; slrememberme=token123"},
)
)
# Mock token endpoint
token_route = respx.post("https://kwork.ru/getWebAuthToken").mock(
return_value=Response( return_value=Response(
200, 200,
json={"web_auth_token": "test_token_abc123"}, json={"user_id": 12345, "web_auth_token": "test_token_abc123"},
) )
) )
@ -42,13 +31,12 @@ class TestAuthentication:
# Verify # Verify
assert login_route.called assert login_route.called
assert token_route.called
assert client._token == "test_token_abc123" assert client._token == "test_token_abc123"
@respx.mock @respx.mock
async def test_login_invalid_credentials(self): async def test_login_invalid_credentials(self):
"""Test login with invalid credentials.""" """Test login with invalid credentials."""
respx.post("https://kwork.ru/signIn").mock( respx.post("https://kwork.ru/api/user/login").mock(
return_value=Response(401, json={"error": "Invalid credentials"}) return_value=Response(401, json={"error": "Invalid credentials"})
) )
@ -57,11 +45,9 @@ class TestAuthentication:
@respx.mock @respx.mock
async def test_login_no_userid(self): async def test_login_no_userid(self):
"""Test login without userId in cookies.""" """Test login without userId in response."""
import httpx respx.post("https://kwork.ru/api/user/login").mock(
return_value=Response(200, json={"error": "No user_id"})
respx.post("https://kwork.ru/signIn").mock(
return_value=httpx.Response(200, headers={"Set-Cookie": "other=value"})
) )
with pytest.raises(KworkAuthError, match="no userId"): with pytest.raises(KworkAuthError, match="no userId"):
@ -70,14 +56,8 @@ class TestAuthentication:
@respx.mock @respx.mock
async def test_login_no_token(self): async def test_login_no_token(self):
"""Test login without web_auth_token in response.""" """Test login without web_auth_token in response."""
import httpx respx.post("https://kwork.ru/api/user/login").mock(
return_value=Response(200, json={"user_id": 123})
respx.post("https://kwork.ru/signIn").mock(
return_value=httpx.Response(200, headers={"Set-Cookie": "userId=123"})
)
respx.post("https://kwork.ru/getWebAuthToken").mock(
return_value=Response(200, json={"other": "data"})
) )
with pytest.raises(KworkAuthError, match="No web_auth_token"): with pytest.raises(KworkAuthError, match="No web_auth_token"):
@ -177,7 +157,9 @@ class TestProjectsAPI:
"pagination": {"current_page": 1}, "pagination": {"current_page": 1},
} }
respx.post(f"{client.base_url}/projects").mock(return_value=Response(200, json=mock_data)) respx.post(f"{client.base_url}/projects").mock(
return_value=Response(200, json=mock_data)
)
result = await client.projects.get_list() result = await client.projects.get_list()
@ -193,7 +175,9 @@ class TestErrorHandling:
"""Test 404 error handling.""" """Test 404 error handling."""
client = KworkClient(token="test") client = KworkClient(token="test")
respx.post(f"{client.base_url}/getKworkDetails").mock(return_value=Response(404)) respx.post(f"{client.base_url}/getKworkDetails").mock(
return_value=Response(404)
)
with pytest.raises(KworkApiError) as exc_info: with pytest.raises(KworkApiError) as exc_info:
await client.catalog.get_details(999) await client.catalog.get_details(999)
@ -205,7 +189,9 @@ class TestErrorHandling:
"""Test 401 error handling.""" """Test 401 error handling."""
client = KworkClient(token="invalid") client = KworkClient(token="invalid")
respx.post(f"{client.base_url}/catalogMainv2").mock(return_value=Response(401)) respx.post(f"{client.base_url}/catalogMainv2").mock(
return_value=Response(401)
)
with pytest.raises(KworkAuthError): with pytest.raises(KworkAuthError):
await client.catalog.get_list() await client.catalog.get_list()
@ -213,10 +199,12 @@ class TestErrorHandling:
@respx.mock @respx.mock
async def test_network_error(self): async def test_network_error(self):
"""Test network error handling.""" """Test network error handling."""
import httpx
client = KworkClient(token="test") client = KworkClient(token="test")
respx.post(f"{client.base_url}/catalogMainv2").mock( respx.post(f"{client.base_url}/catalogMainv2").mock(
side_effect=Exception("Connection refused") side_effect=httpx.RequestError("Connection refused", request=None)
) )
with pytest.raises(KworkNetworkError): with pytest.raises(KworkNetworkError):
@ -231,9 +219,6 @@ class TestContextManager:
async with KworkClient(token="test") as client: async with KworkClient(token="test") as client:
assert client._client is None # Not created yet assert client._client is None # Not created yet
# Client should be created on first request
# (but we don't make actual requests in this test)
# Client should be closed after context # Client should be closed after context
assert client._client is None or client._client.is_closed assert client._client is None or client._client.is_closed