Skip to content

Commit 539f028

Browse files
committed
Add tests for large_file_threshold
1 parent dccea9b commit 539f028

2 files changed

Lines changed: 179 additions & 0 deletions

File tree

tests/test_cli_deploy.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,3 +2194,128 @@ def test_ctrl_c_during_build_streaming_shows_cancelled(
21942194

21952195
assert "🟡" in result.output
21962196
assert "Cancelled." in result.output
2197+
2198+
2199+
def _create_file(path: Path, size_bytes: int) -> None:
2200+
"""Create a file of the given size."""
2201+
path.parent.mkdir(parents=True, exist_ok=True)
2202+
with open(path, "wb") as f:
2203+
if size_bytes > 0:
2204+
f.seek(size_bytes - 1)
2205+
f.write(b"\0")
2206+
2207+
2208+
@pytest.mark.respx
2209+
def test_large_file_threshold_warning(
2210+
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
2211+
) -> None:
2212+
app_data = _get_random_app()
2213+
app_id = app_data["id"]
2214+
team_id = "some-team-id"
2215+
deployment_data = _get_random_deployment(app_id=app_id)
2216+
2217+
_setup_deployment_mocks(
2218+
respx_mock, app_id, team_id, deployment_data, tmp_path
2219+
)
2220+
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2221+
return_value=Response(200, json={**deployment_data, "status": "success"})
2222+
)
2223+
2224+
_create_file(tmp_path / "model.bin", 12 * 1024 * 1024) # 12 MB
2225+
_create_file(tmp_path / "data.csv", 11 * 1024 * 1024) # 11 MB
2226+
2227+
with changing_dir(tmp_path):
2228+
result = runner.invoke(app, ["deploy"])
2229+
2230+
assert result.exit_code == 0
2231+
assert "Some uploaded files are larger than 10 MB" in result.output
2232+
assert "model.bin" in result.output
2233+
assert "12 MB" in result.output
2234+
assert "data.csv" in result.output
2235+
assert "11 MB" in result.output
2236+
2237+
2238+
@pytest.mark.respx
2239+
def test_large_file_threshold_only_top_three_files_with_more_indicator(
2240+
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
2241+
) -> None:
2242+
app_data = _get_random_app()
2243+
app_id = app_data["id"]
2244+
team_id = "some-team-id"
2245+
deployment_data = _get_random_deployment(app_id=app_id)
2246+
2247+
_setup_deployment_mocks(
2248+
respx_mock, app_id, team_id, deployment_data, tmp_path
2249+
)
2250+
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2251+
return_value=Response(200, json={**deployment_data, "status": "success"})
2252+
)
2253+
2254+
_create_file(tmp_path / "huge.bin", 50 * 1024 * 1024)
2255+
_create_file(tmp_path / "big.bin", 40 * 1024 * 1024)
2256+
_create_file(tmp_path / "medium.bin", 30 * 1024 * 1024)
2257+
_create_file(tmp_path / "smaller.bin", 20 * 1024 * 1024)
2258+
_create_file(tmp_path / "smallest.bin", 15 * 1024 * 1024)
2259+
2260+
with changing_dir(tmp_path):
2261+
result = runner.invoke(app, ["deploy"])
2262+
2263+
assert result.exit_code == 0
2264+
assert "huge.bin" in result.output
2265+
assert "big.bin" in result.output
2266+
assert "medium.bin" in result.output
2267+
assert "smaller.bin" not in result.output
2268+
assert "smallest.bin" not in result.output
2269+
assert "...and 2 more" in result.output
2270+
2271+
2272+
@pytest.mark.respx
2273+
def test_large_file_threshold_does_not_warn_when_no_large_files(
2274+
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
2275+
) -> None:
2276+
app_data = _get_random_app()
2277+
app_id = app_data["id"]
2278+
team_id = "some-team-id"
2279+
deployment_data = _get_random_deployment(app_id=app_id)
2280+
2281+
_setup_deployment_mocks(
2282+
respx_mock, app_id, team_id, deployment_data, tmp_path
2283+
)
2284+
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2285+
return_value=Response(200, json={**deployment_data, "status": "success"})
2286+
)
2287+
2288+
(tmp_path / "main.py").write_text("print('hello')")
2289+
2290+
with changing_dir(tmp_path):
2291+
result = runner.invoke(app, ["deploy"])
2292+
2293+
assert result.exit_code == 0
2294+
assert "Some uploaded files are larger than" not in result.output
2295+
2296+
2297+
@pytest.mark.respx
2298+
def test_large_file_threshold_custom_threshold(
2299+
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
2300+
) -> None:
2301+
app_data = _get_random_app()
2302+
app_id = app_data["id"]
2303+
team_id = "some-team-id"
2304+
deployment_data = _get_random_deployment(app_id=app_id)
2305+
2306+
_setup_deployment_mocks(
2307+
respx_mock, app_id, team_id, deployment_data, tmp_path
2308+
)
2309+
respx_mock.get(f"/apps/{app_id}/deployments/{deployment_data['id']}").mock(
2310+
return_value=Response(200, json={**deployment_data, "status": "success"})
2311+
)
2312+
2313+
# 5 MB file: above a 1 MB threshold, below the default 10 MB threshold
2314+
_create_file(tmp_path / "data.bin", 5 * 1024 * 1024)
2315+
2316+
with changing_dir(tmp_path):
2317+
result = runner.invoke(app, ["deploy", "--large-file-threshold", "1"])
2318+
2319+
assert result.exit_code == 0
2320+
assert "Some uploaded files are larger than 1 MB" in result.output
2321+
assert "data.bin" in result.output

