Skip to content

Commit dca1ae1

Browse files
committed
Hardened the codebase by introducing type-checking.
1 parent a5acc4f commit dca1ae1

29 files changed

+254
-205
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
cache: 'poetry'
3434

3535
- name: Install Dependencies
36-
run: poetry install
36+
run: poetry install --no-interaction --sync --all-extras
3737

3838
- name: Run Tests
3939
run: poetry run pytest

.github/workflows/typecheck.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Typecheck
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
permissions:
10+
contents: read
11+
12+
defaults:
13+
run:
14+
working-directory: ./
15+
16+
jobs:
17+
steep-check:
18+
runs-on: ubuntu-latest
19+
name: Inspect Code
20+
21+
steps:
22+
- name: Checkout code
23+
uses: actions/checkout@v4
24+
25+
- name: Install poetry
26+
run: |
27+
pipx install poetry
28+
29+
- name: Setup Python
30+
uses: actions/setup-python@v5
31+
with:
32+
python-version-file: 'pyproject.toml'
33+
cache: 'poetry'
34+
35+
- name: Install Dependencies
36+
run: poetry install --no-interaction --sync --all-extras
37+
38+
- name: Run MyPy
39+
run: poetry run mypy

pyproject.toml

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -69,43 +69,23 @@ extension-pkg-whitelist = "pydantic"
6969
[tool.mypy]
7070
files = [
7171
"zitadel_client",
72-
"tests",
72+
"test",
73+
"spec"
7374
]
74-
75-
# List from: https://mypy.readthedocs.io/en/stable/existing_code.html#introduce-stricter-options
76-
warn_unused_configs = true
77-
warn_redundant_casts = true
78-
warn_unused_ignores = true
79-
80-
## Getting these passing should be easy
81-
strict_equality = true
82-
extra_checks = true
83-
84-
## Strongly recommend enabling this one as soon as you can
85-
check_untyped_defs = true
86-
87-
## These shouldn't be too much additional work, but may be tricky to
88-
## get passing if you use a lot of untyped libraries
89-
disallow_subclassing_any = true
90-
disallow_untyped_decorators = true
91-
disallow_any_generics = true
75+
exclude = '(^zitadel_client/models/|^zitadel_client/api/)'
76+
strict = true
77+
implicit_reexport = true
9278

9379
[[tool.mypy.overrides]]
9480
module = [
95-
"zitadel_client.configuration",
81+
"authlib",
82+
"authlib.integrations.requests_client",
83+
"authlib.jose",
84+
"testcontainers.core.container",
85+
"testcontainers"
9686
]
97-
warn_unused_ignores = true
98-
strict_equality = true
99-
extra_checks = true
100-
check_untyped_defs = true
101-
disallow_subclassing_any = true
102-
disallow_untyped_decorators = true
103-
disallow_any_generics = true
104-
disallow_untyped_calls = true
105-
disallow_incomplete_defs = true
106-
disallow_untyped_defs = true
107-
no_implicit_reexport = true
108-
warn_return_any = true
87+
ignore_missing_imports = true
88+
disable_error_code = ["import-untyped"]
10989

11090
[tool.flake8]
11191
max-line-length = 99

spec/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44

55
@pytest.fixture(scope="session", autouse=True)
6-
def load_env():
6+
def load_env() -> None:
77
"""Load the .env file for the entire test session."""
88
load_dotenv()

spec/sdk_test_using_client_credentials_authentication_spec.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,32 @@
88

99

1010
@pytest.fixture
11-
def client_id():
11+
def client_id() -> str | None:
1212
"""Fixture to return a valid personal access token."""
1313
return os.getenv("CLIENT_ID")
1414

1515

1616
@pytest.fixture
17-
def client_secret():
17+
def client_secret() -> str | None:
1818
"""Fixture to return a valid personal access token."""
1919
return os.getenv("CLIENT_SECRET")
2020

2121

2222
@pytest.fixture
23-
def base_url():
23+
def base_url() -> str | None:
2424
"""Fixture to return the base URL."""
2525
return os.getenv("BASE_URL")
2626

2727

