Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.

Commit 9e31ee4

Browse files
committed
test: organizes test by class and adds one for mapping passage error
1 parent 7cf550e commit 9e31ee4

File tree

4 files changed

+216
-166
lines changed

4 files changed

+216
-166
lines changed

passageidentity/errors.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,43 @@
44

55
from typing import TYPE_CHECKING
66

7+
import typing_extensions
8+
79
if TYPE_CHECKING:
810
from passageidentity.openapi_client.exceptions import ApiException
911

1012

1113
class PassageError(Exception):
1214
"""Error class for handling Passage errors."""
1315

16+
@typing_extensions.deprecated(
17+
"This should only be constructed by the Passage SDK. Use this type just for type checking.",
18+
)
1419
def __init__(
1520
self,
1621
message: str,
1722
status_code: int | None = None,
1823
status_text: str | None = None,
1924
body: dict | None = None,
25+
error_code: str | None = None,
2026
) -> None:
2127
"""Initialize the error with a message, status code, status text, and optional body."""
2228
self.message = message
2329
self.status_code = status_code
2430
self.status_text = status_text
31+
32+
self.error_code = error_code
33+
self.error = None
34+
2535
if body is not None:
2636
self.error = body["error"]
2737
self.error_code = body["code"]
28-
else:
29-
self.error = None
30-
self.error_code = None
3138

32-
@staticmethod
33-
def from_response_error(response_error: ApiException, message: str | None) -> PassageError:
39+
@classmethod
40+
def from_response_error(cls, response_error: ApiException, message: str | None = None) -> PassageError:
3441
"""Initialize the error with a response body and optional message."""
3542
error_code = response_error.body["code"] if response_error.body else None
3643
error_msg = response_error.body["error"] if response_error.body else None
3744
msg = ": ".join(filter(None, [message, error_msg]))
3845

39-
psg_error = PassageError(msg)
40-
psg_error.status_code = response_error.status
41-
psg_error.error_code = error_code
42-
43-
return psg_error
46+
return cls(message=msg, status_code=response_error.status, error_code=error_code)

tests/authenticate_test.py

Lines changed: 0 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
from passageidentity import PassageError
99
from passageidentity.openapi_client.models.app_info import AppInfo
10-
from passageidentity.openapi_client.models.update_user_request import UpdateUserRequest
11-
from passageidentity.openapi_client.models.user_info import UserInfo
1210
from passageidentity.passage import Passage
1311

1412
load_dotenv()
@@ -56,164 +54,10 @@ def test_create_magic_link() -> None:
5654
assert magic_link.ttl == 12 # type: ignore[attr-defined]
5755

5856

