Files
cleveragents-core/robot/rxpy_route_validation.robot
T
CoreRasurae e4c01492d5
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 15s
CI / lint (pull_request) Successful in 3m18s
CI / quality (pull_request) Successful in 3m47s
CI / typecheck (pull_request) Successful in 3m52s
CI / security (pull_request) Successful in 4m1s
CI / integration_tests (pull_request) Successful in 6m53s
CI / unit_tests (pull_request) Successful in 7m9s
CI / docker (pull_request) Successful in 1m9s
CI / e2e_tests (pull_request) Successful in 11m5s
CI / coverage (pull_request) Successful in 11m15s
CI / status-check (pull_request) Successful in 1s
CI / lint (push) Successful in 3m16s
CI / build (push) Successful in 31s
CI / typecheck (push) Successful in 4m0s
CI / security (push) Successful in 4m1s
CI / benchmark-regression (push) Has been skipped
CI / quality (push) Successful in 4m8s
CI / integration_tests (push) Successful in 6m10s
CI / unit_tests (push) Successful in 7m35s
CI / docker (push) Successful in 1m9s
CI / e2e_tests (push) Successful in 9m58s
CI / coverage (push) Successful in 11m7s
CI / status-check (push) Successful in 1s
CI / benchmark-publish (push) Failing after 17m22s
CI / benchmark-regression (pull_request) Successful in 56m7s
refactor(cli): align actor run signature with spec positional args
Aligned the `agents actor run` command signature with the
specification by introducing positional NAME and PROMPT arguments.
The --config/-c option is preserved as an optional fallback for
direct YAML invocation. When NAME is provided without --config,
the actor is resolved from the Actor Registry.

Updated both actor_run.py and actor.py run commands. Added backward
compatibility: if --config is provided, it takes precedence over
name-based resolution.

Review fixes applied (code review round 1):
- P1-1: Narrowed bare `except Exception` to `except NotFoundError`
  in _resolve_config_files to avoid masking infrastructure errors.
- P1-2: Moved _resolve_config_files call inside the try block in
  run() so container/registry init errors get user-friendly messages.
  Added `except click.exceptions.Exit: raise` to let typer.Exit
  propagate through the broadened try scope.
- P1-3: Added atexit.register cleanup for temp files created by
  _resolve_config_files (resource leak fix).
- P1-4: Added CHANGELOG.md entry for the breaking CLI change.
- P2-1: Extracted duplicated _resolve_config_files to shared module
  `_resolve_actor.py`; both actor.py and actor_run.py now import it.
- P2-2: Added guard for actors with no configuration data
  (config_blob=None) to produce a clear error instead of invalid YAML.
- P2-3/P2-4: Added 5 BDD scenarios exercising the real
  resolve_config_files function (registry path, yaml_text path,
  config_blob fallback, no-config-data error, not-found error).
- P2-5: Added @coverage tags to all new BDD scenarios.
- P3-1: Added timeout=120s and on_timeout=kill to Robot tests.
- Fixed rxpy_route_validation.robot tests that used the removed
  --prompt/-p option (replaced with positional NAME + PROMPT args).

Review fixes applied (code review round 2):
- P2-1: Aligned actor_run.py exception handler from
  `CleverAgentsException` to `CleverAgentsError`, matching actor.py
  so infrastructure errors from registry resolution get user-friendly
  messages instead of falling through to the generic handler.
- P2-2: Changed `yaml.dump` to `yaml.safe_dump` in _resolve_actor.py
  for fail-fast behavior on unexpected types, consistent with the
  codebase's dominant pattern.
- P3-6: Replaced defensive `getattr(actor, ...)` calls with direct
  Pydantic model attribute access (`actor.yaml_text`, `actor.config_blob`)
  for type-checker coverage.
- P3-1: Switched BDD temp file cleanup from post-assertion
  `unlink()` to `context.add_cleanup()` for leak-proof teardown.
- P2-3/P3-2/P3-3/P3-4: Added 3 BDD edge-case scenarios
  (empty config_blob dict, infrastructure error propagation, empty
  string name) and 1 Robot test case (actor_app registry resolution).

Review fixes applied (code review round 3):
- P1-1: Migrated 48 remaining `-p` invocations across 9 Robot test
  files to the new positional `NAME PROMPT` pattern (context_delete_all_yes,
  load_context_test, scientific_paper_e2e_test, routing_prefix_stripping,
  scientific_paper_basic, scientific_paper_writer_test,
  context_management_test, initial_next_command_test,
  system_prompt_template_rendering).
- P2-1: Documented `--config/-c` as a spec deviation in
  `_resolve_actor.py` module docstring (spec lines 4562-4566 define
  `actor run` with no --config option; issue #901 AC accepts keeping
  it as optional).
