Skip to content

Commit 52919ee

Browse files
committed
test: update tests to new package structure
1 parent 1824ee0 commit 52919ee

19 files changed

Lines changed: 817 additions & 433 deletions

tests/api/__init__.py

Whitespace-only changes.

tests/api/test_catalog.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from unittest.mock import AsyncMock, MagicMock
2+
3+
import pytest
4+
5+
from vinted.api.catalog import CatalogAPI
6+
from vinted.models.item import CatalogItem
7+
8+
9+
@pytest.fixture
10+
def mock_session():
11+
session = MagicMock()
12+
session.base_url = "https://www.vinted.com"
13+
session.configure_from_url = MagicMock()
14+
return session
15+
16+
17+
@pytest.mark.asyncio
18+
async def test_catalog_search_basic(mock_session):
19+
catalog = CatalogAPI(mock_session)
20+
21+
mock_response = MagicMock()
22+
mock_response.json.return_value = {
23+
"items": [
24+
{
25+
"id": 1,
26+
"title": "Test Item",
27+
"price": {"amount": 10, "currency_code": "EUR"},
28+
"photo": {
29+
"url": "http://example.com/photo.jpg",
30+
"high_resolution": {"timestamp": 1734796339},
31+
},
32+
"url": "http://example.com/item/1",
33+
}
34+
]
35+
}
36+
mock_session.request = AsyncMock(return_value=mock_response)
37+
38+
items = await catalog.search(url="https://www.vinted.com/catalog?search_text=test")
39+
40+
assert len(items) == 1
41+
assert isinstance(items[0], CatalogItem)
42+
assert items[0].id == 1
43+
44+
45+
@pytest.mark.asyncio
46+
async def test_catalog_search_raw_data(mock_session):
47+
catalog = CatalogAPI(mock_session)
48+
49+
mock_response = MagicMock()
50+
mock_response.json.return_value = {"items": [{"id": 1, "title": "Test"}]}
51+
mock_session.request = AsyncMock(return_value=mock_response)
52+
53+
items = await catalog.search(url="https://www.vinted.com/catalog", raw_data=True)
54+
55+
assert isinstance(items, list)
56+
assert isinstance(items[0], dict)
57+
58+
59+
@pytest.mark.asyncio
60+
async def test_catalog_search_with_order(mock_session):
61+
catalog = CatalogAPI(mock_session)
62+
63+
mock_response = MagicMock()
64+
mock_response.json.return_value = {"items": []}
65+
66+
mock_session.request = AsyncMock(return_value=mock_response)
67+
68+
await catalog.search(url="https://www.vinted.com/catalog", order="newest_first")
69+
70+
call_args = mock_session.request.call_args
71+
assert "order" in call_args.kwargs["params"]
72+
73+
74+
def test_extract_catalog_id_from_path():
75+
catalog = CatalogAPI(MagicMock())
76+
77+
catalog_id = catalog._extract_catalog_id("/catalog/123-women-clothes")
78+
assert catalog_id == 123
79+
80+
81+
def test_extract_catalog_id_no_catalog():
82+
catalog = CatalogAPI(MagicMock())
83+
84+
catalog_id = catalog._extract_catalog_id("/search")
85+
assert catalog_id is None
86+
87+
88+
def test_build_params():
89+
catalog = CatalogAPI(MagicMock())
90+
91+
params = catalog._build_params(
92+
url="https://www.vinted.com/catalog?search_text=nike&brand_ids[]=53", per_page=20, page=1
93+
)
94+
95+
assert params["search_text"] == "nike"
96+
assert params["brand_ids"] == "53"
97+
assert params["per_page"] == 20
98+
assert params["page"] == 1

tests/api/test_items.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from unittest.mock import AsyncMock, MagicMock
2+
3+
import pytest
4+
5+
from vinted.api.items import ItemsAPI
6+
from vinted.models.item import DetailedItem
7+
8+
9+
@pytest.fixture
10+
def mock_session():
11+
session = MagicMock()
12+
session.base_url = "https://www.vinted.com"
13+
session.configure_from_url = MagicMock()
14+
return session
15+
16+
17+
@pytest.mark.asyncio
18+
async def test_items_get_details(mock_session, sample_detailed_item_data):
19+
items_api = ItemsAPI(mock_session)
20+
21+
mock_response = MagicMock()
22+
mock_response.json.return_value = {"item": sample_detailed_item_data}
23+
24+
mock_session.request = AsyncMock(return_value=mock_response)
25+
26+
item = await items_api.get_details(url="https://www.vinted.com/items/123-test-item")
27+
28+
assert isinstance(item, DetailedItem)
29+
assert item.id == 456
30+
31+
32+
@pytest.mark.asyncio
33+
async def test_items_get_details_raw(mock_session, sample_detailed_item_data):
34+
items_api = ItemsAPI(mock_session)
35+
36+
mock_response = MagicMock()
37+
mock_response.json.return_value = {"item": sample_detailed_item_data}
38+
39+
mock_session.request = AsyncMock(return_value=mock_response)
40+
41+
item = await items_api.get_details(
42+
url="https://www.vinted.com/items/123-test-item", raw_data=True
43+
)
44+
45+
assert isinstance(item, dict)
46+
assert item["id"] == 456
47+
48+
49+
def test_extract_product_id():
50+
items_api = ItemsAPI(MagicMock())
51+
52+
product_id = items_api._extract_product_id("https://www.vinted.com/items/123-nike-shoes")
53+
54+
assert product_id == "123"
55+
56+
57+
def test_extract_product_id_complex():
58+
items_api = ItemsAPI(MagicMock())
59+
60+
product_id = items_api._extract_product_id(
61+
"https://www.vinted.fr/items/9876-adidas-sneakers-size-42"
62+
)
63+
64+
assert product_id == "9876"

