Repository navigation
feat/0.12.0: add --metadata flag + web API adapter integration #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -313,9 +313,42 @@ def maximize_post() -> tuple[dict, int]: | |
| # Build package objects | ||
| package_objs = [] | ||
| conflict_map: dict[str, list[str]] = {} | ||
| for pkg_name in packages: | ||
| pkg = Package(name=pkg_name, status="candidate") | ||
| package_objs.append(pkg) | ||
| not_found = [] | ||
| metadata_summary = [] | ||
|
|
||
| from ..adapters import get_adapter | ||
|
|
||
| adapter = None | ||
| try: | ||
| adapter = get_adapter(manager) | ||
| except Exception: | ||
| pass | ||
|
|
||
| if adapter is not None: | ||
| for pkg_name in packages: | ||
| metadata = adapter.fetch(pkg_name) | ||
| if metadata and metadata.name: | ||
| pkg = Package(name=metadata.name, status="candidate") | ||
| if metadata.depends: | ||
| pkg.depends = metadata.depends | ||
| if metadata.conflicts: | ||
| pkg.conflicts = metadata.conflicts | ||
| package_objs.append(pkg) | ||
| metadata_summary.append( | ||
| { | ||
| "name": metadata.name, | ||
| "version": metadata.version, | ||
| "depends": metadata.depends, | ||
| "conflicts": metadata.conflicts, | ||
| } | ||
| ) | ||
| else: | ||
| not_found.append(pkg_name) | ||
| package_objs.append(Package(name=pkg_name, status="candidate")) | ||
| else: | ||
| for pkg_name in packages: | ||
| pkg = Package(name=pkg_name, status="candidate") | ||
| package_objs.append(pkg) | ||
|
|
||
| # Apply conflicts | ||
| for c in conflicts: | ||
|
|
@@ -359,7 +392,12 @@ def maximize_post() -> tuple[dict, int]: | |
| "output_count": len(result), | ||
| "selected": result, | ||
| "input": packages, | ||
| "metadata_fetched": ( | ||
| len(packages) - len(not_found) if adapter is not None else 0 | ||
| ), | ||
| } | ||
| if metadata_summary: | ||
| response_data["metadata"] = metadata_summary | ||
|
Comment on lines
+399
to
+400
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Return The endpoint builds 🤖 Prompt for AI Agents |
||
|
|
||
| if explain: | ||
| from ..core.model_encoder import encode_packages | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| from click.testing import CliRunner | ||
|
|
||
| from package_maximizer.cli.main import cli | ||
|
|
||
|
|
||
| def test_metadata_flag_loads_depends_via_adapter(): | ||
| runner = CliRunner() | ||
| fake_metadata = MagicMock() | ||
| fake_metadata.name = "pkg1" | ||
| fake_metadata.depends = ["dep1"] | ||
| fake_metadata.conflicts = [] | ||
|
|
||
| fake_adapter = MagicMock() | ||
| fake_adapter.fetch.return_value = fake_metadata | ||
|
|
||
| with ( | ||
| patch("package_maximizer.adapters.get_adapter", return_value=fake_adapter), | ||
| patch("package_maximizer.core.maximizer.PackageMaximizer") as fake_max, | ||
| ): | ||
| fake_max.return_value.solve.return_value = ["pkg1"] | ||
| result = runner.invoke( | ||
| cli, | ||
| ["maximize", "pkg1", "--metadata", "--manager", "apt", "--output", "json"], | ||
| ) | ||
|
|
||
| assert result.exit_code == 0, result.output | ||
| assert "Загружены метаданные" in result.output | ||
|
|
||
|
|
||
| def test_metadata_flag_warns_without_adapter(): | ||
| runner = CliRunner() | ||
| with ( | ||
| patch("package_maximizer.adapters.get_adapter", return_value=None), | ||
| patch("package_maximizer.core.maximizer.PackageMaximizer") as fake_max, | ||
| ): | ||
| fake_max.return_value.solve.return_value = ["pkg1"] | ||
| result = runner.invoke( | ||
| cli, | ||
| ["maximize", "pkg1", "--metadata", "--manager", "apt", "--output", "json"], | ||
| ) | ||
|
|
||
| assert result.exit_code == 0, result.output | ||
| assert "нет адаптера метаданных" in result.output | ||
|
|
||
|
|
||
| def test_metadata_flag_merges_depends_and_conflicts(): | ||
| runner = CliRunner() | ||
| fake_metadata = MagicMock() | ||
| fake_metadata.name = "pkg1" | ||
| fake_metadata.depends = ["dep1", "dep2"] | ||
| fake_metadata.conflicts = ["conf1"] | ||
|
|
||
| fake_adapter = MagicMock() | ||
| fake_adapter.fetch.return_value = fake_metadata | ||
|
|
||
| with ( | ||
| patch("package_maximizer.adapters.get_adapter", return_value=fake_adapter), | ||
| patch("package_maximizer.core.maximizer.PackageMaximizer") as fake_max, | ||
| ): | ||
| fake_pkg = MagicMock() | ||
| fake_pkg.depends = [] | ||
| fake_pkg.conflicts = [] | ||
| fake_max.return_value.solve.return_value = ["pkg1"] | ||
| result = runner.invoke( | ||
| cli, | ||
| [ | ||
| "maximize", | ||
| "pkg1", | ||
| "--metadata", | ||
| "--manager", | ||
| "apt", | ||
| "-d", | ||
| "existing_dep", | ||
| "-c", | ||
| "existing_conf", | ||
| "--output", | ||
| "json", | ||
| ], | ||
| ) | ||
|
|
||
| assert result.exit_code == 0, result.output | ||
| assert "Загружены метаданные" in result.output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Merge fetched and request constraints before solving.
maximize_postoverwritespkg.conflictsandpkg.dependswith request constraints after loading metadata. This drops constraints from either source when both specify the same package. Merge both lists with order-preserving deduplication, as in the CLI path, before calling the solver.🤖 Prompt for AI Agents