- P2-2: Corrected `--config` help text from "fallback" to "overrides
  registry-based name resolution" — the option takes precedence, not
  the other way around.
- P3-1: Added `.strip()` to `yaml_text` emptiness check in
  `_resolve_actor.py` to handle whitespace-only values that would
  otherwise bypass the `config_blob` fallback.
- P3-2: Added `from None` to the no-configuration-data
  `typer.Exit(code=2)` for consistency with the not-found path.
- P3-3: Added BDD scenario testing `--config` precedence for
  `actor_run_app` (was only tested for `actor_app`).
- P3-4: Strengthened config_blob BDD scenario to verify generated
  YAML is parseable via `yaml.safe_load` round-trip.
- P3-7: Replaced hardcoded `/tmp/dummy.yaml` with
  `tempfile.gettempdir()` for portability.
- P3-8: Moved 5 inline imports to module level per CONTRIBUTING.md
  §1292-1294 (3x `import click`, 1x InfrastructureError in steps;
  1x `import typer` in robot helper).
- P3-9: Added `encoding="utf-8"` to `_write_yaml` in Robot helper
  for consistency with production code.

Review fixes applied (code review round 4):
- P2-1: Wrapped `yaml.safe_dump` in `_resolve_actor.py` with
  `try/except yaml.YAMLError` so non-serialisable config_blob values
  produce a user-friendly error message instead of a raw traceback.
- P2-2: Moved remaining inline `import yaml` to module level in
  `actor_run_signature_steps.py` per CONTRIBUTING.md §1292-1294.
- P2-3: Replaced 3 bare `assert` statements in Robot helper
  `helper_actor_run_signature.py` with diagnostic `if/print/sys.exit`
  pattern matching the rest of the file, improving failure diagnostics.

Review fixes applied (code review round 5):
- P3-4: Replaced per-call `atexit.register(lambda)` in
  `_resolve_actor.py` with a module-level `_temp_files` set and a
  single `atexit` handler (`_cleanup_temp_files`) to prevent
  unbounded handler accumulation in same-process usage (e.g. test
  suites running multiple CliRunner invocations).
- P3-1/P3-2/P3-3: Added 4 BDD scenarios: whitespace-only
  `yaml_text` fallback to config_blob, config-precedence
  registry-not-consulted assertion for both `actor_app` and
  `actor_run_app`, multiple `--config` files with positional NAME.
- P4-1: Strengthened error-path BDD assertions to verify error
  message content (not-found, no-config-data, serialisation-error)
  alongside exit codes via captured stderr.
- P4-2: Added Robot test case for `actor_app` unknown-name error
  path and corresponding helper function.

ISSUES CLOSED: #901
2026-03-26 13:41:31 +00:00

352 lines
12 KiB
Plaintext

