simple-module-test 0.0.38__tar.gz → 0.0.39__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/PKG-INFO +8 -8
  2. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/pyproject.toml +8 -8
  3. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/fixtures.py +3 -0
  4. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/mail.py +2 -11
  5. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/plugin.py +2 -1
  6. simple_module_test-0.0.39/simple_module_test/process_state.py +24 -0
  7. simple_module_test-0.0.39/tests/test_testing_process_state.py +94 -0
  8. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/.gitignore +0 -0
  9. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/LICENSE +0 -0
  10. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/README.md +0 -0
  11. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/__init__.py +0 -0
  12. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/_schema.py +0 -0
  13. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/app_factory.py +0 -0
  14. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/database.py +0 -0
  15. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/fake_events.py +0 -0
  16. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/redis_server.py +0 -0
  17. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/routes.py +0 -0
  18. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/session_cookie.py +0 -0
  19. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/setup_wizard.py +0 -0
  20. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/tenant_client.py +0 -0
  21. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/simple_module_test/waiting.py +0 -0
  22. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/tests/test_app_factory.py +0 -0
  23. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/tests/test_fake_events.py +0 -0
  24. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/tests/test_plugin_fixtures.py +0 -0
  25. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/tests/test_schema_reuse_pg.py +0 -0
  26. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/tests/test_waiting_helpers.py +0 -0
  27. {simple_module_test-0.0.38 → simple_module_test-0.0.39}/tests/test_worker_database.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simple_module_test
3
- Version: 0.0.38
3
+ Version: 0.0.39
4
4
  Summary: Shared pytest fixtures (app, client, db_session, authenticated_client) for writing simple_module module tests
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -22,13 +22,13 @@ Classifier: Topic :: Software Development :: Testing
22
22
  Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.12
24
24
  Requires-Dist: fastapi<0.157,>=0.140
25
- Requires-Dist: httpx>=0.27
26
- Requires-Dist: pytest-asyncio>=0.24
27
- Requires-Dist: pytest>=8.0
28
- Requires-Dist: simple-module-core==0.0.38
29
- Requires-Dist: simple-module-db==0.0.38
30
- Requires-Dist: simple-module-hosting==0.0.38
31
- Requires-Dist: sqlalchemy>=2.0
25
+ Requires-Dist: httpx<0.29,>=0.27
26
+ Requires-Dist: pytest-asyncio<2,>=0.24
27
+ Requires-Dist: pytest<10,>=8.0
28
+ Requires-Dist: simple-module-core==0.0.39
29
+ Requires-Dist: simple-module-db==0.0.39
30
+ Requires-Dist: simple-module-hosting==0.0.39
31
+ Requires-Dist: sqlalchemy<3,>=2.0
32
32
  Description-Content-Type: text/markdown
33
33
 
34
34
  # simple_module_test
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_test"
3
- version = "0.0.38"
3
+ version = "0.0.39"
4
4
  description = "Shared pytest fixtures (app, client, db_session, authenticated_client) for writing simple_module module tests"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -22,13 +22,13 @@ classifiers = [
22
22
  ]
23
23
  dependencies = [
24
24
  "fastapi>=0.140,<0.157",
25
- "httpx>=0.27",
26
- "pytest>=8.0",
27
- "pytest-asyncio>=0.24",
28
- "simple_module_core==0.0.38",
29
- "simple_module_db==0.0.38",
30
- "simple_module_hosting==0.0.38",
31
- "sqlalchemy>=2.0",
25
+ "httpx>=0.27,<0.29",
26
+ "pytest>=8.0,<10",
27
+ "pytest-asyncio>=0.24,<2",
28
+ "simple_module_core==0.0.39",
29
+ "simple_module_db==0.0.39",
30
+ "simple_module_hosting==0.0.39",
31
+ "sqlalchemy>=2.0,<3",
32
32
  ]
33
33
 
34
34
  [project.entry-points.pytest11]
@@ -161,6 +161,9 @@ async def _seed_setup_admin(application) -> None:
161
161
  async def _build_app(settings: Settings, *, seed_admin: bool):
162
162
  from simple_module_hosting.app_builder import create_app
163
163
 
164
+ # Process-wide state is reset per test by the autouse fixture in
165
+ # ``process_state``, not here: a second app built inside one test must not
166
+ # reset the live first app's mail sender or lease backend.
164
167
  application = create_app(settings)
165
168
 
166
169
  await _create_all_tables(application.state.sm.db.engine)
@@ -10,8 +10,7 @@ from __future__ import annotations
10
10
  from collections.abc import Iterator
11
11
 
12
12
  import pytest
13
- from simple_module_core import mail as core_mail
14
- from simple_module_core.mail import MailMessage
13
+ from simple_module_core.mail import MailMessage, install_mail_sender
15
14
 
16
15
 
17
16
  class CapturedMail:
@@ -28,14 +27,6 @@ class CapturedMail:
28
27
  return [m for m in self.messages if m.to == address]
29
28
 
30
29
 
31
- @pytest.fixture(autouse=True)
32
- def _reset_process_mail_sender() -> Iterator[None]:
33
- sender, resolver = core_mail._sender, core_mail._resolver
34
- yield
35
- core_mail.install_mail_sender(sender)
36
- core_mail.install_mail_resolver(resolver)
37
-
38
-
39
30
  @pytest.fixture
40
31
  def captured_mail() -> Iterator[CapturedMail]:
41
32
  """Install a recording sender that claims to deliver; yields it.
@@ -45,5 +36,5 @@ def captured_mail() -> Iterator[CapturedMail]:
45
36
  reinstalls the real one; reinstall with ``install_mail_sender(captured)``.
46
37
  """
