From 9194f07051680326fda4fe0619a357e3bd3648f1 Mon Sep 17 00:00:00 2001 From: Chris Busillo Date: Mon, 25 May 2026 17:35:44 -0400 Subject: [PATCH] Use inherited driver actions in product read models --- .../product_environment_read_model.py | 3 +- tests/test_product_environment_read_model.py | 36 ++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/control_plane/contracts/product_environment_read_model.py b/control_plane/contracts/product_environment_read_model.py index af28f5ca..6dc4337c 100644 --- a/control_plane/contracts/product_environment_read_model.py +++ b/control_plane/contracts/product_environment_read_model.py @@ -29,6 +29,7 @@ from control_plane.contracts.secret_record import SecretBinding from control_plane.drivers.registry import ( build_driver_context_view, + effective_driver_actions, list_driver_descriptors, read_driver_descriptor, ) @@ -1248,7 +1249,7 @@ def _action_availability( ) -> tuple[ProductActionAvailability, ...]: descriptor_actions = { action.action_id: action - for action in (descriptor.actions if descriptor is not None else ()) + for action in (effective_driver_actions(descriptor) if descriptor is not None else ()) if action.operator_visible } action_ids = tuple(descriptor_actions) diff --git a/tests/test_product_environment_read_model.py b/tests/test_product_environment_read_model.py index 58b807a0..a1dba96e 100644 --- a/tests/test_product_environment_read_model.py +++ b/tests/test_product_environment_read_model.py @@ -101,6 +101,19 @@ def _site_profile_payload( } +def _odoo_profile_payload() -> dict[str, object]: + payload = _site_profile_payload( + product="odoo-tenant-cm", + preview_context="cm", + testing_context="cm", + prod_context="cm", + ) + payload["display_name"] = "Odoo Tenant CM" + payload["repository"] = "cbusillo/odoo-tenant-cm" + payload["driver_id"] = "odoo" + return payload + + def _preview_record( *, preview_id: str, @@ -444,6 +457,24 @@ def test_product_site_overview_hides_non_operator_driver_actions(self) -> None: self.assertNotIn("testing_verification", actions) self.assertNotIn("preview_verification", actions) + def test_odoo_product_site_overview_uses_inherited_generic_web_actions(self) -> None: + profile = LaunchplaneProductProfileRecord.model_validate(_odoo_profile_payload()) + + overview = build_product_site_overview( + record_store=_PreviewRecordStore(profile, ()), + product=profile.product, + action_allowed=lambda *_: True, + ) + + actions = {action.action_id: action for action in overview.available_actions} + self.assertEqual(actions["stable_deploy"].route_path, "/v1/drivers/generic-web/deploy") + self.assertEqual( + actions["preview_refresh"].route_path, + "/v1/drivers/generic-web/preview-refresh", + ) + self.assertEqual(actions["preview_apply"].route_path, "/v1/drivers/odoo/preview-apply") + self.assertNotIn("preview_verification", actions) + def test_product_site_overview_raises_for_unknown_product(self) -> None: profile = LaunchplaneProductProfileRecord.model_validate(_site_profile_payload()) store = _PreviewRecordStore(profile, ()) @@ -890,7 +921,6 @@ def test_driver_lane_summary_exposes_artifact_build_provenance(self) -> None: ) self.assertIn("odoo-devkit", detail.model_dump_json()) - def test_product_environment_config_status_reports_expected_key_states(self) -> None: with TemporaryDirectory() as temporary_directory_name: database_path = Path(temporary_directory_name) / "launchplane.sqlite3" @@ -932,9 +962,7 @@ def test_product_environment_config_status_reports_expected_key_states(self) -> ) runtime_statuses = {item.key: item.status for item in config_status.runtime_settings} - secret_statuses = { - item.binding_key: item.status for item in config_status.managed_secrets - } + secret_statuses = {item.binding_key: item.status for item in config_status.managed_secrets} self.assertEqual(runtime_statuses, {"RESEND_FROM_EMAIL": "configured"}) self.assertEqual( secret_statuses,