|
1 | | -from unittest import mock |
2 | 1 | from urllib.parse import urlparse |
3 | 2 |
|
4 | 3 | import pytest |
5 | 4 | from fastapi import FastAPI |
6 | 5 | from fastapi.testclient import TestClient |
| 6 | +from packaging.version import Version |
| 7 | +from pytest_mock import MockerFixture |
7 | 8 |
|
8 | 9 | import murfey |
| 10 | +from murfey.client.update import UPDATE_SUCCESS |
9 | 11 | from murfey.instrument_server import check_for_updates |
10 | | -from murfey.server.api.bootstrap import bootstrap as bootstrap_router |
11 | 12 | from murfey.server.api.bootstrap import pypi as pypi_router |
| 13 | +from murfey.server.api.bootstrap import version as version_router |
12 | 14 | from murfey.util.api import url_path_for |
13 | 15 |
|
14 | 16 | # Set up a test router with only the essential endpoints |
15 | 17 | app = FastAPI() |
16 | | -for router in [pypi_router, bootstrap_router]: |
| 18 | +for router in [pypi_router, version_router]: |
17 | 19 | app.include_router(router) |
18 | 20 | client = TestClient(app) |
19 | | - |
| 21 | +base_url = str(client.base_url) |
20 | 22 |
|
21 | 23 | check_for_updates_test_matrix = ( |
22 | 24 | # Downgrade, upgrade, or keep client version? |
|
29 | 31 | @pytest.mark.parametrize("test_params", check_for_updates_test_matrix) |
30 | 32 | def test_check_for_updates( |
31 | 33 | test_params: tuple[str], |
| 34 | + mocker: MockerFixture, |
32 | 35 | ): |
33 | 36 |
|
34 | 37 | # Unpack test params |
35 | | - (handle_client_version,) = test_params |
36 | | - |
37 | | - with ( |
38 | | - mock.patch("murfey.instrument_server.urlparse") as mock_parse, |
39 | | - mock.patch("murfey.client.update.requests.get") as mock_get, |
40 | | - ): |
41 | | - # Return the test client URL |
42 | | - api_base = urlparse("http://testserver", allow_fragments=False) |
43 | | - mock_parse.return_value = api_base |
| 38 | + (bump_client_version,) = test_params |
| 39 | + |
| 40 | + # Modify client version as needed |
| 41 | + current_version = murfey.__version__ |
| 42 | + supported_client_version = murfey.__supported_client_version__ |
| 43 | + |
| 44 | + major, minor, patch = Version(current_version).release |
| 45 | + |
| 46 | + # Adjust the perceived client version in the function being tested |
| 47 | + if bump_client_version == "downgrade": |
| 48 | + support_client_version_parts = Version(supported_client_version).release |
| 49 | + if patch == 0: |
| 50 | + if minor == 0: |
| 51 | + if major == 0: |
| 52 | + # This can't be downgraded, so skip |
| 53 | + pytest.skip("This version can't be downgraded anymore; skipping") |
| 54 | + else: |
| 55 | + major = support_client_version_parts[0] - 1 |
| 56 | + print(f"Downgraded major version to {major}") |
| 57 | + else: |
| 58 | + minor = support_client_version_parts[1] - 1 |
| 59 | + print(f"Downgraded minor version to {minor}") |
| 60 | + else: |
| 61 | + patch = support_client_version_parts[2] - 1 |
| 62 | + print(f"Downgraded patch version to {patch}") |
| 63 | + elif bump_client_version == "upgrade": |
| 64 | + patch += 1 |
| 65 | + print(f"Bumped patch version to {patch}") |
| 66 | + mock_client_version = f"{major}.{minor}.{patch}" |
| 67 | + |
| 68 | + # Run the version check query and get a response to patch in later |
| 69 | + api_base = urlparse(base_url, allow_fragments=False) |
| 70 | + proxy_path = api_base.path.rstrip("/") |
| 71 | + version_check_path = url_path_for("bootstrap.version", "get_version") |
| 72 | + version_check_query = f"client_version={mock_client_version}" |
| 73 | + version_check_url = api_base._replace( |
| 74 | + path=f"{proxy_path}{version_check_path}", |
| 75 | + query=version_check_query, |
| 76 | + ) |
| 77 | + version_check_response = client.get(f"{version_check_path}?{version_check_query}") |
| 78 | + |
| 79 | + # Check that the endpoint works as expected |
| 80 | + assert version_check_response.status_code == 200 |
| 81 | + assert version_check_response.json() == { |
| 82 | + "server": current_version, |
| 83 | + "oldest-supported-client": supported_client_version, |
| 84 | + "client-needs-update": True if bump_client_version == "downgrade" else False, |
| 85 | + "client-needs-downgrade": True if bump_client_version == "upgrade" else False, |
| 86 | + } |
| 87 | + |
| 88 | + # Patch the URL parse result |
| 89 | + mock_parse = mocker.patch("murfey.instrument_server.urlparse") |
| 90 | + mock_parse.return_value = api_base |
| 91 | + |
| 92 | + # Patch the result of get |
| 93 | + mock_get = mocker.patch("murfey.client.update.requests.get") |
| 94 | + mock_get.return_value = version_check_response |
| 95 | + |
| 96 | + # Patch the installation function |
| 97 | + mock_install = mocker.patch("murfey.client.update.install_murfey") |
| 98 | + |
| 99 | + # Patch the perceived client version |
| 100 | + mocker.patch("murfey.client.update.murfey.__version__", new=mock_client_version) |
| 101 | + |
| 102 | + # If changing the client version, check that 'install_murfey' and 'exit' are called |
| 103 | + if bump_client_version in ("upgrade", "downgrade"): |
| 104 | + with pytest.raises(SystemExit) as exc_info: |
| 105 | + check_for_updates() |
| 106 | + mock_install.assert_called_once() |
| 107 | + # Check that 'exit' is called with the correct message |
| 108 | + assert exc_info.value.code == UPDATE_SUCCESS |
| 109 | + # If client version is the same, 'install_murfey' shouldn't be called |
| 110 | + else: |
44 | 111 | check_for_updates() |
| 112 | + mock_install.assert_not_called() |
45 | 113 |
|
46 | | - # Modify client version as needed |
47 | | - current_version = murfey.__version__ |
48 | | - supported_client_version = murfey.__supported_client_version__ |
49 | | - |
50 | | - # Check that a request was sent to the test_client with the correct URL |
51 | | - proxy_path = api_base.path.rstrip("/") |
52 | | - version_check_url = api_base._replace( |
53 | | - path=f"{proxy_path}{url_path_for('bootstrap.version', 'get_version')}", |
54 | | - query=f"client_version={current_version}", |
55 | | - ) |
56 | | - mock_get.assert_any_call(version_check_url.geturl()) |
57 | | - |
58 | | - # Construct the mock response |
59 | | - mock_response = mock.Mock() |
60 | | - mock_response.json.return_value = { |
61 | | - "server": current_version, |
62 | | - "oldest-supported-client": supported_client_version, |
63 | | - "client-needs-update": True, |
64 | | - "client-needs-downgrade": False, |
65 | | - } |
66 | | - mock_response.status_code = 200 |
67 | | - mock_get.return_value = mock_response |
68 | | - |
69 | | - pass |
| 114 | + # Check that the query URL is correct |
| 115 | + mock_get.assert_called_once_with(version_check_url.geturl()) |
0 commit comments