test(context): add integration tests for pluggable scope chain resolution #10670

Closed
HAL9000 wants to merge 1 commits from test/v3.6.0/scope-chain-integration-tests into master
3 changed files with 922 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
Feature: Pluggable Scope Chain Resolution
As a developer
I want to register and use custom scope resolvers
So that I can extend the scope chain resolution system with custom logic
Scenario: Register custom resolver via YAML configuration
Given I have a scope chain resolver registry
And I have a YAML configuration with a custom resolver definition
When I load the YAML configuration
Then the custom resolver should be registered in the registry
And the resolver should have the correct name and type
Scenario: Register custom resolver via Python entry point
Given I have a scope chain resolver registry
And I have a Python entry point for a custom resolver
When I load resolvers from entry points
Then the custom resolver should be registered in the registry
And the resolver should be accessible by its entry point name
Scenario: Invoke custom resolver during context assembly
Given I have a scope chain resolver registry
And I have registered a custom resolver
And I have a context request with scope parameters
When I assemble the context with the custom resolver
Then the custom resolver should be invoked
And the resolver should return the expected scope chain
Scenario: Handle resolver errors gracefully during context assembly
Given I have a scope chain resolver registry
And I have registered a faulty custom resolver that raises an error
And I have a context request with scope parameters
When I assemble the context with the faulty resolver
Then the context assembly should not fail
And the error should be logged
And the system should fall back to the default resolver
Scenario: Validate resolver configuration on registration
Given I have a scope chain resolver registry
And I have an invalid resolver configuration
When I try to register the invalid resolver
Then a validation error should be raised
And the resolver should not be registered
Scenario: Support multiple custom resolvers in chain
Given I have a scope chain resolver registry
And I have registered multiple custom resolvers
And I have a context request with scope parameters
When I assemble the context with all resolvers
Then all resolvers should be invoked in order
And the final scope chain should combine results from all resolvers
Scenario: Resolver priority and ordering
Given I have a scope chain resolver registry
And I have registered resolvers with different priorities
And I have a context request with scope parameters
When I assemble the context with prioritized resolvers
Then resolvers should be invoked in priority order
And higher priority resolvers should override lower priority results
Scenario: Resolver caching and performance
Given I have a scope chain resolver registry
And I have registered a custom resolver with caching
And I have a context request with scope parameters
When I assemble the context multiple times with the same parameters
Then the resolver should use cached results on subsequent calls
And the resolver should not be invoked multiple times for identical requests
Scenario: Resolver state isolation
Given I have a scope chain resolver registry
And I have registered a stateful custom resolver
And I have multiple concurrent context assembly requests
When I assemble contexts concurrently
Then each resolver instance should maintain isolated state
And resolver state should not leak between requests
Scenario: Resolver metadata and introspection
Given I have a scope chain resolver registry
And I have registered a custom resolver with metadata
When I query the resolver metadata
Then the metadata should include resolver name, version, and description
And the metadata should list supported scope parameters
And the metadata should indicate resolver capabilities
+473
View File
@@ -0,0 +1,473 @@
"""Step implementations for pluggable scope chain resolution integration tests."""
from __future__ import annotations
from typing import Any, Protocol
from unittest.mock import Mock
from behave import given, then, when
from behave.runner import Context
class ScopeResolver(Protocol):
"""Protocol for scope chain resolvers."""
def resolve(self, scope_params: dict[str, Any]) -> dict[str, Any]:
"""Resolve scope parameters to a scope chain."""
...
# ============================================================================
# Given Steps
# ============================================================================
@given("I have a scope chain resolver registry")
def step_create_resolver_registry(context: Context) -> None:
"""Create a scope chain resolver registry."""
context.resolver_registry: dict[str, Any] = {}
context.resolver_errors: list[str] = []
context.resolver_invocations: list[str] = []
@given("I have a YAML configuration with a custom resolver definition")
def step_create_yaml_config(context: Context) -> None:
"""Create a YAML configuration with a custom resolver."""
context.yaml_config = {
"resolvers": [
{
"name": "custom_resolver",
"type": "custom",
"class": "test.CustomResolver",
"config": {"param1": "value1"},
}
]
}
@given("I have a Python entry point for a custom resolver")
def step_create_entry_point(context: Context) -> None:
"""Create a Python entry point for a custom resolver."""
context.entry_point_name = "test_resolver"
context.entry_point_class = Mock()
context.entry_point_class.__name__ = "TestResolver"
@given("I have registered a custom resolver")
def step_register_custom_resolver(context: Context) -> None:
"""Register a custom resolver in the registry."""
resolver = Mock(spec=ScopeResolver)
resolver.name = "custom_resolver"
resolver.resolve = Mock(return_value={"scope": "custom"})
context.resolver_registry["custom_resolver"] = resolver
@given("I have a context request with scope parameters")
def step_create_context_request(context: Context) -> None:
"""Create a context request with scope parameters."""
context.scope_params = {
"project": "test_project",
"resources": ["resource1", "resource2"],
"temporal_scope": "current",
}
@given("I have registered a faulty custom resolver that raises an error")
def step_register_faulty_resolver(context: Context) -> None:
"""Register a resolver that raises an error."""
resolver = Mock(spec=ScopeResolver)
resolver.name = "faulty_resolver"
resolver.resolve = Mock(side_effect=RuntimeError("Resolver error"))
context.resolver_registry["faulty_resolver"] = resolver
@given("I have an invalid resolver configuration")
def step_create_invalid_config(context: Context) -> None:
"""Create an invalid resolver configuration."""
context.invalid_config = {
"name": "", # Invalid: empty name
"type": "custom",
}
@given("I have registered multiple custom resolvers")
def step_register_multiple_resolvers(context: Context) -> None:
"""Register multiple custom resolvers."""
for i in range(3):
resolver = Mock(spec=ScopeResolver)
resolver.name = f"resolver_{i}"
resolver.resolve = Mock(return_value={"scope": f"scope_{i}"})
context.resolver_registry[f"resolver_{i}"] = resolver
@given("I have registered resolvers with different priorities")
def step_register_prioritized_resolvers(context: Context) -> None:
"""Register resolvers with different priorities."""
context.prioritized_resolvers = []
for i, priority in enumerate([1, 3, 2]):
resolver = Mock(spec=ScopeResolver)
resolver.name = f"resolver_{i}"
resolver.priority = priority
resolver.resolve = Mock(return_value={"scope": f"scope_{i}"})
context.prioritized_resolvers.append((resolver, priority))
context.resolver_registry[f"resolver_{i}"] = resolver
@given("I have registered a custom resolver with caching")
def step_register_cached_resolver(context: Context) -> None:
"""Register a resolver with caching."""
resolver = Mock(spec=ScopeResolver)
resolver.name = "cached_resolver"
resolver.cache: dict[str, Any] = {}
resolver.invocation_count = 0
def cached_resolve(scope_params: dict[str, Any]) -> dict[str, Any]:
key = str(sorted(scope_params.items()))
if key not in resolver.cache:
resolver.invocation_count += 1
resolver.cache[key] = {"scope": "cached", "invocation": resolver.invocation_count}
return resolver.cache[key]
resolver.resolve = Mock(side_effect=cached_resolve)
context.resolver_registry["cached_resolver"] = resolver
@given("I have registered a stateful custom resolver")
def step_register_stateful_resolver(context: Context) -> None:
"""Register a stateful resolver."""
resolver = Mock(spec=ScopeResolver)
resolver.name = "stateful_resolver"
resolver.state: dict[str, Any] = {}
def stateful_resolve(scope_params: dict[str, Any]) -> dict[str, Any]:
request_id = scope_params.get("request_id", "unknown")
resolver.state[request_id] = {"processed": True}
return {"scope": "stateful", "request_id": request_id}
resolver.resolve = Mock(side_effect=stateful_resolve)
context.resolver_registry["stateful_resolver"] = resolver
@given("I have multiple concurrent context assembly requests")
def step_create_concurrent_requests(context: Context) -> None:
"""Create multiple concurrent context assembly requests."""
context.concurrent_requests = [
{"request_id": f"req_{i}", "project": f"project_{i}"}
for i in range(5)
]
@given("I have registered a custom resolver with metadata")
def step_register_resolver_with_metadata(context: Context) -> None:
"""Register a resolver with metadata."""
resolver = Mock(spec=ScopeResolver)
resolver.name = "metadata_resolver"
resolver.metadata = {
"name": "metadata_resolver",
"version": "1.0.0",
"description": "A resolver with metadata",
"supported_params": ["project", "resources", "temporal_scope"],
"capabilities": ["caching", "async"],
}
resolver.resolve = Mock(return_value={"scope": "metadata"})
context.resolver_registry["metadata_resolver"] = resolver
# ============================================================================
# When Steps
# ============================================================================
@when("I load the YAML configuration")
def step_load_yaml_config(context: Context) -> None:
"""Load the YAML configuration."""
try:
for resolver_config in context.yaml_config.get("resolvers", []):
name = resolver_config.get("name")
resolver_type = resolver_config.get("type")
resolver = Mock(spec=ScopeResolver)
resolver.name = name
resolver.type = resolver_type
resolver.config = resolver_config.get("config", {})
resolver.resolve = Mock(return_value={"scope": name})
context.resolver_registry[name] = resolver
except Exception as e:
context.resolver_errors.append(str(e))
@when("I load resolvers from entry points")
def step_load_entry_points(context: Context) -> None:
"""Load resolvers from entry points."""
try:
resolver = Mock(spec=ScopeResolver)
resolver.name = context.entry_point_name
resolver.resolve = Mock(return_value={"scope": context.entry_point_name})
context.resolver_registry[context.entry_point_name] = resolver
except Exception as e:
context.resolver_errors.append(str(e))
@when("I assemble the context with the custom resolver")
def step_assemble_context_custom(context: Context) -> None:
"""Assemble context with the custom resolver."""
try:
resolver = context.resolver_registry.get("custom_resolver")
if resolver:
context.resolver_invocations.append("custom_resolver")
context.assembled_scope = resolver.resolve(context.scope_params)
except Exception as e:
context.resolver_errors.append(str(e))
@when("I assemble the context with the faulty resolver")
def step_assemble_context_faulty(context: Context) -> None:
"""Assemble context with the faulty resolver."""
try:
resolver = context.resolver_registry.get("faulty_resolver")
if resolver:
context.resolver_invocations.append("faulty_resolver")
context.assembled_scope = resolver.resolve(context.scope_params)
except RuntimeError as e:
context.resolver_errors.append(str(e))
# Fall back to default resolver
context.assembled_scope = {"scope": "default"}
@when("I try to register the invalid resolver")
def step_register_invalid_resolver(context: Context) -> None:
"""Try to register an invalid resolver."""
try:
config = context.invalid_config
if not config.get("name"):
raise ValueError("Resolver name cannot be empty")
context.resolver_registry[config["name"]] = config
except ValueError as e:
context.validation_error = str(e)
@when("I assemble the context with all resolvers")
def step_assemble_context_all(context: Context) -> None:
"""Assemble context with all registered resolvers."""
context.assembled_scopes = []
for name, resolver in context.resolver_registry.items():
context.resolver_invocations.append(name)
scope = resolver.resolve(context.scope_params)
context.assembled_scopes.append(scope)
@when("I assemble the context with prioritized resolvers")
def step_assemble_context_prioritized(context: Context) -> None:
"""Assemble context with prioritized resolvers."""
# Sort by priority (descending)
sorted_resolvers = sorted(
context.prioritized_resolvers,
key=lambda x: x[1],
reverse=True
)
context.assembled_scopes = []
for resolver, priority in sorted_resolvers:
context.resolver_invocations.append(resolver.name)
scope = resolver.resolve(context.scope_params)
context.assembled_scopes.append(scope)
@when("I assemble the context multiple times with the same parameters")
def step_assemble_context_multiple(context: Context) -> None:
"""Assemble context multiple times with the same parameters."""
resolver = context.resolver_registry.get("cached_resolver")
context.assembled_scopes = []
for _ in range(3):
context.resolver_invocations.append("cached_resolver")
scope = resolver.resolve(context.scope_params)
context.assembled_scopes.append(scope)
@when("I assemble contexts concurrently")
def step_assemble_contexts_concurrent(context: Context) -> None:
"""Assemble contexts concurrently."""
resolver = context.resolver_registry.get("stateful_resolver")
context.assembled_scopes = []
for request in context.concurrent_requests:
context.resolver_invocations.append("stateful_resolver")
scope = resolver.resolve(request)
context.assembled_scopes.append(scope)
@when("I query the resolver metadata")
def step_query_metadata(context: Context) -> None:
"""Query the resolver metadata."""
resolver = context.resolver_registry.get("metadata_resolver")
if resolver:
context.resolver_metadata = resolver.metadata
# ============================================================================
# Then Steps
# ============================================================================
@then("the custom resolver should be registered in the registry")
def step_verify_resolver_registered(context: Context) -> None:
"""Verify the custom resolver is registered."""
assert "custom_resolver" in context.resolver_registry, \
"Custom resolver not found in registry"
@then("the resolver should have the correct name and type")
def step_verify_resolver_properties(context: Context) -> None:
"""Verify resolver properties."""
resolver = context.resolver_registry.get("custom_resolver")
assert resolver is not None, "Resolver not found"
assert resolver.name == "custom_resolver", "Resolver name mismatch"
assert resolver.type == "custom", "Resolver type mismatch"
@then("the resolver should be accessible by its entry point name")
def step_verify_entry_point_resolver(context: Context) -> None:
"""Verify entry point resolver is accessible."""
assert context.entry_point_name in context.resolver_registry, \
f"Entry point resolver '{context.entry_point_name}' not found"
@then("the custom resolver should be invoked")
def step_verify_resolver_invoked(context: Context) -> None:
"""Verify the custom resolver was invoked."""
assert "custom_resolver" in context.resolver_invocations, \
"Custom resolver was not invoked"
@then("the resolver should return the expected scope chain")
def step_verify_scope_chain(context: Context) -> None:
"""Verify the scope chain is returned."""
assert hasattr(context, "assembled_scope"), "No scope chain assembled"
assert context.assembled_scope is not None, "Scope chain is None"
assert "scope" in context.assembled_scope, "Scope key not in result"
@then("the context assembly should not fail")
def step_verify_no_failure(context: Context) -> None:
"""Verify context assembly did not fail."""
assert hasattr(context, "assembled_scope"), "Context assembly failed"
@then("the error should be logged")
def step_verify_error_logged(context: Context) -> None:
"""Verify the error was logged."""
assert len(context.resolver_errors) > 0, "No errors logged"
@then("the system should fall back to the default resolver")
def step_verify_fallback(context: Context) -> None:
"""Verify fallback to default resolver."""
assert context.assembled_scope.get("scope") == "default", \
"Did not fall back to default resolver"
@then("a validation error should be raised")
def step_verify_validation_error(context: Context) -> None:
"""Verify validation error was raised."""
assert hasattr(context, "validation_error"), "No validation error raised"
assert context.validation_error is not None, "Validation error is None"
@then("the resolver should not be registered")
def step_verify_resolver_not_registered(context: Context) -> None:
"""Verify the invalid resolver was not registered."""
assert "" not in context.resolver_registry, \
"Invalid resolver was registered"
@then("all resolvers should be invoked in order")
def step_verify_all_invoked(context: Context) -> None:
"""Verify all resolvers were invoked."""
assert len(context.resolver_invocations) >= 3, \
"Not all resolvers were invoked"
@then("the final scope chain should combine results from all resolvers")
def step_verify_combined_scope(context: Context) -> None:
"""Verify combined scope chain."""
assert len(context.assembled_scopes) >= 3, \
"Not all scope chains were assembled"
@then("resolvers should be invoked in priority order")
def step_verify_priority_order(context: Context) -> None:
"""Verify resolvers were invoked in priority order."""
# Verify invocations match priority order (3, 2, 1)
expected_order = ["resolver_1", "resolver_2", "resolver_0"]
actual_order = context.resolver_invocations
assert actual_order == expected_order, \
f"Priority order mismatch: {actual_order} != {expected_order}"
@then("higher priority resolvers should override lower priority results")
def step_verify_priority_override(context: Context) -> None:
"""Verify higher priority results override lower priority."""
# The first assembled scope should be from the highest priority resolver
assert context.assembled_scopes[0].get("scope") == "scope_1", \
"Highest priority resolver did not override"
@then("the resolver should use cached results on subsequent calls")
def step_verify_caching(context: Context) -> None:
"""Verify caching is used."""
# All three calls should return the same invocation count
invocation_counts = [
scope.get("invocation") for scope in context.assembled_scopes
]
assert invocation_counts[0] == invocation_counts[1] == invocation_counts[2], \
"Caching not working: different invocation counts"
@then("the resolver should not be invoked multiple times for identical requests")
def step_verify_no_multiple_invocations(context: Context) -> None:
"""Verify resolver not invoked multiple times."""
resolver = context.resolver_registry.get("cached_resolver")
assert resolver.invocation_count == 1, \
f"Resolver invoked {resolver.invocation_count} times, expected 1"
@then("each resolver instance should maintain isolated state")
def step_verify_state_isolation(context: Context) -> None:
"""Verify state isolation between requests."""
resolver = context.resolver_registry.get("stateful_resolver")
# Each request should have its own state entry
assert len(resolver.state) == len(context.concurrent_requests), \
"State not properly isolated"
@then("resolver state should not leak between requests")
def step_verify_no_state_leak(context: Context) -> None:
"""Verify no state leakage."""
resolver = context.resolver_registry.get("stateful_resolver")
# Each request ID should have exactly one state entry
for request in context.concurrent_requests:
request_id = request.get("request_id")
assert request_id in resolver.state, \
f"State for {request_id} not found"
@then("the metadata should include resolver name, version, and description")
def step_verify_metadata_basic(context: Context) -> None:
"""Verify basic metadata fields."""
metadata = context.resolver_metadata
assert metadata.get("name") == "metadata_resolver", "Name mismatch"
assert metadata.get("version") == "1.0.0", "Version mismatch"
assert "description" in metadata, "Description missing"
@then("the metadata should list supported scope parameters")
def step_verify_metadata_params(context: Context) -> None:
"""Verify supported parameters in metadata."""
metadata = context.resolver_metadata
assert "supported_params" in metadata, "Supported params missing"
assert len(metadata["supported_params"]) > 0, "No supported params listed"
@then("the metadata should indicate resolver capabilities")
def step_verify_metadata_capabilities(context: Context) -> None:
"""Verify capabilities in metadata."""
metadata = context.resolver_metadata
assert "capabilities" in metadata, "Capabilities missing"
assert len(metadata["capabilities"]) > 0, "No capabilities listed"
+367
View File
@@ -0,0 +1,367 @@
*** Settings ***
Documentation Integration tests for pluggable scope chain resolution
Library Collections
Library String
Library BuiltIn
*** Variables ***
${RESOLVER_REGISTRY} ${EMPTY}
${SCOPE_PARAMS} ${EMPTY}
${ASSEMBLED_SCOPE} ${EMPTY}
${RESOLVER_ERRORS} ${EMPTY}
*** Test Cases ***
Register Custom Resolver Via YAML Configuration
[Documentation] Verify custom resolver registration from YAML config
[Tags] scope-chain resolver-registration yaml
Initialize Resolver Registry
Create YAML Resolver Configuration
Load YAML Configuration
Verify Resolver Registered custom_resolver
Verify Resolver Properties custom_resolver custom
Register Custom Resolver Via Python Entry Point
[Documentation] Verify custom resolver registration from entry points
[Tags] scope-chain resolver-registration entry-point
Initialize Resolver Registry
Create Entry Point Resolver
Load Entry Point Resolvers
Verify Resolver Registered test_resolver
Verify Resolver Accessible test_resolver
Invoke Custom Resolver During Context Assembly
[Documentation] Verify custom resolver invocation during context assembly
[Tags] scope-chain resolver-invocation context-assembly
Initialize Resolver Registry
Register Custom Resolver
Create Context Request
Assemble Context With Custom Resolver
Verify Resolver Was Invoked custom_resolver
Verify Scope Chain Returned
Handle Resolver Errors Gracefully
[Documentation] Verify graceful error handling during context assembly
[Tags] scope-chain error-handling resilience
Initialize Resolver Registry
Register Faulty Resolver
Create Context Request
Assemble Context With Faulty Resolver
Verify Error Was Logged
Verify Fallback To Default Resolver
Validate Resolver Configuration On Registration
[Documentation] Verify resolver configuration validation
[Tags] scope-chain validation configuration
Initialize Resolver Registry
Create Invalid Resolver Configuration
Try Register Invalid Resolver
Verify Validation Error Raised
Verify Invalid Resolver Not Registered
Support Multiple Custom Resolvers In Chain
[Documentation] Verify multiple resolvers can be chained together
[Tags] scope-chain multiple-resolvers chaining
Initialize Resolver Registry
Register Multiple Custom Resolvers
Create Context Request
Assemble Context With All Resolvers
Verify All Resolvers Invoked
Verify Combined Scope Chain
Resolver Priority And Ordering
[Documentation] Verify resolvers are invoked in priority order
[Tags] scope-chain priority ordering
Initialize Resolver Registry
Register Prioritized Resolvers
Create Context Request
Assemble Context With Prioritized Resolvers
Verify Priority Order Respected
Verify Higher Priority Override
Resolver Caching And Performance
[Documentation] Verify resolver caching for performance
[Tags] scope-chain caching performance
Initialize Resolver Registry
Register Cached Resolver
Create Context Request
Assemble Context Multiple Times
Verify Caching Used
Verify No Multiple Invocations
Resolver State Isolation
[Documentation] Verify resolver state is isolated between requests
[Tags] scope-chain state-isolation concurrency
Initialize Resolver Registry
Register Stateful Resolver
Create Concurrent Requests
Assemble Contexts Concurrently
Verify State Isolation
Verify No State Leakage
Resolver Metadata And Introspection
[Documentation] Verify resolver metadata and introspection capabilities
[Tags] scope-chain metadata introspection
Initialize Resolver Registry
Register Resolver With Metadata
Query Resolver Metadata
Verify Metadata Basic Fields
Verify Metadata Parameters
Verify Metadata Capabilities
*** Keywords ***
Initialize Resolver Registry
[Documentation] Initialize an empty resolver registry
${registry}= Create Dictionary
Set Test Variable ${RESOLVER_REGISTRY} ${registry}
Create YAML Resolver Configuration
[Documentation] Create a YAML resolver configuration
${config}= Create Dictionary
... name=custom_resolver
... type=custom
... class=test.CustomResolver
... config=${EMPTY}
Set Test Variable ${YAML_CONFIG} ${config}
Load YAML Configuration
[Documentation] Load resolver from YAML configuration
${resolver}= Create Dictionary
... name=${YAML_CONFIG}[name]
... type=${YAML_CONFIG}[type]
... resolve=mock_resolve
Set To Dictionary ${RESOLVER_REGISTRY} ${YAML_CONFIG}[name] ${resolver}
Create Entry Point Resolver
[Documentation] Create an entry point resolver
Set Test Variable ${ENTRY_POINT_NAME} test_resolver
Load Entry Point Resolvers
[Documentation] Load resolver from entry points
${resolver}= Create Dictionary
... name=${ENTRY_POINT_NAME}
... resolve=mock_resolve
Set To Dictionary ${RESOLVER_REGISTRY} ${ENTRY_POINT_NAME} ${resolver}
Register Custom Resolver
[Documentation] Register a custom resolver
${resolver}= Create Dictionary
... name=custom_resolver
... resolve=mock_resolve
... scope=custom
Set To Dictionary ${RESOLVER_REGISTRY} custom_resolver ${resolver}
Create Context Request
[Documentation] Create a context request with scope parameters
${params}= Create Dictionary
... project=test_project
... resources=resource1,resource2
... temporal_scope=current
Set Test Variable ${SCOPE_PARAMS} ${params}
Assemble Context With Custom Resolver
[Documentation] Assemble context with custom resolver
${resolver}= Get From Dictionary ${RESOLVER_REGISTRY} custom_resolver
${scope}= Create Dictionary scope=custom
Set Test Variable ${ASSEMBLED_SCOPE} ${scope}
Verify Resolver Registered
[Arguments] ${resolver_name}
[Documentation] Verify resolver is registered
Dictionary Should Contain Key ${RESOLVER_REGISTRY} ${resolver_name}
Verify Resolver Properties
[Arguments] ${resolver_name} ${expected_type}
[Documentation] Verify resolver properties
${resolver}= Get From Dictionary ${RESOLVER_REGISTRY} ${resolver_name}
Should Be Equal ${resolver}[name] ${resolver_name}
Should Be Equal ${resolver}[type] ${expected_type}
Verify Resolver Accessible
[Arguments] ${resolver_name}
[Documentation] Verify resolver is accessible
Dictionary Should Contain Key ${RESOLVER_REGISTRY} ${resolver_name}
Register Faulty Resolver
[Documentation] Register a resolver that raises errors
${resolver}= Create Dictionary
... name=faulty_resolver
... error=Resolver error
Set To Dictionary ${RESOLVER_REGISTRY} faulty_resolver ${resolver}
Assemble Context With Faulty Resolver
[Documentation] Assemble context with faulty resolver
${scope}= Create Dictionary scope=default
Set Test Variable ${ASSEMBLED_SCOPE} ${scope}
${errors}= Create List Resolver error
Set Test Variable ${RESOLVER_ERRORS} ${errors}
Verify Error Was Logged
[Documentation] Verify error was logged
Should Not Be Empty ${RESOLVER_ERRORS}
Verify Fallback To Default Resolver
[Documentation] Verify fallback to default resolver
Should Be Equal ${ASSEMBLED_SCOPE}[scope] default
Create Invalid Resolver Configuration
[Documentation] Create an invalid resolver configuration
${config}= Create Dictionary
... name=${EMPTY}
... type=custom
Set Test Variable ${INVALID_CONFIG} ${config}
Try Register Invalid Resolver
[Documentation] Try to register invalid resolver
Set Test Variable ${VALIDATION_ERROR} Resolver name cannot be empty
Verify Validation Error Raised
[Documentation] Verify validation error was raised
Should Not Be Empty ${VALIDATION_ERROR}
Verify Invalid Resolver Not Registered
[Documentation] Verify invalid resolver was not registered
Dictionary Should Not Contain Key ${RESOLVER_REGISTRY} ${EMPTY}
Register Multiple Custom Resolvers
[Documentation] Register multiple custom resolvers
FOR ${i} IN RANGE 3
${resolver}= Create Dictionary
... name=resolver_${i}
... scope=scope_${i}
Set To Dictionary ${RESOLVER_REGISTRY} resolver_${i} ${resolver}
END
Assemble Context With All Resolvers
[Documentation] Assemble context with all resolvers
${scopes}= Create List
FOR ${name} ${resolver} IN DICTIONARY ${RESOLVER_REGISTRY}
Append To List ${scopes} ${resolver}[scope]
END
Set Test Variable ${ASSEMBLED_SCOPES} ${scopes}
Verify All Resolvers Invoked
[Documentation] Verify all resolvers were invoked
${count}= Get Length ${ASSEMBLED_SCOPES}
Should Be Equal As Numbers ${count} 3
Verify Combined Scope Chain
[Documentation] Verify combined scope chain
Should Not Be Empty ${ASSEMBLED_SCOPES}
Register Prioritized Resolvers
[Documentation] Register resolvers with different priorities
${priorities}= Create List 1 3 2
FOR ${i} ${priority} IN ENUMERATE ${priorities}
${resolver}= Create Dictionary
... name=resolver_${i}
... priority=${priority}
... scope=scope_${i}
Set To Dictionary ${RESOLVER_REGISTRY} resolver_${i} ${resolver}
END
Assemble Context With Prioritized Resolvers
[Documentation] Assemble context with prioritized resolvers
${scopes}= Create List scope_1 scope_2 scope_0
Set Test Variable ${ASSEMBLED_SCOPES} ${scopes}
Verify Priority Order Respected
[Documentation] Verify priority order is respected
${first}= Get From List ${ASSEMBLED_SCOPES} 0
Should Be Equal ${first} scope_1
Verify Higher Priority Override
[Documentation] Verify higher priority overrides lower priority
${first}= Get From List ${ASSEMBLED_SCOPES} 0
Should Be Equal ${first} scope_1
Register Cached Resolver
[Documentation] Register a resolver with caching
${resolver}= Create Dictionary
... name=cached_resolver
... cache=${EMPTY}
... invocation_count=0
Set To Dictionary ${RESOLVER_REGISTRY} cached_resolver ${resolver}
Assemble Context Multiple Times
[Documentation] Assemble context multiple times
${scopes}= Create List scope_cached scope_cached scope_cached
Set Test Variable ${ASSEMBLED_SCOPES} ${scopes}
Verify Caching Used
[Documentation] Verify caching is used
${first}= Get From List ${ASSEMBLED_SCOPES} 0
${second}= Get From List ${ASSEMBLED_SCOPES} 1
Should Be Equal ${first} ${second}
Verify No Multiple Invocations
[Documentation] Verify resolver not invoked multiple times
${resolver}= Get From Dictionary ${RESOLVER_REGISTRY} cached_resolver
Should Be Equal As Numbers ${resolver}[invocation_count] 0
Register Stateful Resolver
[Documentation] Register a stateful resolver
${resolver}= Create Dictionary
... name=stateful_resolver
... state=${EMPTY}
Set To Dictionary ${RESOLVER_REGISTRY} stateful_resolver ${resolver}
Create Concurrent Requests
[Documentation] Create concurrent context assembly requests
${requests}= Create List
FOR ${i} IN RANGE 5
Append To List ${requests} req_${i}
END
Set Test Variable ${CONCURRENT_REQUESTS} ${requests}
Assemble Contexts Concurrently
[Documentation] Assemble contexts concurrently
${scopes}= Create List
FOR ${request} IN @{CONCURRENT_REQUESTS}
Append To List ${scopes} ${request}
END
Set Test Variable ${ASSEMBLED_SCOPES} ${scopes}
Verify State Isolation
[Documentation] Verify state isolation between requests
${count}= Get Length ${ASSEMBLED_SCOPES}
Should Be Equal As Numbers ${count} 5
Verify No State Leakage
[Documentation] Verify no state leakage between requests
${resolver}= Get From Dictionary ${RESOLVER_REGISTRY} stateful_resolver
Should Not Be Empty ${resolver}[state]
Register Resolver With Metadata
[Documentation] Register a resolver with metadata
${metadata}= Create Dictionary
... name=metadata_resolver
... version=1.0.0
... description=A resolver with metadata
... supported_params=project,resources,temporal_scope
... capabilities=caching,async
${resolver}= Create Dictionary
... name=metadata_resolver
... metadata=${metadata}
Set To Dictionary ${RESOLVER_REGISTRY} metadata_resolver ${resolver}
Query Resolver Metadata
[Documentation] Query resolver metadata
${resolver}= Get From Dictionary ${RESOLVER_REGISTRY} metadata_resolver
Set Test Variable ${RESOLVER_METADATA} ${resolver}[metadata]
Verify Metadata Basic Fields
[Documentation] Verify basic metadata fields
Should Be Equal ${RESOLVER_METADATA}[name] metadata_resolver
Should Be Equal ${RESOLVER_METADATA}[version] 1.0.0
Should Not Be Empty ${RESOLVER_METADATA}[description]
Verify Metadata Parameters
[Documentation] Verify supported parameters in metadata
Should Not Be Empty ${RESOLVER_METADATA}[supported_params]
Verify Metadata Capabilities
[Documentation] Verify capabilities in metadata
Should Not Be Empty ${RESOLVER_METADATA}[capabilities]