59-
def test_get_user_info_valid() -> None:
60-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
61-
user = cast(UserInfo, psg.getUser(PASSAGE_USER_ID))
62-
assert user.id == PASSAGE_USER_ID
63-
64-
65-
def test_get_user_info_by_identifier_valid() -> None:
66-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
67-
68-
email = f.email()
69-
new_user = cast(UserInfo, psg.createUser({"email": email})) # type: ignore[arg-type]
70-
assert new_user.email == email
71-
72-
user_by_identifier = cast(UserInfo, psg.getUserByIdentifier(email))
73-
assert user_by_identifier.id == new_user.id
74-
75-
user = cast(UserInfo, psg.getUser(new_user.id))
76-
assert user.id == new_user.id
77-
78-
assert user_by_identifier == user
79-
assert psg.deleteUser(new_user.id)
80-
81-
82-
def test_get_user_info_by_identifier_valid_upper_case() -> None:
83-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
84-
85-
email = f.email()
86-
new_user = cast(UserInfo, psg.createUser({"email": email})) # type: ignore[arg-type]
87-
assert new_user.email == email
88-
89-
user_by_identifier = cast(UserInfo, psg.getUserByIdentifier(email.upper()))
90-
assert user_by_identifier.id == new_user.id
91-
92-
user = cast(UserInfo, psg.getUser(new_user.id))
93-
assert user.id == new_user.id
94-
95-
assert user_by_identifier == user
96-
assert psg.deleteUser(new_user.id)
97-
98-
99-
def test_get_user_info_by_identifier_phone_valid() -> None:
100-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
101-
102-
phone = "+15005550030"
103-
new_user = cast(UserInfo, psg.createUser({"phone": phone})) # type: ignore[arg-type]
104-
assert new_user.phone == phone
105-
106-
user_by_identifier = cast(UserInfo, psg.getUserByIdentifier(phone))
107-
assert user_by_identifier.id == new_user.id
108-
109-
user = cast(UserInfo, psg.getUser(new_user.id))
110-
assert user.id == new_user.id
111-
112-
assert user_by_identifier == user
113-
assert psg.deleteUser(new_user.id)
114-
115-
116-
def test_get_user_info_by_identifier_error() -> None:
117-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
118-
119-
with pytest.raises(PassageError, match="Could not find user with identifier"):
120-
psg.getUserByIdentifier("error@passage.id")
121-
122-
123-
def test_activate_user() -> None:
124-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
125-
user = cast(UserInfo, psg.activateUser(PASSAGE_USER_ID))
126-
assert user.status == "active"
127-
128-
129-
def test_deactivate_user() -> None:
130-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
131-
132-
user = cast(UserInfo, psg.getUser(PASSAGE_USER_ID))
133-
user = cast(UserInfo, psg.deactivateUser(user.id))
134-
assert user.status == "inactive"
135-
136-
137-
def test_list_user_devices() -> None:
138-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
139-
140-
devices = cast(list, psg.listUserDevices(PASSAGE_USER_ID))
141-
assert len(devices) == 2
142-
143-
144-
def test_update_user_phone() -> None:
145-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
146-
147-
phone = "+15005550021"
148-
new_user = cast(UserInfo, psg.createUser({"phone": phone})) # type: ignore[arg-type]
149-
150-
phone = "+15005550022"
151-
user = cast(UserInfo, psg.updateUser(new_user.id, {"phone": phone})) # type: ignore[arg-type]
152-
assert user.phone == phone
153-
assert psg.deleteUser(new_user.id)
154-
155-
156-
def test_update_user_email() -> None:
157-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
158-
159-
email = f.email()
160-
req = UpdateUserRequest(email=email)
161-
user = cast(UserInfo, psg.updateUser(PASSAGE_USER_ID, req))
162-
assert user.email == email
163-
164-
165-
def test_update_user_with_metadata() -> None:
166-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
167-
168-
email = f.email()
169-
user = cast(UserInfo, psg.updateUser(PASSAGE_USER_ID, {"email": email, "user_metadata": {"example1": "qwe"}})) # type: ignore[arg-type]
170-
assert user.email == email
171-
assert user.user_metadata["example1"] == "qwe" # type: ignore[index]
172-
173-
user = cast(UserInfo, psg.updateUser(PASSAGE_USER_ID, {"email": email, "user_metadata": {"example1": "asd"}})) # type: ignore[arg-type]
174-
assert user.email == email
175-
assert user.user_metadata["example1"] == "asd" # type: ignore[index]
176-
177-
178-
def test_create_user_with_metadata() -> None:
179-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
180-
181-
email = f.email()
182-
user = cast(UserInfo, psg.createUser({"email": email, "user_metadata": {"example1": "qwe"}})) # type: ignore[arg-type]
183-
assert user.email == email
184-
assert user.user_metadata["example1"] == "qwe" # type: ignore[index]
185-
assert psg.deleteUser(user.id)
186-
187-
188-
def test_get_user_info_user_does_not_exist() -> None:
189-
pass
190-
191-
192-
def test_create_and_delete_user() -> None:
193-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
194-
195-
email = f.email()
196-
new_user = cast(UserInfo, psg.createUser({"email": email})) # type: ignore[arg-type]
197-
assert new_user.email == email
198-
assert psg.deleteUser(new_user.id)
199-
200-
20157
def test_smart_link_valid() -> None:
20258
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
20359

20460
email = f.email()
20561
magic_link = psg.createMagicLink({"email": email}) # type: ignore[arg-type]
20662
assert magic_link.identifier == email # type: ignore[attr-defined]
20763
assert not magic_link.activated # type: ignore[attr-defined]
208-
209-
210-
def test_sign_out() -> None:
211-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
212-
213-
assert psg.signOut(PASSAGE_USER_ID)
214-
215-
216-
def test_revoke_user_refresh_tokens() -> None:
217-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
218-
219-
assert psg.revokeUserRefreshTokens(PASSAGE_USER_ID)

tests/errors_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
from passageidentity import PassageError
2+
from passageidentity.openapi_client.exceptions import ApiException
3+
4+
5+
class MockApiException(ApiException):
6+
def __init__(self, status: int, body: dict) -> None:
7+
self.status = status
8+
self.body = body
29

310

411
def test_error_with_all_values() -> None:
@@ -17,3 +24,31 @@ def test_error_with_only_message() -> None:
1724
assert error.status_text is None
1825
assert error.error is None
1926
assert error.error_code is None
27+
28+
29+
def test_from_response_error() -> None:
30+
response_error = MockApiException(
31+
status=400,
32+
body={"error": "some error", "code": "some_error_code"},
33+
)
34+
35+
error = PassageError.from_response_error(response_error, "some message")
36+
assert error.message == "some message: some error"
37+
assert error.status_code == 400
38+
assert error.error_code == "some_error_code"
39+
assert error.status_text is None
40+
assert error.error is None
41+
42+
43+
def test_from_response_error_without_message() -> None:
44+
response_error = MockApiException(
45+
status=400,
46+
body={"error": "some error", "code": "some_error_code"},
47+
)
48+
49+
error = PassageError.from_response_error(response_error)
50+
assert error.message == "some error"
51+
assert error.status_code == 400
52+
assert error.error_code == "some_error_code"
53+
assert error.status_text is None
54+
assert error.error is None

0 commit comments

Comments
 (0)