@@ -36,6 +36,47 @@ def addition_app(input: Input) -> Output:
36
36
return Output (result = input .lhs + input .rhs )
37
37
38
38
39
+ @fal .function (
40
+ keep_alive = 0 ,
41
+ requirements = ["fastapi" , "uvicorn" , "pydantic==1.10.12" ],
42
+ machine_type = "S" ,
43
+ max_concurrency = 1 ,
44
+ exposed_port = 8000 ,
45
+ )
46
+ def calculator_app ():
47
+ from fastapi import FastAPI
48
+ from fastapi .middleware .cors import CORSMiddleware
49
+ from uvicorn import run
50
+
51
+ app = FastAPI ()
52
+
53
+ def _wait (wait_time : int ):
54
+ print ("starting..." )
55
+ for _ in range (wait_time ):
56
+ print ("sleeping..." )
57
+ time .sleep (1 )
58
+
59
+ @app .post ("/add" )
60
+ def add (input : Input ) -> Output :
61
+ _wait (input .wait_time )
62
+ return Output (result = input .lhs + input .rhs )
63
+
64
+ @app .post ("/subtract" )
65
+ def subtract (input : Input ) -> Output :
66
+ _wait (input .wait_time )
67
+ return Output (result = input .lhs - input .rhs )
68
+
69
+ app .add_middleware (
70
+ CORSMiddleware ,
71
+ allow_credentials = True ,
72
+ allow_headers = ("*" ),
73
+ allow_methods = ("*" ),
74
+ allow_origins = ("*" ),
75
+ )
76
+
77
+ run (app , host = "0.0.0.0" , port = 8080 )
78
+
79
+
39
80
@pytest .fixture (scope = "module" )
40
81
def aliased_app () -> Generator [tuple [str , str ], None , None ]:
41
82
# Create a temporary app, register it, and return the ID of it.
@@ -67,6 +108,20 @@ def test_app():
67
108
yield f"{ user_id } -{ app_revision } "
68
109
69
110
111
+ @pytest .fixture (scope = "module" )
112
+ def test_fastapi_app ():
113
+ # Create a temporary app, register it, and return the ID of it.
114
+
115
+ from fal .cli import _get_user_id
116
+
117
+ app_revision = calculator_app .host .register (
118
+ func = calculator_app .func ,
119
+ options = calculator_app .options ,
120
+ )
121
+ user_id = _get_user_id ()
122
+ yield f"{ user_id } -{ app_revision } "
123
+
124
+
70
125
def test_app_client (test_app : str ):
71
126
response = apps .run (test_app , arguments = {"lhs" : 1 , "rhs" : 2 })
72
127
assert response ["result" ] == 3
@@ -105,7 +160,7 @@ def test_app_client_async(test_app: str):
105
160
assert result == {"result" : 5 }
106
161
107
162
108
- def test_app_openapi_spec_metadata (test_app : str ):
163
+ def test_app_openapi_spec_metadata (test_app : str , request : pytest . FixtureRequest ):
109
164
user_id , _ , app_id = test_app .partition ("-" )
110
165
res = app_metadata .sync_detailed (
111
166
app_alias_or_id = app_id , app_user_id = user_id , client = REST_CLIENT
@@ -121,6 +176,26 @@ def test_app_openapi_spec_metadata(test_app: str):
121
176
assert key in openapi_spec , f"{ key } key missing from openapi { openapi_spec } "
122
177
123
178
179
+ def test_app_no_serve_spec_metadata (
180
+ test_fastapi_app : str , request : pytest .FixtureRequest
181
+ ):
182
+ # We do not store the openapi spec for apps that do not use serve=True
183
+ user_id , _ , app_id = test_fastapi_app .partition ("-" )
184
+ res = app_metadata .sync_detailed (
185
+ app_alias_or_id = app_id , app_user_id = user_id , client = REST_CLIENT
186
+ )
187
+
188
+ assert (
189
+ res .status_code == 200
190
+ ), f"Failed to fetch metadata for app { test_fastapi_app } "
191
+ assert res .parsed , f"Failed to parse metadata for app { test_fastapi_app } "
192
+
193
+ metadata = res .parsed .to_dict ()
194
+ assert (
195
+ "openapi" not in metadata
196
+ ), f"openapi should not be present in metadata { metadata } "
197
+
198
+
124
199
def test_app_update_app (aliased_app : tuple [str , str ]):
125
200
app_revision , app_alias = aliased_app
126
201
0 commit comments