|
6 | 6 | import json |
7 | 7 | import os |
8 | 8 | import signal |
| 9 | +import sys |
9 | 10 | from pathlib import Path |
10 | 11 | from typing import Optional |
11 | 12 | from unittest.mock import ANY, MagicMock, Mock, PropertyMock, mock_open, patch |
@@ -117,6 +118,25 @@ def test_version_info( |
117 | 118 | "MockAdaptor Data Interface Version": "1.5", |
118 | 119 | } |
119 | 120 |
|
| 121 | + def test_logs_versions_at_startup(self): |
| 122 | + """Verifies that both the runtime and adaptor versions are logged during start().""" |
| 123 | + # GIVEN |
| 124 | + entrypoint = EntryPoint(FakeAdaptor) |
| 125 | + |
| 126 | + with ( |
| 127 | + patch.object(entrypoint, "_get_adaptor_package_version", return_value="1.2.3"), |
| 128 | + patch.object(runtime_entrypoint.sys, "argv", ["Adaptor", "run"]), |
| 129 | + patch.object(runtime_entrypoint, "_logger") as mock_logger, |
| 130 | + ): |
| 131 | + # WHEN |
| 132 | + entrypoint.start() |
| 133 | + |
| 134 | + # THEN |
| 135 | + mock_logger.info.assert_any_call( |
| 136 | + f"openjd-adaptor-runtime version: {runtime_entrypoint._RUNTIME_VERSION}" |
| 137 | + ) |
| 138 | + mock_logger.info.assert_any_call("FakeAdaptor version: 1.2.3") |
| 139 | + |
120 | 140 | @pytest.mark.parametrize("integration_version", ["1.4", "1.5"]) |
121 | 141 | def test_is_compatible( |
122 | 142 | self, |
@@ -904,3 +924,166 @@ def test_raises_on_nonvalid_parsed_data_type(self): |
904 | 924 |
|
905 | 925 | # THEN |
906 | 926 | assert raised_err.match(f"Expected loaded data to be a dict, but got {type(input)}") |
| 927 | + |
| 928 | + |
| 929 | +@pytest.mark.skipif( |
| 930 | + sys.version_info < (3, 10), |
| 931 | + reason="packages_distributions requires Python 3.10+", |
| 932 | +) |
| 933 | +class TestGetAdaptorPackageVersion: |
| 934 | + """ |
| 935 | + Tests for the EntryPoint._get_adaptor_package_version method |
| 936 | + """ |
| 937 | + |
| 938 | + def test_returns_version_from_distribution_file_match(self, mock_adaptor_cls: MagicMock): |
| 939 | + """When the adaptor module file is found in a distribution's recorded files, return its version.""" |
| 940 | + # GIVEN |
| 941 | + mock_adaptor_cls.__module__ = "deadline.max_adaptor.MaxAdaptor.adaptor" |
| 942 | + entrypoint = EntryPoint(mock_adaptor_cls) |
| 943 | + |
| 944 | + mock_dist = MagicMock() |
| 945 | + mock_dist.version = "0.3.3" |
| 946 | + mock_dist.files = [ |
| 947 | + MagicMock(__str__=lambda self: "deadline/max_adaptor/MaxAdaptor/adaptor.py"), |
| 948 | + MagicMock(__str__=lambda self: "deadline/max_adaptor/__init__.py"), |
| 949 | + ] |
| 950 | + |
| 951 | + def fake_distribution(name): |
| 952 | + if name == "deadline-cloud-for-3ds-max": |
| 953 | + return mock_dist |
| 954 | + raise runtime_entrypoint.importlib.metadata.PackageNotFoundError(name) |
| 955 | + |
| 956 | + with ( |
| 957 | + patch( |
| 958 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.packages_distributions", |
| 959 | + return_value={"deadline": ["deadline-cloud-for-3ds-max", "deadline"]}, |
| 960 | + ), |
| 961 | + patch( |
| 962 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.distribution", |
| 963 | + side_effect=fake_distribution, |
| 964 | + ), |
| 965 | + ): |
| 966 | + # WHEN |
| 967 | + result = entrypoint._get_adaptor_package_version() |
| 968 | + |
| 969 | + # THEN |
| 970 | + assert result == "0.3.3" |
| 971 | + |
| 972 | + def test_does_not_match_wrong_distribution(self, mock_adaptor_cls: MagicMock): |
| 973 | + """When the top-level package has multiple distributions, only match the correct one.""" |
| 974 | + # GIVEN |
| 975 | + mock_adaptor_cls.__module__ = "deadline.max_adaptor.MaxAdaptor.adaptor" |
| 976 | + entrypoint = EntryPoint(mock_adaptor_cls) |
| 977 | + |
| 978 | + # deadline-cloud dist does NOT contain the adaptor module file |
| 979 | + deadline_cloud_dist = MagicMock() |
| 980 | + deadline_cloud_dist.version = "0.57.3" |
| 981 | + deadline_cloud_dist.files = [ |
| 982 | + MagicMock(__str__=lambda self: "deadline/client/__init__.py"), |
| 983 | + MagicMock(__str__=lambda self: "deadline/client/cli.py"), |
| 984 | + ] |
| 985 | + |
| 986 | + # deadline-cloud-for-3ds-max dist DOES contain the adaptor module file |
| 987 | + max_adaptor_dist = MagicMock() |
| 988 | + max_adaptor_dist.version = "0.3.3" |
| 989 | + max_adaptor_dist.files = [ |
| 990 | + MagicMock(__str__=lambda self: "deadline/max_adaptor/MaxAdaptor/adaptor.py"), |
| 991 | + MagicMock(__str__=lambda self: "deadline/max_adaptor/__init__.py"), |
| 992 | + ] |
| 993 | + |
| 994 | + def fake_distribution(name): |
| 995 | + if name == "deadline": |
| 996 | + return deadline_cloud_dist |
| 997 | + if name == "deadline-cloud-for-3ds-max": |
| 998 | + return max_adaptor_dist |
| 999 | + raise runtime_entrypoint.importlib.metadata.PackageNotFoundError(name) |
| 1000 | + |
| 1001 | + with ( |
| 1002 | + patch( |
| 1003 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.packages_distributions", |
| 1004 | + return_value={"deadline": ["deadline", "deadline-cloud-for-3ds-max"]}, |
| 1005 | + ), |
| 1006 | + patch( |
| 1007 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.distribution", |
| 1008 | + side_effect=fake_distribution, |
| 1009 | + ), |
| 1010 | + ): |
| 1011 | + # WHEN |
| 1012 | + result = entrypoint._get_adaptor_package_version() |
| 1013 | + |
| 1014 | + # THEN |
| 1015 | + assert result == "0.3.3" |
| 1016 | + |
| 1017 | + def test_falls_back_to_version_module(self, mock_adaptor_cls: MagicMock): |
| 1018 | + """When no distribution file match is found, fall back to _version module in sys.modules.""" |
| 1019 | + # GIVEN |
| 1020 | + mock_adaptor_cls.__module__ = "deadline.max_adaptor.MaxAdaptor.adaptor" |
| 1021 | + entrypoint = EntryPoint(mock_adaptor_cls) |
| 1022 | + |
| 1023 | + mock_version_module = MagicMock() |
| 1024 | + mock_version_module.version = "3.5.1" |
| 1025 | + |
| 1026 | + with ( |
| 1027 | + patch( |
| 1028 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.packages_distributions", |
| 1029 | + return_value={"deadline": []}, |
| 1030 | + ), |
| 1031 | + patch.dict( |
| 1032 | + runtime_entrypoint.sys.modules, |
| 1033 | + {"deadline.max_adaptor._version": mock_version_module}, |
| 1034 | + ), |
| 1035 | + ): |
| 1036 | + # WHEN |
| 1037 | + result = entrypoint._get_adaptor_package_version() |
| 1038 | + |
| 1039 | + # THEN |
| 1040 | + assert result == "3.5.1" |
| 1041 | + |
| 1042 | + def test_fallback_walks_up_module_path(self, mock_adaptor_cls: MagicMock): |
| 1043 | + """The _version fallback walks up the module path to find the correct _version module.""" |
| 1044 | + # GIVEN |
| 1045 | + mock_adaptor_cls.__module__ = "deadline.max_adaptor.MaxAdaptor.adaptor" |
| 1046 | + entrypoint = EntryPoint(mock_adaptor_cls) |
| 1047 | + |
| 1048 | + mock_version_module = MagicMock() |
| 1049 | + mock_version_module.version = "0.3.2" |
| 1050 | + |
| 1051 | + with ( |
| 1052 | + patch( |
| 1053 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.packages_distributions", |
| 1054 | + return_value={"deadline": []}, |
| 1055 | + ), |
| 1056 | + patch.dict( |
| 1057 | + runtime_entrypoint.sys.modules, |
| 1058 | + {"deadline.max_adaptor._version": mock_version_module}, |
| 1059 | + ), |
| 1060 | + ): |
| 1061 | + # WHEN |
| 1062 | + result = entrypoint._get_adaptor_package_version() |
| 1063 | + |
| 1064 | + # THEN |
| 1065 | + assert result == "0.3.2" |
| 1066 | + |
| 1067 | + def test_returns_unknown_when_version_not_found(self, mock_adaptor_cls: MagicMock): |
| 1068 | + """When no version can be determined, return 'UNKNOWN' and log a warning.""" |
| 1069 | + # GIVEN |
| 1070 | + mock_adaptor_cls.__module__ = "deadline.max_adaptor.MaxAdaptor.adaptor" |
| 1071 | + mock_adaptor_cls.__name__ = "MaxAdaptor" |
| 1072 | + entrypoint = EntryPoint(mock_adaptor_cls) |
| 1073 | + |
| 1074 | + with ( |
| 1075 | + patch( |
| 1076 | + "openjd.adaptor_runtime._entrypoint.importlib.metadata.packages_distributions", |
| 1077 | + return_value={"deadline": []}, |
| 1078 | + ), |
| 1079 | + patch.dict(runtime_entrypoint.sys.modules, {}, clear=False), |
| 1080 | + ): |
| 1081 | + runtime_entrypoint.sys.modules.pop("deadline.max_adaptor.MaxAdaptor._version", None) |
| 1082 | + runtime_entrypoint.sys.modules.pop("deadline.max_adaptor._version", None) |
| 1083 | + runtime_entrypoint.sys.modules.pop("deadline._version", None) |
| 1084 | + |
| 1085 | + # WHEN |
| 1086 | + result = entrypoint._get_adaptor_package_version() |
| 1087 | + |
| 1088 | + # THEN |
| 1089 | + assert result == "UNKNOWN" |
0 commit comments