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
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
245 lines
9.0 KiB
Python
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}"
|
|
)
|