Skip to content

Commit 7dd5c36

Browse files
committed
Added more tests and fixed duty.py file for multiple py version testing
1 parent 8dd78f1 commit 7dd5c36

File tree

13 files changed

+293
-134
lines changed

13 files changed

+293
-134
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ repos:
2828
pass_filenames: false
2929
- id: test
3030
name: test
31-
entry: duty test
31+
files: \.py
32+
entry: duty test-all-versions
3233
language: system
3334
pass_filenames: false
3435

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ actions = \
1414
format \
1515
lint \
1616
type-check \
17+
test-all-versions \
1718
test \
1819
coverage \
1920
docs \

duties.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from pathlib import Path
77
from typing import Iterator
88

9-
from duty import context, duty
9+
from duty import duty
10+
from duty._internal import context
1011

1112
PYTHON_VERSIONS = os.getenv('PYTHON_VERSIONS', '3.9 3.10 3.11 3.12 3.13').split()
1213

@@ -98,18 +99,36 @@ def type_check(ctx: context.Context, *files):
9899

99100
@duty
100101
def test(ctx: context.Context, live: bool = False):
102+
"""Run tests on a single file."""
103+
ctx.run(
104+
(
105+
'uv run --active --no-sync coverage run -m pytest -sv --durations=10'
106+
f'{" --live" if live else ""}'
107+
),
108+
title='Running test',
109+
)
110+
111+
112+
@duty
113+
def test_all_versions(ctx: context.Context, live: bool = False):
101114
"""Run tests with local environment"""
102115
for ver in PYTHON_VERSIONS:
103116
venv_path = Path(f'.venvs{SEP}{ver}')
104117

105-
with environ(VIRTUAL_ENV=str(venv_path)):
106-
ctx.run(
107-
'uv run --active --no-sync coverage run --data-file=coverage/.coverage.py'
108-
+ ver
109-
+ ' -m pytest -sv --durations=10 '
110-
+ ('--live' if live else ''),
111-
title=f'Running tests (python {ver})',
112-
)
118+
if sys.platform == 'win32':
119+
activate_cmd = f'{venv_path}\\Scripts\\activate.bat && '
120+
else:
121+
activate_cmd = f'source {venv_path}/bin/activate && '
122+
123+
# PPrint python environment with uv
124+
ctx.run(
125+
(
126+
f'{activate_cmd}uv run --active --no-sync '
127+
f'coverage run --data-file=coverage/.coverage.py{ver}'
128+
f' -m pytest -sv --durations=10 {" --live" if live else ""}'
129+
),
130+
title=f'Running tests (python {ver})',
131+
)
113132

114133

115134
@duty