2828
@pytest.fixture
29-
def user_id(client_id, client_secret, base_url):
29+
def user_id(client_id: str, client_secret: str, base_url: str) -> str | None:
3030
"""Fixture to create a user and return their ID."""
3131
with zitadel.Zitadel(ClientCredentialsAuthenticator.builder(base_url, client_id, client_secret).build()) as client:
3232
try:
3333
response = client.users.add_human_user(
3434
body=zitadel.models.V2AddHumanUserRequest(
3535
username=uuid.uuid4().hex,
36-
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"),
36+
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"), # type: ignore[call-arg]
3737
email=zitadel.models.V2SetHumanEmail(email=f"johndoe{uuid.uuid4().hex}@caos.ag")
3838
)
3939
)
@@ -43,7 +43,7 @@ def user_id(client_id, client_secret, base_url):
4343
pytest.fail(f"Exception while creating user: {e}")
4444

4545

46-
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, client_id, client_secret, base_url):
46+
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id: str, client_id: str, client_secret: str, base_url: str) -> None:
4747
"""Test to (de)activate the user with a valid token."""
4848
with zitadel.Zitadel(ClientCredentialsAuthenticator.builder(base_url, client_id, client_secret).build()) as client:
4949
try:
@@ -58,7 +58,7 @@ def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, client_
5858
pytest.fail(f"Exception when calling deactivate_user or reactivate_user with valid token: {e}")
5959

6060

61-
def test_should_not_deactivate_or_reactivate_user_with_invalid_token(user_id, base_url):
61+
def test_should_not_deactivate_or_reactivate_user_with_invalid_token(user_id: str, base_url: str) -> None:
6262
"""Test to attempt (de)activating the user with an invalid token."""
6363
with zitadel.Zitadel(ClientCredentialsAuthenticator.builder(base_url, "id", "secret").build()) as client:
6464
try:

spec/sdk_test_using_personal_access_token_authentication_spec.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,32 @@
99

1010

1111
@pytest.fixture
12-
def valid_token():
12+
def valid_token() -> str | None:
1313
"""Fixture to return a valid personal access token."""
1414
return os.getenv("AUTH_TOKEN")
1515

1616

1717
@pytest.fixture
18-
def invalid_token():
18+
def invalid_token() -> str | None:
1919
"""Fixture to return an invalid token."""
2020
return "whoops"
2121

2222

2323
@pytest.fixture
24-
def base_url():
24+
def base_url() -> str | None:
2525
"""Fixture to return the base URL."""
2626
return os.getenv("BASE_URL")
2727

2828

2929
@pytest.fixture
30-
def user_id(valid_token, base_url):
30+
def user_id(valid_token: str, base_url: str) -> str | None:
3131
"""Fixture to create a user and return their ID."""
3232
with zitadel.Zitadel(PersonalAccessTokenAuthenticator(base_url, valid_token)) as client:
3333
try:
3434
response = client.users.add_human_user(
3535
body=zitadel.models.V2AddHumanUserRequest(
3636
username=uuid.uuid4().hex,
37-
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"),
37+
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"), # type: ignore[call-arg]
3838
email=zitadel.models.V2SetHumanEmail(email=f"johndoe{uuid.uuid4().hex}@caos.ag")
3939
)
4040
)
@@ -44,7 +44,7 @@ def user_id(valid_token, base_url):
4444
pytest.fail(f"Exception while creating user: {e}")
4545

4646

47-
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, valid_token, base_url):
47+
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id: str, valid_token: str, base_url: str) -> None:
4848
"""Test to (de)activate the user with a valid token."""
4949
with zitadel.Zitadel(PersonalAccessTokenAuthenticator(base_url, valid_token)) as client:
5050
try:
@@ -59,7 +59,7 @@ def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, valid_t
5959
pytest.fail(f"Exception when calling deactivate_user or reactivate_user with valid token: {e}")
6060

6161

62-
def test_should_not_deactivate_or_reactivate_user_with_invalid_token(user_id, invalid_token, base_url):
62+
def test_should_not_deactivate_or_reactivate_user_with_invalid_token(user_id: str, invalid_token: str, base_url: str) -> None:
6363
"""Test to attempt (de)activating the user with an invalid token."""
6464
with zitadel.Zitadel(PersonalAccessTokenAuthenticator(base_url, invalid_token)) as client:
6565
try:

