Skip to content

Commit ea5c02b

Browse files
authored
chore: Update codecov configuration and add missing unit tests (#49)
- Modified pyproject codecov configuration to skip TYPE_CHECKING sections from coverage calculation - Updated codecov configuration to only check coverage for the cozepy directory - Added missing unit tests to improve overall test coverage
1 parent d1fc143 commit ea5c02b

File tree

10 files changed

+197
-7
lines changed

10 files changed

+197
-7
lines changed

codecov.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ coverage:
55
target: 70%
66
threshold: 70%
77
base: auto
8+
paths:
9+
- cozepy
810
comment: #this is a top-level key
911
layout: "diff, flags, files"
1012
behavior: default

cozepy/auth/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class WebOAuthApp(OAuthApp):
122122
Normal OAuth App.
123123
"""
124124

125-
def __init__(self, client_id: str, client_secret: str, base_url: str = COZE_COM_BASE_URL):
125+
def __init__(self, client_id: str, client_secret: str, base_url: str = COZE_COM_BASE_URL, www_base_url: str = ""):
126126
"""
127127
:param client_id:
128128
:param client_secret:
@@ -133,7 +133,7 @@ def __init__(self, client_id: str, client_secret: str, base_url: str = COZE_COM_
133133
self._base_url = base_url
134134
self._api_endpoint = urlparse(base_url).netloc
135135
self._token = ""
136-
super().__init__(client_id, base_url, www_base_url="")
136+
super().__init__(client_id, base_url, www_base_url=www_base_url)
137137

138138
def get_oauth_url(
139139
self,

cozepy/exception.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@ def __init__(self, code: int = None, msg: str = "", logid: str = None):
2424
super().__init__(f"msg: {msg}, logid: {logid}")
2525

2626

27-
class CozePKCEAuthError(CozeAPIError):
27+
class CozePKCEAuthError(CozeError):
2828
"""
2929
base class for all pkce auth errors
3030
"""
3131

3232
def __init__(
3333
self, error: Literal["authorization_pending", "slow_down", "access_denied", "expired_token"], logid: str = None
3434
):
35-
super().__init__(code=None, msg=error, logid=logid)
35+
super().__init__(f"pkce auth error: {error}")
3636
self.error = error
37+
self.logid = logid
3738

3839

3940
class CozeEventError(CozeError):

cozepy/model.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ def __init__(self, items: List[T], has_more: bool):
2020
self.items = items
2121
self.has_more = has_more
2222

23-
def __repr__(self):
24-
return f"PagedBase(items={self.items}, has_more={self.has_more})"
25-
2623

2724
class TokenPaged(PagedBase[T]):
2825
"""

pyproject.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ line-length = 120
2727
select = ["E", "F", "I"]
2828
ignore = ["E501"]
2929

30+
[tool.coverage.report]
31+
precision = 1
32+
skip_covered = true
33+
exclude_lines = [
34+
"abc.abstractmethod",
35+
"if TYPE_CHECKING.*:",
36+
"raise NotImplementedError",
37+
]
38+
3039
[build-system]
3140
requires = ["poetry-core"]
3241
build-backend = "poetry.core.masonry.api"

tests/test_auth.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,30 @@ def test_get_oauth_url(self, respx_mock):
4141
"state=state"
4242
) == url
4343

44+
def test_get_oauth_url_config_www_url(self, respx_mock):
45+
app = WebOAuthApp("client id", "client secret", www_base_url="https://example.com")
46+
47+
url = app.get_oauth_url("https://example.com", "state")
48+
assert (
49+
"https://example.com/api/permission/oauth2/authorize"
50+
"?response_type=code&"
51+
"client_id=client+id&"
52+
"redirect_uri=https%3A%2F%2Fexample.com&"
53+
"state=state"
54+
) == url
55+
56+
def test_get_oauth_url_config_custom_api_base_url(self, respx_mock):
57+
app = WebOAuthApp("client id", "client secret", base_url="https://api.example.com")
58+
59+
url = app.get_oauth_url("https://example.com", "state")
60+
assert (
61+
"https://api.example.com/api/permission/oauth2/authorize"
62+
"?response_type=code&"
63+
"client_id=client+id&"
64+
"redirect_uri=https%3A%2F%2Fexample.com&"
65+
"state=state"
66+
) == url
67+
4468
def test_get_access_token(self, respx_mock):
4569
app = WebOAuthApp("client id", "client secret")
4670
mock_token = random_hex(20)

tests/test_chat.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,36 @@ def test_stream(self, respx_mock):
8181
)
8282
assert events[len(events) - 1].event == ChatEventType.CONVERSATION_CHAT_COMPLETED
8383

84+
def test_stream_error(self, respx_mock):
85+
coze = Coze(auth=TokenAuth(token="token"))
86+
87+
respx_mock.post("/v3/chat").mock(
88+
httpx.Response(
89+
200,
90+
content="""
91+
event:error
92+
data:{}
93+
""",
94+
)
95+
)
96+
with pytest.raises(Exception, match="error event"):
97+
list(coze.chat.stream(bot_id="bot", user_id="user"))
98+
99+
def test_stream_invalid_event(self, respx_mock):
100+
coze = Coze(auth=TokenAuth(token="token"))
101+
102+
respx_mock.post("/v3/chat").mock(
103+
httpx.Response(
104+
200,
105+
content="""
106+
event:invalid
107+
data:{}
108+
""",
109+
)
110+
)
111+
with pytest.raises(Exception, match="invalid chat.event: invalid"):
112+
list(coze.chat.stream(bot_id="bot", user_id="user"))
113+
84114
def test_retrieve(self, respx_mock):
85115
coze = Coze(auth=TokenAuth(token="token"))
86116

tests/test_model.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from typing import Dict
2+
3+
import pytest
4+
5+
from cozepy import CozeEventError, LastIDPaged, NumberPaged, Stream, TokenPaged
6+
7+
8+
def test_page_mode():
9+
page = TokenPaged(["a", "b", "c"], "next_page", True)
10+
assert f"{page}" == "TokenPaged(items=['a', 'b', 'c'], next_page_token=next_page)"
11+
12+
page = NumberPaged(["a", "b", "c"], 1, 3, 100)
13+
assert f"{page}" == "NumberPaged(items=['a', 'b', 'c'], page_num=1, page_size=3, total=100)"
14+
15+
page = LastIDPaged(["a", "b", "c"], 1, 3, True)
16+
assert f"{page}" == "LastIDPaged(items=['a', 'b', 'c'], first_id=1, last_id=3, has_more=True)"
17+
18+
19+
def mock_handler(d: Dict[str, str]):
20+
return d
21+
22+
23+
def test_stream_invalid_event():
24+
items = ["event:x"]
25+
s = Stream(iter(items), ["field"], mock_handler)
26+
27+
with pytest.raises(CozeEventError, match="invalid event, data: event:x"):
28+
next(s)
29+
30+
31+
def test_stream_invalid_field():
32+
items = ["event:x1", "event:x2"]
33+
s = Stream(iter(items), ["event", "second"], mock_handler)
34+
35+
with pytest.raises(CozeEventError, match="invalid event, field: event, data: event:x2"):
36+
next(s)

tests/test_request.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import httpx
2+
import pytest
3+
4+
from cozepy import CozeAPIError, CozePKCEAuthError
5+
from cozepy.model import CozeModel
6+
from cozepy.request import Requester
7+
8+
9+
class TestModel(CozeModel):
10+
id: str
11+
12+
13+
class TestDebugModel(CozeModel):
14+
debug_url: str
15+
data: str
16+
17+
18+
@pytest.mark.respx(base_url="https://api.coze.com")
19+
class TestRequester:
20+
def test_code_msg(self, respx_mock):
21+
respx_mock.post("/api/test").mock(
22+
httpx.Response(200, json={"code": 100, "msg": "request failed"}, headers={"x-tt-logid": "mock-logid"})
23+
)
24+
25+
with pytest.raises(CozeAPIError, match="code: 100, msg: request failed, logid: mock-logid"):
26+
Requester().request("post", "https://api.coze.com/api/test", TestModel)
27+
28+
def test_auth_slow_down(self, respx_mock):
29+
respx_mock.post("/api/test").mock(
30+
httpx.Response(
31+
200,
32+
json={
33+
"error_code": "slow_down",
34+
},
35+
headers={"x-tt-logid": "mock-logid"},
36+
)
37+
)
38+
39+
with pytest.raises(CozePKCEAuthError, match="pkce auth error: slow_down"):
40+
Requester().request("post", "https://api.coze.com/api/test", TestModel)
41+
42+
def test_error_message(self, respx_mock):
43+
respx_mock.post("/api/test").mock(
44+
httpx.Response(
45+
200,
46+
json={
47+
"error_message": "error_message",
48+
},
49+
headers={"x-tt-logid": "mock-logid"},
50+
)
51+
)
52+
53+
with pytest.raises(CozeAPIError, match="msg: error_message, logid: mock-logid"):
54+
Requester().request("post", "https://api.coze.com/api/test", TestModel)
55+
56+
def test_debug_url(self, respx_mock):
57+
respx_mock.post("/api/test").mock(
58+
httpx.Response(
59+
200,
60+
json={
61+
"debug_url": "debug_url",
62+
"data": "data",
63+
},
64+
headers={"x-tt-logid": "mock-logid"},
65+
)
66+
)
67+
68+
Requester().request("post", "https://api.coze.com/api/test", TestDebugModel)

tests/test_workflow.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,26 @@ def test_resume(self, respx_mock):
8686

8787
assert events
8888
assert len(events) == 9
89+
90+
def test_stream_invalid_event(self, respx_mock):
91+
coze = Coze(auth=TokenAuth(token="token"))
92+
93+
respx_mock.post("/v1/workflow/stream_resume").mock(
94+
httpx.Response(
95+
200,
96+
content="""
97+
id:0
98+
event:invalid
99+
data:{}""",
100+
)
101+
)
102+
103+
with pytest.raises(Exception, match="invalid workflows.event: invalid"):
104+
list(
105+
coze.workflows.runs.resume(
106+
workflow_id="id",
107+
event_id="event_id",
108+
resume_data="resume_data",
109+
interrupt_type=123,
110+
)
111+
)

0 commit comments

Comments
 (0)