*** Settings ***
Documentation Integration tests for RxPY route validation in run command
Library Process
Library OperatingSystem
Library String
Library Collections
Resource ${CURDIR}/v2_paths.resource
Suite Setup Setup Test Environment
Suite Teardown Cleanup Test Environment
*** Variables ***
${PYTHON} python
${TEST_DIR} ${TEMPDIR}/rxpy_validation_test
${RXPY_CONFIG} ${TEST_DIR}/rxpy_config.yaml
${LANGGRAPH_CONFIG} ${TEST_DIR}/langgraph_config.yaml
${CONTEXT_DIR} ${TEST_DIR}/test_contexts
${UNIQUE_ID} ${EMPTY}
${RXPY_RUN_MODE_ERROR} RxPY stream routes are only supported in run mode unless allowed. Use --allow-rxpy-in-run-mode to bypass.
*** Test Cases ***
Test Run Command Rejects RxPY Routes
[Documentation] Verify run command rejects RxPY stream routes
[Tags] rxpy validation run
Create RxPY Config File
# Try to run with RxPY routes - should fail
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${RXPY_CONFIG}
... --unsafe
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
Should Not Be Equal As Integers ${result.rc} 0 Command should have failed
Should Contain ${result.stdout} ${RXPY_RUN_MODE_ERROR}
Should Contain ${result.stdout} allow-rxpy-in-run-mode
Test Run Command With Context Does Not Create Context
[Documentation] Verify that no context is created when RxPY validation fails
[Tags] rxpy validation context
${context_name} = Set Variable rxpy_test_ctx_${UNIQUE_ID}
Create RxPY Config File
# Ensure context doesn't exist
Delete Context If Exists ${context_name}
# Try to run with --context flag
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${RXPY_CONFIG}
... --unsafe
... --context ${context_name}
... --context-dir ${CONTEXT_DIR}
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
Should Not Be Equal As Integers ${result.rc} 0 Command should have failed
Should Contain ${result.stdout} ${RXPY_RUN_MODE_ERROR}
# Verify context was NOT created
${context_exists} = Context Exists ${context_name}
Should Not Be True ${context_exists} Context should not have been created
Test Run Command With LangGraph Routes Succeeds
[Documentation] Verify run command works with LangGraph routes
[Tags] langgraph validation run slow
Create LangGraph Config File
# Run with LangGraph routes - should succeed
${context_name} = Set Variable rxpy_test_ctx_${UNIQUE_ID}
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${LANGGRAPH_CONFIG}
... --context-dir ${CONTEXT_DIR}
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
# For now, might fail on actual execution but shouldn't have route validation error
${output} = Set Variable ${result.stdout}
Should Not Contain ${output} ${RXPY_RUN_MODE_ERROR}
Test Run Command Accepts RxPY Routes When Allowed
[Documentation] Verify actor run works with RxPY routes when allowed
[Tags] rxpy run slow
Create RxPY Config File
${context_name} = Set Variable rxpy_test_ctx_${UNIQUE_ID}
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${RXPY_CONFIG}
... --unsafe
... --allow-rxpy-in-run-mode
... --context ${context_name}
... --context-dir ${CONTEXT_DIR}
... test-actor test
... stderr=STDOUT
... timeout=120s on_timeout=kill
Should Be Equal As Integers ${result.rc} 0
Should Not Contain ${result.stdout} ${RXPY_RUN_MODE_ERROR}
Test Error Message Content
[Documentation] Verify error message contains helpful information
[Tags] rxpy validation error-message
Create RxPY Config File
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${RXPY_CONFIG}
... --unsafe
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
Should Not Be Equal As Integers ${result.rc} 0
# Check error message contains all important information
${output} = Convert To Lower Case ${result.stdout}
Should Contain ${output} nested
Should Contain ${output} completion detection
Should Contain ${output} run mode
Should Contain ${output} langgraph
Test Run With Nested Switch Operators
[Documentation] Verify run command rejects configs with nested switch operators
[Tags] rxpy nested validation
Create Nested Switch Config File
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${TEST_DIR}/nested_switch_config.yaml
... --unsafe
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
Should Not Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} ${RXPY_RUN_MODE_ERROR}
Test Mixed Route Types Detection
[Documentation] Verify detection works with mix of RxPY and LangGraph routes
[Tags] rxpy mixed validation
Create Mixed Routes Config File
# Should detect RxPY routes even if LangGraph routes also present
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${TEST_DIR}/mixed_config.yaml
... --unsafe
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
Should Not Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} ${RXPY_RUN_MODE_ERROR}
Test Context File Not Created On Multiple Runs
[Documentation] Verify repeated failed runs do not create context
[Tags] rxpy context validation
${context_name} = Set Variable rxpy_multi_test_${UNIQUE_ID}
Create RxPY Config File
Delete Context If Exists ${context_name}
# Run multiple times
FOR ${i} IN RANGE 3
${result} = Run Process ${PYTHON} -m cleveragents actor run
... -c ${RXPY_CONFIG}
... --unsafe
... --context ${context_name}
... --context-dir ${CONTEXT_DIR}
... test-actor test
... stderr=STDOUT timeout=120s on_timeout=kill
Should Not Be Equal As Integers ${result.rc} 0
END
# Verify context still doesn't exist after multiple attempts
${context_exists} = Context Exists ${context_name}
Should Not Be True ${context_exists}
*** Keywords ***
Setup Test Environment
[Documentation] Setup test environment
Create Directory ${TEST_DIR}
Create Directory ${CONTEXT_DIR}
# Generate unique ID for this test run
${timestamp} = Get Time epoch
Set Suite Variable ${UNIQUE_ID} ${timestamp}
Cleanup Test Environment
[Documentation] Clean up test environment
Remove Directory ${TEST_DIR} recursive=True
Create RxPY Config File
[Documentation] Create a test config with RxPY stream routes
${config} = Catenate SEPARATOR=\n
... actors:
... ${SPACE*2}echo_actor:
... ${SPACE*4}type: tool
... ${SPACE*4}config:
... ${SPACE*6}tools:
... ${SPACE*8}- echo
... ${SPACE*6}safe_mode: true
... ${EMPTY}
... routes:
... ${SPACE*2}echo_stream:
... ${SPACE*4}type: stream
... ${SPACE*4}stream_type: cold
... ${SPACE*4}operators:
... ${SPACE*6}- type: map
... ${SPACE*8}params:
... ${SPACE*10}actor: echo_actor
... ${SPACE*4}publications:
... ${SPACE*6}- __output__
... ${EMPTY}
... merges:
... ${SPACE*2}- sources: [__input__]
... ${SPACE*4}target: echo_stream
Create File ${RXPY_CONFIG} ${config}
Create LangGraph Config File
[Documentation] Create a test config with LangGraph routes
${config} = Catenate SEPARATOR=\n
... actors:
... ${SPACE*2}echo_actor:
... ${SPACE*4}type: tool
... ${SPACE*4}config:
... ${SPACE*6}tools:
... ${SPACE*8}- echo
... ${SPACE*6}safe_mode: true
... ${EMPTY}
... routes:
... ${SPACE*2}main:
... ${SPACE*4}type: graph
... ${SPACE*4}nodes:
... ${SPACE*6}- id: start
... ${SPACE*8}actor: echo_actor
... ${SPACE*4}edges:
... ${SPACE*6}- from: __input__
... ${SPACE*8}to: start
... ${SPACE*6}- from: start
... ${SPACE*8}to: __output__
... ${EMPTY}
... merges:
... ${SPACE*2}- sources: [__input__]
... ${SPACE*4}target: main
Create File ${LANGGRAPH_CONFIG} ${config}
Create Nested Switch Config File
[Documentation] Create a config with nested switch operators
${config} = Catenate SEPARATOR=\n
... actors:
... ${SPACE*2}test_actor:
... ${SPACE*4}type: tool
... ${SPACE*4}config:
... ${SPACE*6}tools:
... ${SPACE*8}- echo
... ${SPACE*6}safe_mode: true
... ${EMPTY}
... routes:
... ${SPACE*2}main:
... ${SPACE*4}type: stream
... ${SPACE*4}stream_type: cold
... ${SPACE*4}operators:
... ${SPACE*6}- type: switch
... ${SPACE*8}params:
... ${SPACE*10}cases:
... ${SPACE*12}- condition: "test"
... ${SPACE*14}route: nested_route
... ${SPACE*2}nested_route:
... ${SPACE*4}type: stream
... ${SPACE*4}stream_type: cold
... ${SPACE*4}operators:
... ${SPACE*6}- type: map
... ${SPACE*8}params:
... ${SPACE*10}actor: test_actor
... ${SPACE*4}publications:
... ${SPACE*6}- __output__
... ${EMPTY}
... merges:
... ${SPACE*2}- sources: [__input__]
... ${SPACE*4}target: main
Create File ${TEST_DIR}/nested_switch_config.yaml ${config}
Create Mixed Routes Config File
[Documentation] Create a config with both RxPY and LangGraph routes
${config} = Catenate SEPARATOR=\n
... actors:
... ${SPACE*2}test_actor:
... ${SPACE*4}type: tool
... ${SPACE*4}config:
... ${SPACE*6}tools:
... ${SPACE*8}- echo
... ${SPACE*6}safe_mode: true
... ${EMPTY}
... routes:
... ${SPACE*2}stream_route:
... ${SPACE*4}type: stream
... ${SPACE*4}stream_type: cold
... ${SPACE*4}operators:
... ${SPACE*6}- type: map
... ${SPACE*8}params:
... ${SPACE*10}actor: test_actor
... ${SPACE*4}publications:
... ${SPACE*6}- __output__
... ${SPACE*2}graph_route:
... ${SPACE*4}type: graph
... ${SPACE*4}entry_point: start
... ${SPACE*4}nodes:
... ${SPACE*6}start:
... ${SPACE*8}type: actor
... ${SPACE*8}actor: test_actor
... ${SPACE*4}edges:
... ${SPACE*6}- source: start
... ${SPACE*8}target: end
... ${EMPTY}
... merges:
... ${SPACE*2}- sources: [__input__]
... ${SPACE*4}target: stream_route
Create File ${TEST_DIR}/mixed_config.yaml ${config}
Delete Context If Exists
[Documentation] Delete a context if it exists
[Arguments] ${context_name}
${result} = Run Process ${PYTHON} -m cleveragents context delete
... ${context_name}
... --context-dir ${CONTEXT_DIR}
... --yes
... stderr=STDOUT
# Don't fail if context doesn't exist
Log Deleted context ${context_name} (if it existed)
Context Exists
[Documentation] Check if a context exists
[Arguments] ${context_name}
${result} = Run Process ${PYTHON} -m cleveragents context show
... ${context_name}
... --context-dir ${CONTEXT_DIR}
... stderr=STDOUT
# If return code is 0, context exists
${exists} = Evaluate ${result.rc} == 0
RETURN ${exists}