spec/sdk_test_using_web_token_authentication_spec.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
@pytest.fixture
12-
def key_file():
12+
def key_file() -> str:
1313
jwt_key = os.getenv("JWT_KEY")
1414
if jwt_key is None:
1515
pytest.fail("JWT_KEY is not set in the environment")
@@ -19,20 +19,20 @@ def key_file():
1919

2020

2121
@pytest.fixture
22-
def base_url():
22+
def base_url() -> str | None:
2323
"""Fixture to return the base URL."""
2424
return os.getenv("BASE_URL")
2525

2626

2727
@pytest.fixture
28-
def user_id(key_file, base_url):
28+
def user_id(key_file: str, base_url: str) -> str | None:
2929
"""Fixture to create a user and return their ID."""
3030
with zitadel.Zitadel(WebTokenAuthenticator.from_json(base_url, key_file)) as client:
3131
try:
3232
response = client.users.add_human_user(
3333
body=zitadel.models.V2AddHumanUserRequest(
3434
username=uuid.uuid4().hex,
35-
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"),
35+
profile=zitadel.models.V2SetHumanProfile(given_name="John", family_name="Doe"), # type: ignore[call-arg]
3636
email=zitadel.models.V2SetHumanEmail(email=f"johndoe{uuid.uuid4().hex}@caos.ag")
3737
)
3838
)
@@ -42,7 +42,7 @@ def user_id(key_file, base_url):
4242
pytest.fail(f"Exception while creating user: {e}")
4343

4444

45-
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id, key_file, base_url):
45+
def test_should_deactivate_and_reactivate_user_with_valid_token(user_id: str, key_file: str, base_url: str) -> None:
4646
"""Test to (de)activate the user with a valid token."""
4747
with zitadel.Zitadel(WebTokenAuthenticator.from_json(base_url, key_file)) as client:
4848
try:

test/auth/test_client_credentials_authenticator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ class ClientCredentialsAuthenticatorTest(OAuthAuthenticatorTest):
1111
Extends the base OAuthAuthenticatorTest class.
1212
"""
1313

14-
def test_refresh_token(self):
14+
def test_refresh_token(self) -> None:
1515
time.sleep(20)
1616

17+
assert self.oauth_host is not None
1718
authenticator = ClientCredentialsAuthenticator.builder(self.oauth_host, "dummy-client", "dummy-secret") \
1819
.scopes("openid", "foo") \
1920
.build()

test/auth/test_no_auth_authenticator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55

66
class NoAuthAuthenticatorTest(unittest.TestCase):
7-
def test_returns_empty_headers_and_default_host(self):
7+
def test_returns_empty_headers_and_default_host(self) -> None:
88
auth = NoAuthAuthenticator()
99
self.assertEqual({}, auth.get_auth_headers())
1010
self.assertEqual("http://localhost", auth.get_host())
1111

12-
def test_returns_empty_headers_and_custom_host(self):
12+
def test_returns_empty_headers_and_custom_host(self) -> None:
1313
auth = NoAuthAuthenticator("https://custom-host")
1414
self.assertEqual({}, auth.get_auth_headers())
1515
self.assertEqual("https://custom-host", auth.get_host())

test/auth/test_oauth_authenticator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ class OAuthAuthenticatorTest(unittest.TestCase):
1414
The container is configured to wait for an HTTP response from the "/" endpoint
1515
with a status code of 405, using HttpWaitStrategy.
1616
"""
17-
oauth_host: str = None
17+
oauth_host: str | None = None
1818
mock_oauth2_server: DockerContainer = None
1919

2020
@classmethod
21-
def setUpClass(cls):
21+
def setUpClass(cls) -> None:
2222
cls.mock_oauth2_server = DockerContainer("ghcr.io/navikt/mock-oauth2-server:2.1.10") \
2323
.with_exposed_ports(8080)
2424
cls.mock_oauth2_server.start()
@@ -27,6 +27,6 @@ def setUpClass(cls):
2727
cls.oauth_host = f"http://{host}:{port}"
2828

2929
@classmethod
30-
def tearDownClass(cls):
30+
def tearDownClass(cls) -> None:
3131
if cls.mock_oauth2_server is not None:
3232
cls.mock_oauth2_server.stop()

0 commit comments

Comments
 (0)