forked from cleveragents/cleveragents-core
fix(resource): call bootstrap_builtin_types during initialization
Add a call to bootstrap_builtin_types() in init_command() (project.py) immediately after initialize_project() returns. This seeds the built-in resource types (fs-directory, git-checkout, etc.) into the database so that "resource add" commands succeed without "Resource type not found" errors. The call is idempotent — invoking it multiple times will not create duplicate types. Also fix the TDD robot test (resource_type_bootstrap_git.robot) to initialize a project before running "resource add", and fix a pre-existing parallel test failure in plan_commands_new_coverage where unittest.mock.patch could not reliably intercept PlanApplyService under behave-parallel fork() workers. ISSUES CLOSED: #523, #524
This commit is contained in:
@@ -170,23 +170,40 @@ use_step_matcher("parse")
|
||||
|
||||
@when("new_cov I call _get_apply_service directly")
|
||||
def step_new_cov_call_get_apply(context: Context) -> None:
|
||||
"""Call the real _get_apply_service, mocking only its dependencies.
|
||||
"""Call the real ``_get_apply_service``, mocking its dependencies.
|
||||
|
||||
This exercises the actual function body (import, lifecycle lookup,
|
||||
PlanApplyService construction) to cover lines 1817, 1821, 1822.
|
||||
``PlanApplyService`` construction) to cover lines 1817, 1821, 1822.
|
||||
|
||||
Both ``_get_lifecycle_service`` **and** ``PlanApplyService`` are
|
||||
patched. The class is patched at two locations — the canonical
|
||||
source module and the plan module (with ``create=True``) — so the
|
||||
lazy ``from … import PlanApplyService`` inside the function always
|
||||
resolves to our mock, even under ``behave-parallel``'s
|
||||
``fork()``-based workers.
|
||||
"""
|
||||
from cleveragents.cli.commands.plan import _get_apply_service
|
||||
|
||||
mock_lifecycle = MagicMock()
|
||||
mock_pas_cls = MagicMock()
|
||||
mock_pas_instance = MagicMock()
|
||||
mock_pas_class = MagicMock(return_value=mock_pas_instance)
|
||||
mock_pas_cls.return_value = mock_pas_instance
|
||||
|
||||
with (
|
||||
patch(_PATCH_LIFECYCLE, return_value=context.new_cov_mock_lifecycle),
|
||||
patch(_PATCH_PAS_CLASS, mock_pas_class),
|
||||
patch(_PATCH_LIFECYCLE, return_value=mock_lifecycle),
|
||||
patch(
|
||||
"cleveragents.cli.commands.plan.PlanApplyService",
|
||||
mock_pas_cls,
|
||||
create=True,
|
||||
),
|
||||
patch(_PATCH_PAS_CLASS, mock_pas_cls),
|
||||
):
|
||||
context.new_cov_apply_result = _get_apply_service()
|
||||
context.new_cov_pas_class = mock_pas_class
|
||||
context.new_cov_pas_instance = mock_pas_instance
|
||||
result = _get_apply_service()
|
||||
|
||||
context.new_cov_apply_result = result
|
||||
context.new_cov_mock_lifecycle_used = mock_lifecycle
|
||||
context.new_cov_mock_pas_cls = mock_pas_cls
|
||||
context.new_cov_mock_pas_instance = mock_pas_instance
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -218,13 +235,16 @@ def step_new_cov_output_contains(context: Context, text: str) -> None:
|
||||
|
||||
@then("new_cov the returned object should be a PlanApplyService instance")
|
||||
def step_new_cov_result_is_pas(context: Context) -> None:
|
||||
assert context.new_cov_apply_result is context.new_cov_pas_instance, (
|
||||
f"Expected the mock PAS instance, got {type(context.new_cov_apply_result).__name__}"
|
||||
result = context.new_cov_apply_result
|
||||
expected = context.new_cov_mock_pas_instance
|
||||
assert result is expected, (
|
||||
f"Expected _get_apply_service to return the mock PlanApplyService "
|
||||
f"instance, got {type(result).__name__}"
|
||||
)
|
||||
|
||||
|
||||
@then("new_cov PlanApplyService was constructed with the lifecycle service")
|
||||
def step_new_cov_pas_called_with_lifecycle(context: Context) -> None:
|
||||
context.new_cov_pas_class.assert_called_once_with(
|
||||
lifecycle_service=context.new_cov_mock_lifecycle,
|
||||
context.new_cov_mock_pas_cls.assert_called_once_with(
|
||||
lifecycle_service=context.new_cov_mock_lifecycle_used,
|
||||
)
|
||||
|
||||
@@ -12,16 +12,23 @@ Suite Teardown Cleanup Test Environment
|
||||
|
||||
*** Test Cases ***
|
||||
Resource Add Git Checkout Should Not Fail With Type Not Found
|
||||
[Documentation] Run "agents resource add git-checkout" and verify it does not
|
||||
... produce a "Resource type not found" error. This currently fails
|
||||
... because bootstrap_builtin_types() is never called.
|
||||
[Documentation] Initialize a project then run "agents resource add git-checkout"
|
||||
... and verify it does not produce a "Resource type not found" error.
|
||||
... bootstrap_builtin_types() is called during project init.
|
||||
${proj_dir}= Set Variable ${TEMPDIR}${/}git_checkout_bootstrap_test
|
||||
Create Directory ${proj_dir}
|
||||
${init_result}= Run Process ${PYTHON} -m cleveragents init test-git-bootstrap
|
||||
... cwd=${proj_dir} timeout=120s
|
||||
Should Be Equal As Integers ${init_result.rc} 0
|
||||
... msg=Project init failed: rc=${init_result.rc} stderr=${init_result.stderr}
|
||||
${result}= Run Process ${PYTHON} -m cleveragents resource add
|
||||
... git-checkout local/test --path /tmp/repo --branch main
|
||||
... timeout=60s
|
||||
... cwd=${proj_dir} timeout=60s
|
||||
Should Not Contain ${result.stdout} Resource type not found
|
||||
Should Not Contain ${result.stderr} Resource type not found
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
... msg=Expected exit code 0 but got ${result.rc}. stdout: ${result.stdout} stderr: ${result.stderr}
|
||||
[Teardown] Run Keyword And Ignore Error Remove Directory ${proj_dir} recursive=True
|
||||
|
||||
Git Checkout Type Exists After Bootstrap
|
||||
[Documentation] Directly call bootstrap_builtin_types() and verify git-checkout
|
||||
|
||||
@@ -200,6 +200,18 @@ def init_command(
|
||||
apply_default_filters=default_filters,
|
||||
)
|
||||
|
||||
# Bootstrap built-in resource types (fs-directory, git-checkout, etc.)
|
||||
# into the database after schema creation. This must happen after
|
||||
# initialize_project() because the database tables are created there
|
||||
# via unit_of_work.init_database(). The call is idempotent — calling
|
||||
# it multiple times will not create duplicate types.
|
||||
from cleveragents.application.services.resource_registry_service import (
|
||||
ResourceRegistryService,
|
||||
)
|
||||
|
||||
resource_registry: ResourceRegistryService = container.resource_registry_service()
|
||||
resource_registry.bootstrap_builtin_types()
|
||||
|
||||
console.print(
|
||||
Panel(
|
||||
f"[green]✓[/green] Project '{project.name}' "
|
||||
|
||||
Reference in New Issue
Block a user