55verifies that it works as expected.
66"""
77
8+ import re
9+
10+ import pytest
11+ import uvicorn
12+
813from fastapi .testclient import TestClient
914import labthings_fastapi as lt
1015from labthings_fastapi .server .fallback import app
16+ from labthings_fastapi .example_things import ThingThatCantStart
17+
18+ CONFIG_DICT = {
19+ "things" : {
20+ "thing1" : "labthings_fastapi.example_things:MyThing" ,
21+ "thing2" : {
22+ "class" : "labthings_fastapi.example_things:MyThing" ,
23+ "kwargs" : {},
24+ },
25+ }
26+ }
27+
28+
29+ @pytest .fixture (autouse = True )
30+ def reset_app_state ():
31+ """Reset the fallback app state before each fallback test."""
32+ app .labthings_config = None
33+ app .labthings_server = None
34+ app .labthings_error = None
35+ app .log_history = None
36+
37+
38+ def test_fallback_redirect ():
39+ """Test that the redirect works."""
40+ with TestClient (app ) as client :
41+ response = client .get ("/" )
42+ # No history as no redirect
43+ assert len (response .history ) == 0
44+ html = response .text
45+ # test that something when wrong is shown
46+ assert "Something went wrong" in html
47+
48+ # Now try another url
49+ response = client .get ("/foo/bar" )
50+ # redirected so there is a history item showing a 307 Temporary Redirect code.
51+ assert len (response .history ) == 1
52+ assert response .history [0 ].status_code == 307
53+
54+ # Redirects to error page.
55+ html = response .text
56+ # test that something when wrong is shown
57+ assert "Something went wrong" in html
1158
1259
1360def test_fallback_empty ():
@@ -19,14 +66,31 @@ def test_fallback_empty():
1966 assert "No logging info available" in html
2067
2168
22- def test_fallback_with_config ():
23- app .labthings_config = {"hello" : "goodbye" }
69+ def test_fallback_with_config_dict ():
70+ """Check that fallback server prints a config dictionary as JSON."""
71+ app .labthings_config = CONFIG_DICT
72+ with TestClient (app ) as client :
73+ response = client .get ("/" )
74+ html = response .text
75+ assert "Something went wrong" in html
76+ assert "No logging info available" in html
77+ assert '"thing1": "labthings_fastapi.example_things:MyThing"' in html
78+ assert '"class": "labthings_fastapi.example_things:MyThing"' in html
79+
80+
81+ def test_fallback_with_config_obj ():
82+ """Check that fallback server prints the config object as JSON."""
83+ config = lt .ThingServerConfig .model_validate (CONFIG_DICT )
84+ app .labthings_config = config
2485 with TestClient (app ) as client :
2586 response = client .get ("/" )
2687 html = response .text
2788 assert "Something went wrong" in html
2889 assert "No logging info available" in html
29- assert '"hello": "goodbye"' in html
90+ assert "thing1" in html
91+ assert "thing2" in html
92+ cls_regex = re .compile (r'"cls": "labthings_fastapi\.example_things\.MyThing"' )
93+ assert len (cls_regex .findall (html )) == 2
3094
3195
3296def test_fallback_with_error ():
@@ -41,16 +105,7 @@ def test_fallback_with_error():
41105
42106
43107def test_fallback_with_server ():
44- config_dict = {
45- "things" : {
46- "thing1" : "labthings_fastapi.example_things:MyThing" ,
47- "thing2" : {
48- "class" : "labthings_fastapi.example_things:MyThing" ,
49- "kwargs" : {},
50- },
51- }
52- }
53- config = lt .ThingServerConfig .model_validate (config_dict )
108+ config = lt .ThingServerConfig .model_validate (CONFIG_DICT )
54109 app .labthings_server = lt .ThingServer .from_config (config )
55110 with TestClient (app ) as client :
56111 response = client .get ("/" )
@@ -70,3 +125,38 @@ def test_fallback_with_log():
70125 assert "No logging info available" not in html
71126 assert "<p>Logging info</p>" in html
72127 assert "Fake log content" in html
128+
129+
130+ def test_actual_server_fallback ():
131+ """Test that the the server configures its startup failure correctly.
132+
133+ This may want to become an integration test in the fullness of time. Though
134+ the integration test may want to actually let the cli really serve up the
135+ fallback.
136+ """
137+ # ThingThatCantStart has an error in __enter__
138+ server = lt .ThingServer ({"bad_thing" : ThingThatCantStart })
139+
140+ # Starting the server is a SystemExit
141+ with pytest .raises (SystemExit , match = "3" ) as excinfo :
142+ uvicorn .run (server .app , port = 5000 )
143+ server_error = excinfo .value
144+ assert server .startup_failure is not None
145+ assert server .startup_failure ["thing" ] == "bad_thing"
146+ thing_error = server .startup_failure ["exception" ]
147+ assert isinstance (thing_error , RuntimeError )
148+
149+ app .labthings_server = server
150+ app .labthings_error = server_error
151+ with TestClient (app ) as client :
152+ response = client .get ("/" )
153+ html = response .text
154+ assert "Something went wrong" in html
155+ # Shouldn't be displaying the meaningless SystemExit
156+ assert "SystemExit" not in html
157+
158+ # The message from when the Thing errored should be displayed
159+ assert str (thing_error ) in html
160+ # With the traceback
161+ assert 'labthings_fastapi/example_things/__init__.py", line' in html
162+ assert f'RuntimeError("{ thing_error } ")' in html
0 commit comments