|
1 | 1 | import random |
| 2 | +import re |
2 | 3 | import string |
3 | 4 | from datetime import timedelta |
4 | 5 | from pathlib import Path |
|
8 | 9 | import httpx |
9 | 10 | import pytest |
10 | 11 | import respx |
| 12 | +import rich_toolkit |
11 | 13 | from click.testing import Result |
12 | 14 | from httpx import Response |
13 | 15 | from time_machine import TimeMachineFixture |
@@ -1578,6 +1580,101 @@ def test_deploy_with_token_fails( |
1578 | 1580 | ) |
1579 | 1581 |
|
1580 | 1582 |
|
| 1583 | +@pytest.mark.parametrize( |
| 1584 | + ("size", "expected_msgs"), |
| 1585 | + [ |
| 1586 | + ( |
| 1587 | + 100, |
| 1588 | + [ |
| 1589 | + r"\(\d+ bytes\)", # e.g. "(123 bytes)" |
| 1590 | + r"\(\d+ bytes of \d+ bytes\)", # e.g. "(123 bytes of 456 bytes)" |
| 1591 | + ], |
| 1592 | + ), |
| 1593 | + ( |
| 1594 | + 10 * 1024, |
| 1595 | + [ |
| 1596 | + r"\(\d+\.\d+ KB\)", # e.g. "(1.23 KB)" |
| 1597 | + r"\(\d+\.\d+ KB of \d+\.\d+ KB\)", # e.g. "(1.23 KB of 4.56 KB)" |
| 1598 | + ], |
| 1599 | + ), |
| 1600 | + ( |
| 1601 | + 10 * 1024 * 1024, |
| 1602 | + [ |
| 1603 | + r"\(\d+\.\d+ MB\)", # e.g. "(1.23 MB)" |
| 1604 | + r"\(\d+\.\d+ KB of \d+\.\d+ MB\)", # e.g. "(1.23 KB of 4.56 MB)" |
| 1605 | + r"\(\d+\.\d+ MB of \d+\.\d+ MB\)", # e.g. "(1.23 MB of 4.56 MB)" |
| 1606 | + ], |
| 1607 | + ), |
| 1608 | + ], |
| 1609 | +) |
| 1610 | +@pytest.mark.respx |
| 1611 | +def test_upload_deployment_progress( |
| 1612 | + logged_in_cli: None, |
| 1613 | + tmp_path: Path, |
| 1614 | + respx_mock: respx.MockRouter, |
| 1615 | + size: int, |
| 1616 | + expected_msgs: list[str], |
| 1617 | +) -> None: |
| 1618 | + app_data = _get_random_app() |
| 1619 | + team_data = _get_random_team() |
| 1620 | + app_id = app_data["id"] |
| 1621 | + team_id = team_data["id"] |
| 1622 | + deployment_data = _get_random_deployment(app_id=app_id) |
| 1623 | + deployment_id = deployment_data["id"] |
| 1624 | + |
| 1625 | + config_path = tmp_path / ".fastapicloud" / "cloud.json" |
| 1626 | + config_path.parent.mkdir(parents=True, exist_ok=True) |
| 1627 | + config_path.write_text(f'{{"app_id": "{app_id}", "team_id": "{team_id}"}}') |
| 1628 | + |
| 1629 | + (tmp_path / "file.bin").write_bytes(random.randbytes(size)) |
| 1630 | + |
| 1631 | + respx_mock.get(f"/apps/{app_id}").mock(return_value=Response(200, json=app_data)) |
| 1632 | + respx_mock.post(f"/apps/{app_id}/deployments/").mock( |
| 1633 | + return_value=Response(201, json=deployment_data) |
| 1634 | + ) |
| 1635 | + respx_mock.post(f"/deployments/{deployment_id}/upload").mock( |
| 1636 | + return_value=Response( |
| 1637 | + 200, |
| 1638 | + json={"url": "http://test.com", "fields": {"key": "value"}}, |
| 1639 | + ) |
| 1640 | + ) |
| 1641 | + respx_mock.post("http://test.com", data={"key": "value"}).mock( |
| 1642 | + return_value=Response(200) |
| 1643 | + ) |
| 1644 | + respx_mock.post(f"/deployments/{deployment_id}/upload-complete").mock( |
| 1645 | + return_value=Response(200) |
| 1646 | + ) |
| 1647 | + respx_mock.get(f"/deployments/{deployment_id}/build-logs").mock( |
| 1648 | + return_value=Response( |
| 1649 | + 200, |
| 1650 | + content=build_logs_response( |
| 1651 | + {"type": "message", "message": "Building...", "id": "1"}, |
| 1652 | + {"type": "complete"}, |
| 1653 | + ), |
| 1654 | + ) |
| 1655 | + ) |
| 1656 | + respx_mock.get(f"/apps/{app_id}/deployments/{deployment_id}").mock( |
| 1657 | + return_value=Response(200, json={**deployment_data, "status": "success"}) |
| 1658 | + ) |
| 1659 | + |
| 1660 | + with ( |
| 1661 | + changing_dir(tmp_path), |
| 1662 | + patch.object(rich_toolkit.toolkit.Progress, "log") as mock_progress, |
| 1663 | + ): |
| 1664 | + result = runner.invoke(app, ["deploy"]) |
| 1665 | + assert result.exit_code == 0 |
| 1666 | + |
| 1667 | + call_args = [ |
| 1668 | + c.args[0] for c in mock_progress.call_args_list if isinstance(c.args[0], str) |
| 1669 | + ] |
| 1670 | + |
| 1671 | + for expected_msg in expected_msgs: |
| 1672 | + pattern = re.compile(f"Uploading deployment {expected_msg}\\.\\.\\.") |
| 1673 | + assert any(pattern.match(arg) for arg in call_args), ( |
| 1674 | + f"Expected message '{pattern.pattern}' not found in {call_args}" |
| 1675 | + ) |
| 1676 | + |
| 1677 | + |
1581 | 1678 | @pytest.mark.respx |
1582 | 1679 | def test_deploy_with_app_id_arg( |
1583 | 1680 | logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter |
|
0 commit comments