src/integrify/clopos/client.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ObjectResponse,
3535
)
3636
from integrify.clopos.schemas.customers.object import Customer, Group
37+
from integrify.clopos.schemas.customers.request import CustomerFilter
3738
from integrify.clopos.schemas.enums import (
3839
CategoryType,
3940
DiscountType,
@@ -42,7 +43,7 @@
4243
)
4344
from integrify.clopos.schemas.orders.object import Order, OrderPayloadIn
4445
from integrify.clopos.schemas.products.object import Product, StopList
45-
from integrify.clopos.schemas.products.request import GetProducstRequestFilter
46+
from integrify.clopos.schemas.products.request import GetProducstRequestFilter, StopListFilter
4647
from integrify.clopos.schemas.receipts.object import Receipt, ReceiptProductIn
4748
from integrify.clopos.schemas.receipts.request import PaymentMethodIn, UpdateReceiptMetaData
4849
from integrify.clopos.schemas.sales.object import PaymentMethod, SaleType
@@ -279,8 +280,7 @@ def get_customers(
279280
page: Unset[int] = 1,
280281
limit: Unset[int] = 20,
281282
with_: Unset[list[str]] = UNSET,
282-
filter_bys: Unset[list[str]] = UNSET,
283-
filter_values: Unset[list[str]] = UNSET,
283+
filters: Unset[list[CustomerFilter]] = UNSET,
284284
*,
285285
headers: Unset[dict[str, str]] = UNSET,
286286
) -> APIResponse[ObjectListResponse[Customer]]:
@@ -303,9 +303,8 @@ def get_customers(
303303
Args:
304304
page: Page number for pagination (starts at 1)
305305
limit: Maximum number of objects to return (1-100)
306-
with_: Include related data in the response. Supported values: `group`, `balance`, `cashback_balance`. You can include multiple with parameters.
307-
filter_bys: Filter by specific fields. Supported values: `name` (partial match), `phones`, `group_id`.
308-
filter_values: Filter values matching the filter_bys
306+
with_: Include related data in the response. Supported values: `group`, `balance`, cashback_balance. You can include multiple with parameters.
307+
filters: List of filters to apply in format {'by': 'name'|'phones'|'group_id', 'value': str}
309308
headers: Headers for request
310309
```
311310
""" # noqa: E501
@@ -619,9 +618,7 @@ def get_product_by_id(
619618

620619
def get_stop_list(
621620
self,
622-
filter_bys: list[Literal['id', 'limit', 'timestamp']] = [],
623-
filter_froms: list[int] = [],
624-
filter_tos: list[int] = [],
621+
filters: Unset[list[StopListFilter]] = UNSET,
625622
*,
626623
headers: Unset[dict[str, str]] = UNSET,
627624
) -> APIResponse[ObjectListResponse[StopList]]:
@@ -639,19 +636,18 @@ def get_stop_list(
639636
640637
# Or if you have set the environment variables
641638
CloposClient.get_stop_list(
642-
filter_bys=['id', 'limit'],
643-
filter_froms=[1, 0],
644-
filter_tos=[100, 10],
639+
filters=[
640+
{'by': 'id', 'from_': '0', 'to': '100'},
641+
{'by': 'limit', 'from_': '1', 'to': '10'},
642+
]
645643
headers={'x-token': token},
646-
)) # Filter by id from 0 to 100 AND limit from 1 to 10
644+
) # Filter by id from 0 to 100 AND limit from 1 to 10
647645
```
648646
649647
**Response format: [`ObjectListResponse[StopList]`][integrify.clopos.schemas.common.response.ObjectListResponse]**
650648
651649
Args:
652-
filter_bys: Filter by: id, limit, timestamp
653-
filter_froms: Filter from values
654-
filter_tos: Filter to values
650+
filters: List of filter options. Each filter is a dict with keys 'by', 'from_', and 'to'.
655651
headers: Headers for request
656652
```
657653
""" # noqa: E501

src/integrify/clopos/schemas/common/object.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,12 @@ class Price(BaseModel):
199199
price: Decimal
200200
"""Price"""
201201

202-
from_: int = Field(alias='from')
202+
from_: UnsetOrNoneField[int] = Field(alias='from')
203203
"""From which period"""
204204

205+
venue_id: UnsetOrNoneField[int]
206+
"""The venue ID"""
207+
205208

206209
class TimerSetting(BaseModel):
207210
interval: int

src/integrify/clopos/schemas/common/response.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,15 @@ class ObjectListResponse(PaginatedResponse, Generic[_ObjectTypeT]):
4141
data: list[_ObjectTypeT]
4242

4343

44+
class Errors(BaseModel):
45+
message: Optional[str] = None
46+
type: Optional[str] = None
47+
exception: Optional[str] = None
48+
code: Optional[int] = None
49+
http_code: Optional[int] = None
50+
51+
4452
class ErrorResponse(BaseModel):
4553
success: Literal[False]
46-
error: str
54+
error: Optional[list[Errors]] = None
4755
message: Optional[str] = None

src/integrify/clopos/schemas/customers/request.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Literal
22

33
from pydantic import Field, model_serializer
4+
from typing_extensions import TypedDict
45

56
from integrify.clopos.helpers import IsoDate
67
from integrify.clopos.schemas.common.request import PaginatedDataRequest
@@ -9,22 +10,26 @@
910
from integrify.utils import UnsetField, UnsetOrNoneField
1011

1112

13+
class CustomerFilter(TypedDict):
14+
by: Literal['name', 'phones', 'group_id']
15+
value: str
16+
17+
1218
class GetCustomersRequest(PaginatedDataRequest):
13-
with_: UnsetField[list[Literal['group', 'balance', 'cashback_balance']]] = Field(alias='with[]')
14-
filter_bys: UnsetField[list[str]] = Field(exclude=True)
15-
filter_values: UnsetField[list[str]] = Field(exclude=True)
19+
with_: UnsetField[list[Literal['group', 'balance', 'cashback_balance']]] = Field(exclude=True)
20+
filters: UnsetField[list[CustomerFilter]] = Field(exclude=True)
1621

17-
@model_serializer(mode='wrap')
18-
def serialize_model(self, serializer) -> dict:
22+
@model_serializer()
23+
def serialize_model(self) -> dict:
1924
"""Model serializer"""
20-
data = serializer(self)
25+
data = {}
2126

22-
filter_bys = data.pop('filter_bys', [])
23-
filter_values = data.pop('filter_values', [])
27+
for i, with_item in enumerate(self.with_ or []):
28+
data[f'with[{i}]'] = with_item
2429

25-
for i, (filter_by, filter_value) in enumerate(zip(filter_bys, filter_values)):
26-
data[f'filters[{i}][0]'] = filter_by
27-
data[f'filter[{i}][1]'] = filter_value
30+
for i, filter_ in enumerate(self.filters or []):
31+
data[f'filters[{i}][0]'] = filter_['by']
32+
data[f'filter[{i}][1]'] = filter_['value']
2833

2934
return data
3035

src/integrify/clopos/schemas/products/request.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from itertools import zip_longest
21
from typing import Literal, Union
32

43
from pydantic import Field, field_serializer, model_serializer
@@ -53,7 +52,7 @@ class GetProducstRequestFilter(TypedDict):
5352

5453

5554
class GetProductsRequest(PaginatedDataRequest):
56-
selects: UnsetField[Union[list[str], str]] = Field(alias='selects[]')
55+
selects: UnsetField[Union[list[str], str]] = Field(serialization_alias='selects[]')
5756
filters: UnsetField[GetProducstRequestFilter]
5857

5958
@field_serializer('selects')
@@ -71,7 +70,7 @@ def serialize_filters(self, filters):
7170
if value is not None: # Only include non-None values
7271
result[key] = [key, value]
7372

74-
return {'filters': result}
73+
return result
7574

7675

7776
class GetProductByIDRequest(ByIDRequest):
@@ -89,26 +88,28 @@ class GetProductByIDRequest(ByIDRequest):
8988
'setting',
9089
]
9190
]
92-
] = Field(alias='with[]')
91+
] = Field(serialization_alias='with[]')
92+
93+
94+
class StopListFilter(TypedDict):
95+
by: Literal['id', 'limit', 'timestamp']
96+
from_: int
97+
to: NotRequired[int]
9398

9499

95100
class GetStopListRequest(PayloadBaseModel):
96-
filter_bys: list[Literal['id', 'limit', 'timestamp']] = []
97-
filter_froms: list[int] = []
98-
filter_tos: list[int] = []
101+
filters: UnsetField[list[StopListFilter]]
99102

100103
@model_serializer
101104
def serialize_model(self) -> dict:
102105
"""Model serializer"""
103106
data = {}
104107

105-
for i, (filter_name, fr, to) in enumerate(
106-
zip_longest(self.filter_bys, self.filter_froms, self.filter_tos)
107-
):
108-
data[f'filters[{i}][0]'] = filter_name
109-
data[f'filters[{i}][1][0]'] = fr
108+
for i, filter_ in enumerate(self.filters or []):
109+
data[f'filters[{i}][0]'] = filter_['by']
110+
data[f'filters[{i}][1][0]'] = filter_['from_']
110111

111-
if to:
112-
data[f'filters[{i}][1][1]'] = to
112+
if 'to' in filter_:
113+
data[f'filters[{i}][1][1]'] = filter_['to']
113114

114115
return data

tests/conftest.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
from integrify.clopos.schemas.common.response import ObjectResponse
7+
from integrify.clopos.schemas.orders.object import Order
78
from integrify.clopos.schemas.receipts.object import Receipt
89
from integrify.schemas import APIResponse
910
from integrify.test import pytest_addoption # noqa: F401
@@ -36,6 +37,80 @@ def authed_client(client):
3637
yield client
3738

3839

40+
@pytest.fixture(scope='package')
41+
def authed_dry_client(client):
42+
from tests.client import CloposTestClientClass
43+
44+
client = CloposTestClientClass(dry=True)
45+
client._token = 'randomtoken'
46+
yield client
47+
48+
49+
@pytest.fixture(scope='package')
50+
def new_order_resp(authed_client: 'CloposTestClientClass'):
51+
resp = authed_client.create_order(
52+
customer_id=1,
53+
payload={ # type: ignore[arg-type]
54+
'service': {
55+
'sale_type_id': 2,
56+
'sale_type_name': 'Delivery',
57+
'venue_id': 1,
58+
'venue_name': 'Main',
59+
},
60+
'customer': {
61+
'id': 9,
62+
'name': 'Rahid Akhundzada',
63+
'customer_discount_type': 1,
64+
'phone': '+994705401040',
65+
},
66+
'products': [
67+
{
68+
'product_id': 1,
69+
'count': 1,
70+
'product_modificators': [
71+
{'modificator_id': 187, 'count': 1},
72+
{'modificator_id': 201, 'count': 1},
73+
],
74+
'meta': {
75+
'price': 0,
76+
'order_product': {
77+
'product': {
78+
'id': 1,
79+
'name': 'Mega Dürüm Menü Alana Çiğ Köfte Dürüm',
80+
'category_id': 1,
81+
'station_id': 1,
82+
'price': 0,
83+
},
84+
'count': 1,
85+
'status': 'completed',
86+
'product_modificators': [
87+
{'modificator_id': 187, 'count': 1},
88+
{'modificator_id': 201, 'count': 1},
89+
],
90+
'product_hash': 'MTExODcsMTEyMDE=',
91+
},
92+
},
93+
}
94+
],
95+
},
96+
meta={
97+
'comment': '',
98+
'discount': {'discount_type': 1, 'discount_value': 10},
99+
'orderTotal': '16.2000',
100+
'apply_service_charge': True,
101+
'customer_discount_type': 1,
102+
'service_charge_value': 0,
103+
},
104+
)
105+
106+
yield resp
107+
108+
109+
@pytest.fixture(scope='package')
110+
def new_order_object(new_order_resp: APIResponse[ObjectResponse[Order]]):
111+
yield new_order_resp.body.data
112+
113+
39114
@pytest.fixture(scope='package')
40115
def new_receipt_resp(authed_client: 'CloposTestClientClass'):
41116
cid = uuid.uuid4().hex

0 commit comments

Comments
 (0)