tests/conftest.py

Lines changed: 57 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,74 @@
1-
from pathlib import Path
2-
from typing import Any, Callable, Generator
1+
from unittest.mock import AsyncMock, MagicMock
32

43
import pytest
5-
6-
from vinted_api_kit import VintedApi
7-
from vinted_api_kit.client.vinted_http_client import VintedHttpClient
8-
from vinted_api_kit.services.item_service import ItemService
9-
10-
11-
@pytest.fixture
12-
def vinted_http_client(tmp_path: Path) -> Generator[VintedHttpClient, Any, None]:
13-
client = VintedHttpClient(
14-
locale="fr",
15-
proxies={"http": "http://127.0.0.1:8888"},
16-
client_ip="123.123.123.123",
17-
cookies_dir=tmp_path,
18-
persist_cookies=False,
19-
)
20-
yield client
21-
import asyncio
22-
23-
asyncio.run(client.close())
24-
25-
26-
@pytest.fixture
27-
def item_service(vinted_http_client: VintedHttpClient):
28-
return ItemService(client=vinted_http_client)
29-
30-
31-
@pytest.fixture
32-
async def vinted_api():
33-
async with VintedApi() as api:
34-
yield api
4+
from curl_cffi.requests import Response
355

366

377
@pytest.fixture
38-
def sample_catalog_item_data() -> Callable[[int], dict[str, Any]]:
39-
def factory(ts: int = 1710000000) -> dict[str, Any]:
40-
return {
41-
"id": 77,
42-
"title": "Vintage Jeans",
43-
"brand_title": "Levis",
44-
"size_title": "32",
45-
"price": {"currency_code": "EUR", "amount": 45},
46-
"photo": {
47-
"url": "https://cdn.vinted.net/catalog.jpg",
48-
"high_resolution": {"timestamp": ts},
49-
},
50-
"photos": [{"high_resolution": {"timestamp": ts}}],
51-
"url": "https://www.vinted.it/items/77-vintage-jeans",
52-
}
53-
54-
return factory
8+
def sample_catalog_item_data():
9+
return {
10+
"id": 123,
11+
"title": "Nike Air Max",
12+
"brand_title": "Nike",
13+
"size_title": "42",
14+
"price": {"amount": 50.0, "currency_code": "EUR"},
15+
"photo": {
16+
"url": "https://example.com/photo.jpg",
17+
"high_resolution": {"timestamp": 1734796339},
18+
},
19+
"url": "https://vinted.com/items/123-nike-air-max",
20+
}
5521

5622

5723
@pytest.fixture
58-
def sample_detailed_item_data() -> dict[str, Any]:
24+
def sample_detailed_item_data():
5925
return {
60-
"id": 42,
61-
"title": "Test Product",
62-
"description": "Very rare Vinted item",
63-
"brand_dto": {"title": "Nike", "slug": "nike"},
64-
"price": {"currency_code": "EUR", "amount": 19.99},
65-
"total_item_price": {"amount": 22.49},
26+
"id": 456,
27+
"title": "Adidas Sneakers",
28+
"description": "Great condition",
29+
"brand_dto": {"title": "Adidas", "slug": "adidas"},
30+
"price": {"amount": 75.0, "currency_code": "USD"},
31+
"total_item_price": {"amount": 80.0, "currency_code": "USD"},
6632
"photos": [
67-
{
68-
"url": "https://cdn.vinted.net/images.jpg",
69-
"high_resolution": {"timestamp": 1710000000},
70-
}
33+
{"url": "https://example.com/photo1.jpg", "high_resolution": {"timestamp": 1734796339}}
7134
],
72-
"url": "https://www.vinted.it/items/42-test-product",
35+
"url": "https://vinted.com/items/456",
7336
"plugins": [
7437
{
7538
"name": "attributes",
76-
"data": {"attributes": [{"code": "size", "data": {"value": "M"}}]},
39+
"data": {"attributes": [{"code": "size", "data": {"value": "44"}}]},
7740
}
7841
],
7942
}
43+
44+
45+
@pytest.fixture
46+
def mock_async_session():
47+
session = AsyncMock()
48+
session.cookies = MagicMock()
49+
session.cookies.get = MagicMock(return_value=None)
50+
session.headers = {}
51+
return session
52+
53+
54+
@pytest.fixture
55+
def mock_http_response():
56+
response = MagicMock(spec=Response)
57+
response.status_code = 200
58+
response.json.return_value = {
59+
"items": [
60+
{
61+
"id": 1,
62+
"title": "Item 1",
63+
"price": {"amount": 10, "currency_code": "EUR"},
64+
}
65+
]
66+
}
67+
return response
68+
69+
70+
@pytest.fixture
71+
def temp_cookies_dir(tmp_path):
72+
cookies_dir = tmp_path / "cookies"
73+
cookies_dir.mkdir()
74+
return cookies_dir

