Files
cleveragents-core/features/steps/cloud_handler_coverage_steps.py
freemo 051ee7c290
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 47s
CI / security (pull_request) Successful in 52s
CI / build (pull_request) Successful in 56s
CI / e2e_tests (pull_request) Successful in 5m1s
CI / integration_tests (pull_request) Successful in 5m30s
CI / unit_tests (pull_request) Successful in 5m42s
CI / docker (pull_request) Successful in 58s
CI / coverage (pull_request) Successful in 7m35s
CI / build (push) Successful in 21s
CI / docker (push) Has been skipped
CI / benchmark-regression (pull_request) Failing after 49m24s
CI / lint (push) Successful in 22s
CI / quality (push) Successful in 39s
CI / security (push) Successful in 48s
CI / typecheck (push) Successful in 1m26s
CI / benchmark-regression (push) Has been skipped
CI / e2e_tests (push) Successful in 5m53s
CI / coverage (push) Successful in 9m4s
CI / benchmark-publish (push) Successful in 19m10s
CI / integration_tests (push) Failing after 19m18s
CI / unit_tests (push) Failing after 19m20s
test(coverage): add Behave BDD tests to improve coverage across 52 source files
Added 52 new .feature files and corresponding _steps.py files targeting
previously uncovered code paths in the following areas:

- TUI layer: app, commands, persona (state/schema/registry), widgets,
  input (shell_exec, reference_parser)
- Application services: plan lifecycle/service/executor, session,
  project, repo indexing, correction, checkpoint, actor, llm_actors,
  strategy coordinator, resource file watcher, service retry wiring
- CLI commands: session, resource, repl, plan, db, automation_profile
- Domain models: retry_policy, resource_type, cost_budget,
  docker_compose_analyzer, detail_level, _sql_string_aware,
  _postgresql_helpers
- Core: circuit_breaker, retry_service_patterns
- Infrastructure: repositories, transaction_sandbox, strategy_registry,
  plugins/loader, container
- Config: settings
- Agents: plan_generation, context_analysis, auto_debug
- A2A: facade

All new tests follow the Behave/Gherkin BDD standard. Resolved step
definition collisions with unique prefixes. Fixed Alembic fileConfig
logger disabling issue (disable_existing_loggers=False).

ISSUES CLOSED: #1068
2026-03-20 21:22:10 +00:00

245 lines
9.0 KiB
Python

