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

Commit 62e2fc7

Browse files
committed
test: organizes test by class and adds one for mapping passage error
1 parent 464f18d commit 62e2fc7

File tree

4 files changed

+218
-166
lines changed

4 files changed

+218
-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: 1 addition & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@
88
from passageidentity import PassageError
99
from passageidentity.openapi_client.models.app_info import AppInfo
1010
from passageidentity.openapi_client.models.magic_link import MagicLink
11-
from passageidentity.openapi_client.models.update_user_request import UpdateUserRequest
12-
from passageidentity.openapi_client.models.user_info import UserInfo
1311
from passageidentity.passage import Passage
1412

1513
load_dotenv()
1614
f = Faker()
15+
1716
PASSAGE_USER_ID = os.environ.get("PASSAGE_USER_ID") or ""
1817
PASSAGE_APP_ID = os.environ.get("PASSAGE_APP_ID") or ""
1918
PASSAGE_API_KEY = os.environ.get("PASSAGE_API_KEY") or ""
@@ -57,164 +56,10 @@ def test_create_magic_link() -> None:
5756
assert magic_link.ttl == 12 # type: ignore[attr-defined]
5857

5958

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

20562
email = f.email()
20663
magic_link = cast(MagicLink, psg.createMagicLink({"email": email})) # type: ignore[arg-type]
20764
assert magic_link.identifier == email
20865
assert not magic_link.activated
209-
210-
211-
def test_sign_out() -> None:
212-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
213-
214-
assert psg.signOut(PASSAGE_USER_ID)
215-
216-
217-
def test_revoke_user_refresh_tokens() -> None:
218-
psg = Passage(PASSAGE_APP_ID, PASSAGE_API_KEY)
219-
220-
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)