tests/models/test_catalog_item.py

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,54 @@
1-
from datetime import datetime, timedelta, timezone
2-
from typing import Callable
1+
from datetime import datetime, timezone
32

43
import pytest
54

6-
from vinted_api_kit.models import CatalogItem
5+
from vinted.models.item import CatalogItem
76

87

9-
def test_basic_catalog_item_parsing(sample_catalog_item_data: Callable[[int], dict]):
10-
data = sample_catalog_item_data(1710000000)
8+
def test_catalog_item_creation(sample_catalog_item_data):
9+
item = CatalogItem(raw_data=sample_catalog_item_data)
1110

12-
item = CatalogItem(data)
13-
14-
assert item.id == 77
15-
assert item.title == "Vintage Jeans"
16-
assert item.brand_title == "Levis"
17-
assert item.size_title == "32"
11+
assert item.id == 123
12+
assert item.title == "Nike Air Max"
13+
assert item.brand_title == "Nike"
14+
assert item.size_title == "42"
1815
assert item.currency == "EUR"
19-
assert item.price == 45
20-
assert item.photo == "https://cdn.vinted.net/catalog.jpg"
21-
assert item.url.startswith("https://www.vinted.it/items/")
16+
assert item.price == 50.0
17+
assert item.photo == "https://example.com/photo.jpg"
18+
assert item.url == "https://vinted.com/items/123-nike-air-max"
2219
assert isinstance(item.created_at_ts, datetime)
23-
assert item.raw_timestamp == 1710000000
20+
assert item.raw_timestamp == 1734796339
2421

2522

26-
@pytest.mark.parametrize(
27-
"delta_seconds, expected",
28-
[
29-
(30, True),
30-
(120 * 60, False),
31-
],
32-
)
33-
def test_is_new_item_parametrized(
34-
sample_catalog_item_data: Callable[[int], dict], delta_seconds: int, expected: bool
35-
):
36-
ts = int((datetime.now(timezone.utc) - timedelta(seconds=delta_seconds)).timestamp())
37-
data = sample_catalog_item_data(ts)
23+
def test_catalog_item_equality(sample_catalog_item_data):
24+
item1 = CatalogItem(raw_data=sample_catalog_item_data)
25+
item2 = CatalogItem(raw_data=sample_catalog_item_data)
26+
item3 = CatalogItem(raw_data={**sample_catalog_item_data, "id": 999})
3827

39-
item = CatalogItem(data)
28+
assert item1 == item2
29+
assert item1 != item3
4030

41-
assert item.is_new_item(minutes=1) is expected
4231

32+
def test_catalog_item_hash(sample_catalog_item_data):
33+
item1 = CatalogItem(raw_data=sample_catalog_item_data)
34+
item2 = CatalogItem(raw_data=sample_catalog_item_data)
4335

44-
def test_equality_and_hash(sample_catalog_item_data: Callable[[int], dict]):
45-
data1 = sample_catalog_item_data(1710000000)
46-
data2 = sample_catalog_item_data(1710000000)
36+
assert hash(item1) == hash(item2)
37+
assert len({item1, item2}) == 1
4738

48-
item1 = CatalogItem(data1)
49-
item2 = CatalogItem(data2)
5039

51-
assert item1 == item2
52-
assert hash(item1) == hash(item2)
40+
def test_catalog_item_is_new(sample_catalog_item_data):
41+
data = {**sample_catalog_item_data}
42+
data["photo"]["high_resolution"]["timestamp"] = int(datetime.now(timezone.utc).timestamp())
5343

44+
item = CatalogItem(raw_data=data)
45+
assert item.is_new_item(minutes=1) is True
5446

55-
def test_missing_fields_dont_crash():
56-
data = {"id": 15, "title": "stub", "brand_title": None, "size_title": None, "url": None}
5747

58-
item = CatalogItem(data)
48+
@pytest.mark.parametrize("missing_field", ["price", "photo", "url"])
49+
def test_catalog_item_with_missing_fields(sample_catalog_item_data, missing_field):
50+
data = {**sample_catalog_item_data}
51+
del data[missing_field]
5952

60-
assert item.price is None
61-
assert item.currency is None
62-
assert item.photo is None
63-
assert item.raw_timestamp is None
53+
item = CatalogItem(raw_data=data)
54+
assert item.id == 123

0 commit comments

Comments
 (0)