"""Step definitions for cloud_handler_coverage.feature.
These steps target specific uncovered lines in
cleveragents/resource/handlers/cloud.py:
- Lines 249-251: extract_provider hierarchical prefix loop
- Line 253: extract_provider returns None for cloud-* generic types
- Lines 302-303: resolve_credentials explicit property priority
- Lines 331-333: validate_credentials with unknown provider
- Lines 420-423: CloudResourceHandler.resolve generic cloud type
- Lines 428-431: CloudResourceHandler.resolve unknown provider mapping
"""
import os
from unittest.mock import MagicMock, patch
from behave import given, then, when
from cleveragents.domain.models.core.resource import (
PhysVirt,
Resource,
ResourceCapabilities,
SandboxStrategy,
)
from cleveragents.resource.handlers.cloud import (
CloudResourceHandler,
extract_provider,
resolve_credentials,
validate_credentials,
)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
_VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"
def _make_resource(type_name: str, properties: dict | None = None) -> Resource:
"""Create a minimal Resource for testing."""
return Resource(
resource_id=_VALID_ULID,
resource_type_name=type_name,
classification=PhysVirt.VIRTUAL,
properties=properties or {},
sandbox_strategy=SandboxStrategy.NONE,
capabilities=ResourceCapabilities(),
)
# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------
@given("the cloud handler module is imported")
def step_cloud_module_imported(context):
"""Ensure the cloud handler module is importable."""
assert extract_provider is not None
assert resolve_credentials is not None
assert validate_credentials is not None
assert CloudResourceHandler is not None
# ---------------------------------------------------------------------------
# extract_provider: hierarchical prefix matching (lines 249-251)
# ---------------------------------------------------------------------------
@when('I extract the provider from type name "{type_name}"')
def step_extract_provider(context, type_name):
"""Call extract_provider with the given type name."""
context.extracted_provider = extract_provider(type_name)
@then('the extracted provider should be "{expected}"')
def step_verify_extracted_provider(context, expected):
"""Verify the extracted provider matches the expected value."""
assert context.extracted_provider == expected, (
f"Expected provider '{expected}', got '{context.extracted_provider}'"
)
@then("the extracted provider should be None")
def step_verify_extracted_provider_none(context):
"""Verify the extracted provider is None."""
assert context.extracted_provider is None, (
f"Expected None, got '{context.extracted_provider}'"
)
# ---------------------------------------------------------------------------
# resolve_credentials: explicit properties take priority (lines 302-303)
# ---------------------------------------------------------------------------
@given('chcov the environment variable "{var_name}" is set to "{var_value}"')
def step_set_env_var(context, var_name, var_value):
"""Set an environment variable, restoring on cleanup."""
original = os.environ.get(var_name)
os.environ[var_name] = var_value
def restore():
if original is None:
os.environ.pop(var_name, None)
else:
os.environ[var_name] = original
context.add_cleanup(restore)
@when(
'I resolve credentials for provider "{provider}" with explicit property "{prop}" set to "{value}"'
)
def step_resolve_with_explicit(context, provider, prop, value):
"""Resolve credentials with explicit properties."""
context.resolved_creds = resolve_credentials(provider, {prop: value})
@then('the resolved credential "{field}" should be "{expected}"')
def step_verify_resolved_credential(context, field, expected):
"""Verify a specific resolved credential value."""
actual = context.resolved_creds.get(field)
assert actual == expected, (
f"Expected resolved['{field}'] == '{expected}', got '{actual}'"
)
# ---------------------------------------------------------------------------
# validate_credentials: unknown provider (lines 331-333)
# ---------------------------------------------------------------------------
@when('I validate credentials for unknown provider "{provider}"')
def step_validate_unknown_provider(context, provider):
"""Call validate_credentials with an unknown provider."""
context.validation_errors = validate_credentials(provider, {})
@then('the validation errors should mention unknown provider "{provider}"')
def step_verify_unknown_provider_errors(context, provider):
"""Verify validation errors mention the unknown provider."""
assert len(context.validation_errors) > 0, "Expected at least one error"
joined = " ".join(context.validation_errors)
assert f"Unknown cloud provider '{provider}'" in joined, (
f"Expected mention of unknown provider '{provider}' in: {joined}"
)
# ---------------------------------------------------------------------------
# CloudResourceHandler.resolve: generic cloud-* type (lines 420-423)
# ---------------------------------------------------------------------------
@given('I have a cloud resource with type "{type_name}"')
def step_create_cloud_resource(context, type_name):
"""Create a Resource with the given type name."""
context.cloud_resource = _make_resource(type_name)
@when("I call resolve on the cloud resource handler")
def step_call_handler_resolve(context):
"""Call CloudResourceHandler.resolve and capture the exception."""
handler = CloudResourceHandler()
sandbox_mgr = MagicMock()
try:
handler.resolve(
resource=context.cloud_resource,
plan_id="plan-001",
slot_name="test-slot",
sandbox_manager=sandbox_mgr,
)
context.handler_error = None
context.handler_error_type = None
except NotImplementedError as exc:
context.handler_error = exc
context.handler_error_type = "NotImplementedError"
except ValueError as exc:
context.handler_error = exc
context.handler_error_type = "ValueError"
@then('a NotImplementedError should be raised mentioning generic type "{type_name}"')
def step_verify_not_implemented_generic(context, type_name):
"""Verify a NotImplementedError was raised with the correct message."""
assert context.handler_error_type == "NotImplementedError", (
f"Expected NotImplementedError, got {context.handler_error_type}: {context.handler_error}"
)
msg = str(context.handler_error)
assert type_name in msg, f"Expected '{type_name}' in error message: {msg}"
assert "generic" in msg.lower(), f"Expected 'generic' in error message: {msg}"
# ---------------------------------------------------------------------------
# CloudResourceHandler.resolve: unknown provider mapping (lines 428-431)
# ---------------------------------------------------------------------------
@given('extract_provider is mocked to return "{fake_provider}"')
def step_mock_extract_provider(context, fake_provider):
"""Patch extract_provider to return a fake provider string."""
context.fake_provider = fake_provider
context.extract_patcher = patch(
"cleveragents.resource.handlers.cloud.extract_provider",
return_value=fake_provider,
)
context.extract_patcher.start()
context.add_cleanup(context.extract_patcher.stop)
@when("I call resolve on the cloud resource handler expecting a ValueError")
def step_call_handler_resolve_value_error(context):
"""Call CloudResourceHandler.resolve and capture the ValueError."""
handler = CloudResourceHandler()
sandbox_mgr = MagicMock()
try:
handler.resolve(
resource=context.cloud_resource,
plan_id="plan-002",
slot_name="test-slot",
sandbox_manager=sandbox_mgr,
)
context.handler_error = None
context.handler_error_type = None
except ValueError as exc:
context.handler_error = exc
context.handler_error_type = "ValueError"
except NotImplementedError as exc:
context.handler_error = exc
context.handler_error_type = "NotImplementedError"
@then('a ValueError should be raised mentioning unknown provider "{provider}"')
def step_verify_value_error_unknown_provider(context, provider):
"""Verify a ValueError was raised mentioning the unknown provider."""
assert context.handler_error_type == "ValueError", (
f"Expected ValueError, got {context.handler_error_type}: {context.handler_error}"
)
msg = str(context.handler_error)
assert provider in msg, f"Expected '{provider}' in error message: {msg}"
assert "unknown provider" in msg.lower() or "maps to unknown" in msg.lower(), (
f"Expected 'unknown provider' in error message: {msg}"
)