47
38
  captured = CapturedMail()
48
- core_mail.install_mail_sender(captured)
39
+ install_mail_sender(captured)
49
40
  yield captured
@@ -33,7 +33,8 @@ from simple_module_test.fixtures import ( # noqa: F401
33
33
  settings,
34
34
  setup_pending_app,
35
35
  )
36
- from simple_module_test.mail import _reset_process_mail_sender, captured_mail # noqa: F401
36
+ from simple_module_test.mail import captured_mail # noqa: F401
37
+ from simple_module_test.process_state import _reset_process_state # noqa: F401
37
38
  from simple_module_test.redis_server import redis_server # noqa: F401
38
39
  from simple_module_test.tenant_client import tenant_client # noqa: F401
39
40
 
@@ -0,0 +1,24 @@
1
+ """Reset process services between tests and apps built by the test plugin."""
2
+
3
+ from collections.abc import Iterator
4
+
5
+ import pytest
6
+ from simple_module_core.leases import reset_lease_backend
7
+ from simple_module_core.mail import reset_mail_sender
8
+ from simple_module_hosting.sentry import reset_sentry
9
+
10
+
11
+ def reset_process_state() -> None:
12
+ """Restore unconfigured services without retaining another app's resources."""
13
+ reset_mail_sender()
14
+ reset_lease_backend()
15
+ reset_sentry()
16
+
17
+
18
+ @pytest.fixture(autouse=True)
19
+ def _reset_process_state() -> Iterator[None]:
20
+ reset_process_state()
21
+ try:
22
+ yield
23
+ finally:
24
+ reset_process_state()
@@ -0,0 +1,94 @@
1
+ """Process services are isolated between plugin-built apps and tests."""
2
+
3
+ from unittest.mock import Mock
4
+
5
+ from simple_module_core.leases import (
6
+ InProcessLeaseBackend,
7
+ configure_leases,
8
+ get_leases,
9
+ reset_lease_backend,
10
+ )
11
+ from simple_module_core.mail import (
12
+ ConsoleMailSender,
13
+ current_mail_sender,
14
+ install_mail_resolver,
15
+ install_mail_sender,
16
+ mail_transport_installed,
17
+ reset_mail_sender,
18
+ resolve_mail_sender,
19
+ )
20
+ from simple_module_hosting import sentry
21
+ from simple_module_test.mail import CapturedMail
22
+ from simple_module_test.process_state import reset_process_state
23
+
24
+
25
+ async def test_reset_mail_clears_sender_and_worker_resolver() -> None:
26
+ sender = CapturedMail()
27
+
28
+ async def resolver(db):
29
+ return sender
30
+
31
+ install_mail_sender(sender)
32
+ install_mail_resolver(resolver)
33
+ reset_mail_sender()
34
+ assert not mail_transport_installed()
35
+ assert isinstance(current_mail_sender(), ConsoleMailSender)
36
+ assert isinstance(await resolve_mail_sender(object()), ConsoleMailSender)
37
+ assert current_mail_sender().include_body is False
38
+
39
+
40
+ async def test_reset_leases_discards_configuration_and_held_names() -> None:
41
+ original = InProcessLeaseBackend()
42
+ configure_leases(original)
43
+ assert await original.acquire("held", "previous-app", 60) == 1
44
+ reset_lease_backend()
45
+ replacement = get_leases()
46
+ assert replacement is not original
47
+ assert isinstance(replacement, InProcessLeaseBackend)
48
+ assert await replacement.acquire("held", "next-app", 60) == 1
49
+
50
+
51
+ def test_reset_sentry_closes_and_unbinds_client(monkeypatch) -> None:
52
+ import pytest
53
+
54
+ sdk = pytest.importorskip("sentry_sdk")
55
+ scope = Mock()
56
+ monkeypatch.setattr(sdk, "get_global_scope", lambda: scope)
57
+ monkeypatch.setattr(sentry, "_enabled", True)
58
+ sentry._request_scope.set({"type": "http"})
59
+ sentry.reset_sentry()
60
+ scope.client.close.assert_called_once_with()
61
+ scope.set_client.assert_called_once_with(None)
62
+ assert not sentry.is_enabled()
63
+ assert sentry._request_scope.get() is None
64
+ sentry.reset_sentry()
65
+ scope.client.close.assert_called_once_with()
66
+
67
+
68
+ async def test_plugin_second_app_keeps_first_apps_services(settings, monkeypatch) -> None:
69
+ """Resets are per test (autouse fixture), so a second app in one test must not
70
+ wipe the first app's installed mail sender."""
71
+ from fastapi import FastAPI
72
+ from simple_module_hosting import app_builder
73
+ from simple_module_test import fixtures
74
+
75
+ def create_app(settings):
76
+ app = FastAPI()
77
+ app.state.sm = Mock()
78
+ return app
79
+
80
+ async def create_tables(engine):
81
+ pass
82
+
83
+ monkeypatch.setattr(app_builder, "create_app", create_app)
84
+ monkeypatch.setattr(fixtures, "_create_all_tables", create_tables)
85
+ install_mail_sender(CapturedMail())
86
+ async for _app in fixtures._build_app(settings, seed_admin=False):
87
+ assert mail_transport_installed()
88
+ reset_process_state()
89
+
90
+
91
+ def test_autouse_reset_clears_state_left_by_a_previous_test() -> None:
92
+ # The autouse ``_reset_process_state`` fixture ran before this test.
93
+ assert not mail_transport_installed()
94
+ assert not sentry.is_enabled()