|
3 | 3 |
|
4 | 4 | from __future__ import annotations |
5 | 5 |
|
| 6 | +import json |
6 | 7 | from pathlib import Path |
| 8 | +from types import SimpleNamespace |
7 | 9 | from unittest.mock import MagicMock, call, patch |
8 | 10 |
|
9 | 11 | import pytest |
@@ -291,10 +293,7 @@ def test_run_info_renders_package_metadata_with_nested_runtime_plugins( |
291 | 293 | controller.catalog_service.get_catalog.return_value = catalog |
292 | 294 | controller.catalog_service.get_package_entries.return_value = package_entries |
293 | 295 | controller.catalog_service.evaluate_compatibility.return_value = CompatibilityResult(True, []) |
294 | | - controller.install_service.build_install_plan.return_value = _plan( |
295 | | - catalog, |
296 | | - data_designer_protection="uv will keep Data Designer packages pinned", |
297 | | - ) |
| 296 | + controller.install_service.build_install_plan.return_value = _plan(catalog) |
298 | 297 |
|
299 | 298 | controller.run_info("text-transform", catalog_alias="local") |
300 | 299 |
|
@@ -455,7 +454,7 @@ def test_run_install_dry_run_renders_plan_without_installing( |
455 | 454 | ) -> None: |
456 | 455 | entry = _entry() |
457 | 456 | catalog = _catalog() |
458 | | - plan = _plan(catalog, data_designer_protection="pinned installed Data Designer packages; data-designer 0.5.10") |
| 457 | + plan = _plan(catalog) |
459 | 458 | controller.catalog_service.get_catalog.return_value = catalog |
460 | 459 | controller.catalog_service.get_package_entries.return_value = [entry] |
461 | 460 | controller.catalog_service.evaluate_compatibility.return_value = CompatibilityResult(True, []) |
@@ -612,6 +611,89 @@ def test_run_install_versioned_dry_run_warns_instead_of_blocking_on_catalog_comp |
612 | 611 | mock_print_success.assert_any_call("Dry run complete; no changes made") |
613 | 612 |
|
614 | 613 |
|
| 614 | +@patch("data_designer.cli.controllers.plugin_catalog_controller.console") |
| 615 | +@patch("data_designer.cli.controllers.plugin_catalog_controller.print_success") |
| 616 | +@patch("data_designer.cli.controllers.plugin_catalog_controller.print_warning") |
| 617 | +def test_run_install_versioned_real_install_warns_instead_of_blocking_on_catalog_compatibility( |
| 618 | + mock_print_warning: MagicMock, |
| 619 | + mock_print_success: MagicMock, |
| 620 | + mock_console: MagicMock, |
| 621 | + controller: PluginCatalogController, |
| 622 | +) -> None: |
| 623 | + entry = _entry(package_name="data-designer-github") |
| 624 | + catalog = _catalog() |
| 625 | + plan = _plan(catalog, package_name="data-designer-github", requirement="data-designer-github==0.1.0") |
| 626 | + controller.catalog_service.get_catalog.return_value = catalog |
| 627 | + controller.catalog_service.get_package_entries.return_value = [entry] |
| 628 | + controller.catalog_service.evaluate_compatibility.return_value = CompatibilityResult( |
| 629 | + False, |
| 630 | + ["Data Designer 0.5.7 does not satisfy >=99.0"], |
| 631 | + ) |
| 632 | + controller.install_service.build_install_plan.return_value = plan |
| 633 | + controller.install_service.verify_entry_points.return_value = True |
| 634 | + |
| 635 | + controller.run_install("github==0.1.0", catalog_alias="local", yes=True) |
| 636 | + |
| 637 | + controller.install_service.build_install_plan.assert_called_once_with( |
| 638 | + entry, |
| 639 | + catalog, |
| 640 | + manager="auto", |
| 641 | + version_specifier="==0.1.0", |
| 642 | + ) |
| 643 | + controller.install_service.install.assert_called_once_with(plan) |
| 644 | + controller.install_service.verify_entry_points.assert_called_once_with([entry]) |
| 645 | + mock_print_warning.assert_called_once_with( |
| 646 | + "Catalog compatibility metadata may describe the catalog's default package version, not the requested version. " |
| 647 | + "Data Designer packages remain pinned during install; the package manager will fail if the requested plugin " |
| 648 | + "version cannot use the installed Data Designer version." |
| 649 | + ) |
| 650 | + mock_print_success.assert_called_once_with( |
| 651 | + "Plugin package 'data-designer-github' installed and runtime entry points loaded" |
| 652 | + ) |
| 653 | + assert mock_console.print.call_count >= 1 |
| 654 | + |
| 655 | + |
| 656 | +def test_run_install_versioned_dry_run_uses_local_catalog_and_real_services(tmp_path: Path) -> None: |
| 657 | + catalog_file = tmp_path / "plugins.json" |
| 658 | + catalog_file.write_text( |
| 659 | + json.dumps( |
| 660 | + _catalog_payload( |
| 661 | + package_name="data-designer-github", |
| 662 | + runtime_plugin_name="github", |
| 663 | + install_requirement="data-designer-github", |
| 664 | + ) |
| 665 | + ), |
| 666 | + encoding="utf-8", |
| 667 | + ) |
| 668 | + |
| 669 | + with ( |
| 670 | + patch("data_designer.cli.controllers.plugin_catalog_controller.console") as mock_console, |
| 671 | + patch("data_designer.cli.controllers.plugin_catalog_controller.print_success") as mock_print_success, |
| 672 | + patch( |
| 673 | + "data_designer.cli.services.plugin_catalog_service.importlib.metadata.version", |
| 674 | + return_value="0.5.10", |
| 675 | + ), |
| 676 | + patch( |
| 677 | + "data_designer.cli.services.plugin_install_service.importlib.metadata.version", |
| 678 | + return_value="0.5.10", |
| 679 | + ), |
| 680 | + patch( |
| 681 | + "data_designer.cli.services.plugin_install_service.subprocess.run", |
| 682 | + return_value=SimpleNamespace(returncode=0, stdout="pip 24.0\n", stderr=""), |
| 683 | + ), |
| 684 | + ): |
| 685 | + plugin_controller = PluginCatalogController(tmp_path / "data-designer-home") |
| 686 | + plugin_controller.catalog_service.add_catalog("local", str(catalog_file)) |
| 687 | + |
| 688 | + plugin_controller.run_install("github==0.1.0", catalog_alias="local", manager="pip", dry_run=True, refresh=True) |
| 689 | + |
| 690 | + mock_console.print.assert_any_call(" Runtime plugins: [bold]github (processor)[/bold]") |
| 691 | + mock_console.print.assert_any_call(" Requirement: [bold]data-designer-github==0.1.0[/bold]") |
| 692 | + mock_console.print.assert_any_call(" Install strategy: [bold]pip install[/bold]") |
| 693 | + mock_console.print.assert_any_call(" data-designer version: [bold]0.5.10[/bold]") |
| 694 | + mock_print_success.assert_any_call("Dry run complete; no changes made") |
| 695 | + |
| 696 | + |
615 | 697 | @patch("data_designer.cli.controllers.plugin_catalog_controller.console") |
616 | 698 | @patch("data_designer.cli.controllers.plugin_catalog_controller.print_error") |
617 | 699 | def test_run_install_blocks_incompatible_package( |
@@ -1068,19 +1150,16 @@ def _plan( |
1068 | 1150 | package_name: str = "data-designer-text-transform", |
1069 | 1151 | requirement: str | None = None, |
1070 | 1152 | source_warning: str | None = None, |
1071 | | - data_designer_protection: str | None = None, |
1072 | 1153 | manager: str = "pip", |
1073 | 1154 | install_mode: str = "pip-environment", |
1074 | 1155 | ) -> InstallPlan: |
1075 | 1156 | return InstallPlan( |
1076 | 1157 | package_name=package_name, |
1077 | | - source_description=requirement or package_name, |
1078 | 1158 | command=["python", "-m", "pip", "install", requirement or package_name], |
1079 | 1159 | manager=manager, |
1080 | 1160 | catalog_alias=catalog.alias, |
1081 | 1161 | requirement=requirement, |
1082 | 1162 | source_warning=source_warning, |
1083 | | - data_designer_protection=data_designer_protection, |
1084 | 1163 | data_designer_version="0.5.10", |
1085 | 1164 | install_mode=install_mode, |
1086 | 1165 | ) |
@@ -1133,3 +1212,46 @@ def _entry( |
1133 | 1212 | }, |
1134 | 1213 | } |
1135 | 1214 | ) |
| 1215 | + |
| 1216 | + |
| 1217 | +def _catalog_payload( |
| 1218 | + *, |
| 1219 | + package_name: str, |
| 1220 | + runtime_plugin_name: str, |
| 1221 | + install_requirement: str, |
| 1222 | +) -> dict[str, object]: |
| 1223 | + return { |
| 1224 | + "schema_version": 2, |
| 1225 | + "packages": [ |
| 1226 | + { |
| 1227 | + "name": package_name, |
| 1228 | + "description": "GitHub repository reader", |
| 1229 | + "install": { |
| 1230 | + "requirement": install_requirement, |
| 1231 | + "index_url": "https://nvidia-nemo.github.io/DataDesignerPlugins/simple/", |
| 1232 | + }, |
| 1233 | + "compatibility": { |
| 1234 | + "python": {"specifier": ">=3.10"}, |
| 1235 | + "data_designer": { |
| 1236 | + "requirement": "data-designer>=0.5.7", |
| 1237 | + "specifier": ">=0.5.7", |
| 1238 | + "marker": None, |
| 1239 | + }, |
| 1240 | + }, |
| 1241 | + "docs": { |
| 1242 | + "url": f"https://docs.example.test/plugins/{package_name}/", |
| 1243 | + }, |
| 1244 | + "plugins": [ |
| 1245 | + { |
| 1246 | + "name": runtime_plugin_name, |
| 1247 | + "plugin_type": "processor", |
| 1248 | + "entry_point": { |
| 1249 | + "group": "data_designer.plugins", |
| 1250 | + "name": runtime_plugin_name, |
| 1251 | + "value": "data_designer_github.plugin:plugin", |
| 1252 | + }, |
| 1253 | + } |
| 1254 | + ], |
| 1255 | + } |
| 1256 | + ], |
| 1257 | + } |
0 commit comments