Skip to content

Commit a91469e

Browse files
authored
Тесты на prepare_value (#24)
* Тесты на prepare_value * Тест на prepare_value и енумы
1 parent 123bac4 commit a91469e

File tree

3 files changed

+85
-13
lines changed

3 files changed

+85
-13
lines changed

aliceio/utils/funcs.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,78 @@
11
import datetime
22
import json
33
from enum import Enum
4-
from typing import Any, Callable, Protocol
4+
from typing import Any, Callable, Literal, Protocol, Union
55

66
from aiohttp import JsonPayload
77

88
from aliceio.dispatcher.event.bases import REJECTED, UNHANDLED
99
from aliceio.types import InputFile
10+
from aliceio.types.base import AliceObject
1011

1112

1213
class PrepareValue(Protocol): # pragma: no cover
13-
def __call__(self, value: Any, files: dict[str, Any]) -> Any: ...
14+
def __call__(
15+
self,
16+
value: Any,
17+
files: dict[str, Any],
18+
_dumps_json: Union[Callable[..., str], Literal[False]] = False,
19+
) -> Any: ...
1420

1521

1622
# Ключи, у которых значение = None, не пропускаются, потому что иначе не получится
1723
# установить None в значение хранилища состояний API Алисы
18-
def prepare_value(value: Any, files: dict[str, Any]) -> Any:
24+
def prepare_value(
25+
value: Any,
26+
files: dict[str, Any],
27+
_dumps_json: Union[Callable[..., str], Literal[False]] = False,
28+
) -> Any:
1929
"""Подготовка значения перед отправкой."""
2030
if value in (None, UNHANDLED, REJECTED):
2131
return None
2232
if isinstance(value, str):
2333
return value
2434
if isinstance(value, InputFile):
25-
key = "file"
35+
key = "file" # aiogram: key = secrets.token_urlsafe(10)
2636
files[key] = value
2737
return f"attach://{key}"
2838
if isinstance(value, dict):
29-
return {key: prepare_value(item, files=files) for key, item in value.items()}
39+
value = {key: prepare_value(item, files=files) for key, item in value.items()}
40+
if _dumps_json:
41+
return _dumps_json(value)
42+
return value
3043
if isinstance(value, list):
31-
return [prepare_value(item, files=files) for item in value]
44+
value = [prepare_value(item, files=files) for item in value]
45+
if _dumps_json:
46+
return _dumps_json(value)
47+
return value
3248
if isinstance(value, datetime.timedelta):
3349
now = datetime.datetime.now()
3450
return str(round((now + value).timestamp()))
3551
if isinstance(value, datetime.datetime):
3652
return str(round(value.timestamp()))
3753
if isinstance(value, Enum):
3854
return prepare_value(value.value, files=files)
55+
if isinstance(value, AliceObject):
56+
return prepare_value(
57+
value.model_dump(warnings=False),
58+
files=files,
59+
_dumps_json=_dumps_json,
60+
)
61+
if _dumps_json:
62+
return _dumps_json(value)
3963
return value
4064

4165

66+
# TODO: В prepavre_value_fn не передаётся json_dumps, пофиксить?
4267
def build_json_payload(
4368
value: Any,
4469
prepare_value_fn: PrepareValue = prepare_value,
4570
json_dumps: Callable[..., str] = json.dumps,
4671
) -> JsonPayload:
4772
return JsonPayload(
48-
value=prepare_value_fn(value=value, files={}),
73+
value=prepare_value_fn(
74+
value=value,
75+
files={},
76+
),
4977
dumps=json_dumps,
5078
)

aliceio/webhook/aiohttp_server/base.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from aiohttp import JsonPayload, web
77
from aiohttp.abc import Application
8-
from pydantic import BaseModel
98

109
from aliceio import Dispatcher, Skill
1110
from aliceio.dispatcher.event.bases import REJECTED, UNHANDLED
@@ -125,7 +124,4 @@ def _build_web_response(self, result: Any) -> web.Response:
125124
)
126125

127126
def _build_json_response(self, result: Optional[AliceObject]) -> JsonPayload:
128-
return build_json_payload(
129-
value=result.model_dump() if isinstance(result, BaseModel) else result,
130-
json_dumps=self.json_dumps,
131-
)
127+
return build_json_payload(value=result, json_dumps=self.json_dumps)

tests/test_api/test_client/test_session/test_base_session.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,28 @@
22
import json
33
from collections.abc import AsyncGenerator
44
from contextlib import AbstractAsyncContextManager
5+
from enum import Enum
56
from typing import Any, Optional
67
from unittest.mock import AsyncMock, patch
78

89
import pytest
10+
from pytz import utc
911

1012
from aliceio import Skill
1113
from aliceio.client.alice import PRODUCTION, AliceAPIServer
1214
from aliceio.client.session.base import AliceType, BaseSession
15+
from aliceio.enums import CardType
1316
from aliceio.exceptions import AliceAPIError, ClientDecodeError
1417
from aliceio.methods import AliceMethod, Status
15-
from aliceio.types import PreQuota, Quota, SpaceStatus
18+
from aliceio.types import AnalyticEvent, PreQuota, Quota, SpaceStatus
1619
from aliceio.utils.funcs import prepare_value
1720
from tests.mocked.mocked_skill import MockedSkill
1821

1922

23+
class _TestEnum(Enum):
24+
field = "fieldValue"
25+
26+
2027
class CustomSession(BaseSession):
2128
async def close(self):
2229
pass
@@ -78,6 +85,47 @@ def test_prepare_value_timedelta(self, skill: MockedSkill):
7885
value = prepare_value(datetime.timedelta(minutes=2), files={})
7986
assert isinstance(value, str)
8087

88+
@pytest.mark.parametrize(
89+
"value,result",
90+
[
91+
[None, None],
92+
["text", "text"],
93+
[CardType.BIG_IMAGE, "BigImage"],
94+
[_TestEnum.field, "fieldValue"],
95+
[42, "42"],
96+
[True, "true"],
97+
[["test"], '["test"]'],
98+
[["test", ["test"]], '["test", ["test"]]'],
99+
[[{"test": "pass", "spam": None}], '[{"test": "pass", "spam": null}]'],
100+
[
101+
{"test": "pass", "number": 42, "spam": None},
102+
'{"test": "pass", "number": 42, "spam": null}',
103+
],
104+
[
105+
{"foo": {"test": "pass", "spam": None}},
106+
'{"foo": {"test": "pass", "spam": null}}',
107+
],
108+
[
109+
datetime.datetime(
110+
year=2017,
111+
month=5,
112+
day=17,
113+
hour=4,
114+
minute=11,
115+
second=42,
116+
tzinfo=utc,
117+
),
118+
"1494994302",
119+
],
120+
[
121+
{"analytic_event": AnalyticEvent(name="John", value={"key": "door"})},
122+
'{"analytic_event": {"name": "John", "value": {"key": "door"}}}',
123+
],
124+
],
125+
)
126+
def test_prepare_value(self, value: Any, result: str, skill: MockedSkill):
127+
assert prepare_value(value, files={}, _dumps_json=json.dumps) == result
128+
81129
def test_check_response_json_decode_error(self):
82130
session = CustomSession()
83131
skill = MockedSkill()

0 commit comments

Comments
 (0)