tests/test_deploy_utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,21 @@
33
import pytest
44

55
from fastapi_cloud_cli.commands.deploy import (
6+
_get_large_files,
67
_should_exclude_entry,
78
validate_app_directory,
89
)
910
from fastapi_cloud_cli.utils.api import DeploymentStatus
1011

1112

13+
def _create_file(path: Path, size_bytes: int) -> None:
14+
path.parent.mkdir(parents=True, exist_ok=True)
15+
with open(path, "wb") as f:
16+
if size_bytes > 0:
17+
f.seek(size_bytes - 1)
18+
f.write(b"\0")
19+
20+
1221
@pytest.mark.parametrize(
1322
"path",
1423
[
@@ -135,3 +144,48 @@ def test_validate_app_directory_invalid(value: str, expected_message: str) -> No
135144
validate_app_directory(value)
136145

137146
assert str(exc_info.value) == expected_message
147+
148+
149+
def test_get_large_files_no_files_above_threshold(tmp_path: Path) -> None:
150+
"""Should not return files smaller than the threshold."""
151+
_create_file(tmp_path / "small.bin", 512 * 1024) # 0.5 MB
152+
153+
assert _get_large_files(tmp_path, threshold_mb=1) == []
154+
155+
156+
def test_get_large_files_returns_files_at_or_above_threshold(tmp_path: Path) -> None:
157+
"""Should return files at or above the threshold with sizes and relative paths."""
158+
_create_file(tmp_path / "big.bin", 2 * 1024 * 1024) # 2 MB
159+
_create_file(tmp_path / "subdir" / "huge.bin", 5 * 1024 * 1024) # 5 MB
160+
_create_file(tmp_path / "small.bin", 100 * 1024) # 0.1 MB
161+
162+
result = _get_large_files(tmp_path, threshold_mb=1)
163+
164+
assert sorted(result, key=lambda x: x[1]) == [
165+
(Path("big.bin"), 2 * 1024 * 1024),
166+
(Path("subdir") / "huge.bin", 5 * 1024 * 1024),
167+
]
168+
169+
170+
def test_get_large_files_excludes_default_exclusions(tmp_path: Path) -> None:
171+
"""Should not count files in excluded directories like .venv or __pycache__."""
172+
_create_file(tmp_path / ".venv" / "lib" / "huge.so", 5 * 1024 * 1024)
173+
_create_file(
174+
tmp_path / "__pycache__" / "module.cpython-311.pyc", 5 * 1024 * 1024
175+
)
176+
_create_file(tmp_path / "main.py", 5 * 1024 * 1024)
177+
178+
assert _get_large_files(tmp_path, threshold_mb=1) == [
179+
(Path("main.py"), 5 * 1024 * 1024)
180+
]
181+
182+
183+
def test_get_large_files_respects_fastapicloudignore(tmp_path: Path) -> None:
184+
"""Should not count files matching .fastapicloudignore patterns."""
185+
_create_file(tmp_path / "data" / "huge.bin", 5 * 1024 * 1024)
186+
_create_file(tmp_path / "main.bin", 5 * 1024 * 1024)
187+
(tmp_path / ".fastapicloudignore").write_text("data/\n")
188+
189+
assert _get_large_files(tmp_path, threshold_mb=1) == [
190+
(Path("main.bin"), 5 * 1024 * 1024)
191+
]

0 commit comments

Comments
 (0)