create-caspian-app 1.5.0 → 1.5.2
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.
|
@@ -1,13 +1,76 @@
|
|
|
1
|
-
"""Unit tests for
|
|
1
|
+
"""Unit tests for focused helper and lifecycle functions in `main.py`.
|
|
2
2
|
|
|
3
3
|
These cover the app-owned logic that has no external dependencies: env
|
|
4
4
|
parsing, query-param coercion, and the component-deferral HTML transform.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import inspect
|
|
8
|
-
|
|
8
|
+
import io
|
|
9
|
+
from dataclasses import replace
|
|
10
|
+
from types import SimpleNamespace
|
|
11
|
+
from typing import Optional, cast
|
|
12
|
+
from unittest.mock import AsyncMock
|
|
9
13
|
|
|
10
14
|
import main
|
|
15
|
+
from conftest import run_async
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestPrismaLifespan:
|
|
19
|
+
def test_disconnects_on_shutdown(self, monkeypatch):
|
|
20
|
+
disconnect = AsyncMock()
|
|
21
|
+
monkeypatch.setattr(main.prisma, "disconnect", disconnect)
|
|
22
|
+
|
|
23
|
+
async def exercise_lifespan():
|
|
24
|
+
async with main.prisma_lifespan(main.app):
|
|
25
|
+
disconnect.assert_not_awaited()
|
|
26
|
+
|
|
27
|
+
run_async(exercise_lifespan())
|
|
28
|
+
disconnect.assert_awaited_once_with()
|
|
29
|
+
|
|
30
|
+
def test_disabled_prisma_is_not_registered(self, monkeypatch):
|
|
31
|
+
monkeypatch.setattr(main, "cfg", replace(main.cfg, prisma=False))
|
|
32
|
+
|
|
33
|
+
assert main.prisma_lifespan not in main.get_app_lifespans()
|
|
34
|
+
|
|
35
|
+
def test_disconnects_when_another_lifespan_raises(self, monkeypatch):
|
|
36
|
+
disconnect = AsyncMock()
|
|
37
|
+
monkeypatch.setattr(main.prisma, "disconnect", disconnect)
|
|
38
|
+
|
|
39
|
+
async def exercise_lifespan():
|
|
40
|
+
try:
|
|
41
|
+
async with main.prisma_lifespan(main.app):
|
|
42
|
+
raise RuntimeError("shutdown path")
|
|
43
|
+
except RuntimeError:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
run_async(exercise_lifespan())
|
|
47
|
+
disconnect.assert_awaited_once_with()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestDevControlPipe:
|
|
51
|
+
def test_valid_shutdown_command_stops_server(self):
|
|
52
|
+
server = cast(main.uvicorn.Server, SimpleNamespace(should_exit=False))
|
|
53
|
+
|
|
54
|
+
main._consume_dev_control_stream(
|
|
55
|
+
server,
|
|
56
|
+
"expected-token",
|
|
57
|
+
io.StringIO("shutdown:expected-token\n"),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
assert server.should_exit is True
|
|
61
|
+
|
|
62
|
+
def test_ignores_invalid_commands_until_pipe_closes(self):
|
|
63
|
+
server = cast(main.uvicorn.Server, SimpleNamespace(should_exit=False))
|
|
64
|
+
|
|
65
|
+
main._consume_dev_control_stream(
|
|
66
|
+
server,
|
|
67
|
+
"expected-token",
|
|
68
|
+
io.StringIO("shutdown:wrong-token\nnoop:expected-token\n"),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# EOF means the owning development orchestrator exited, so an otherwise
|
|
72
|
+
# orphaned child still shuts down cleanly.
|
|
73
|
+
assert server.should_exit is True
|
|
11
74
|
|
|
12
75
|
|
|
13
76
|
class TestEnvParsing:
|