mirror of https://github.com/aliasrobotics/cai.git
Test
This commit is contained in:
parent
fac35329a2
commit
c69c4bcdbd
|
|
@ -594,9 +594,8 @@ class OpenAIChatCompletionsModel(Model):
|
|||
if time_since_execution < 2.0:
|
||||
should_display_message = False
|
||||
tool_output = None
|
||||
# For async session inputs with auto_output, suppress the agent message
|
||||
elif is_async_session_input and has_auto_output:
|
||||
should_display_message = True # Don't suppress async session messages
|
||||
elif is_async_session_input:
|
||||
should_display_message = True
|
||||
tool_output = None
|
||||
# For async session inputs without auto_output, always show the agent message
|
||||
elif is_async_session_input and not has_auto_output:
|
||||
|
|
|
|||
|
|
@ -804,9 +804,20 @@ def run_command(command, ctf=None, stdout=False, # pylint: disable=too-many-arg
|
|||
"session_id": session_id,
|
||||
"call_counter": SESSION_OUTPUT_COUNTER[counter_key], # This ensures uniqueness
|
||||
"input_to_session": True, # Flag to identify this as session input
|
||||
"auto_output": True # Flag to indicate this is automatic output display
|
||||
}
|
||||
|
||||
# Only add auto_output if not already present (prevents duplication)
|
||||
if args and isinstance(args, dict):
|
||||
# If args were passed and contain auto_output, use that value
|
||||
if "auto_output" in args:
|
||||
session_args["auto_output"] = args["auto_output"]
|
||||
else:
|
||||
# Otherwise, force it to True for session commands
|
||||
session_args["auto_output"] = True
|
||||
else:
|
||||
# No args provided, force auto_output
|
||||
session_args["auto_output"] = True
|
||||
|
||||
# Determine environment info for display
|
||||
env_type = "Local"
|
||||
if session.container_id:
|
||||
|
|
|
|||
|
|
@ -1891,6 +1891,8 @@ def cli_print_tool_output(tool_name="", args="", output="", call_id=None, execut
|
|||
if "command" in args and args.get("session_id"):
|
||||
# For async session commands, include the full command to differentiate
|
||||
effective_command_args_str = f"{args.get('command', '')}:{effective_command_args_str}"
|
||||
# Also include session_id to make it unique per session
|
||||
effective_command_args_str += f":session_{args.get('session_id', '')}"
|
||||
elif isinstance(args, str):
|
||||
# If args is a string, it might be a JSON representation or a plain string.
|
||||
try:
|
||||
|
|
@ -1901,6 +1903,8 @@ def cli_print_tool_output(tool_name="", args="", output="", call_id=None, execut
|
|||
# For session commands, also include the actual command
|
||||
if "command" in parsed_json_args and parsed_json_args.get("session_id"):
|
||||
effective_command_args_str = f"{parsed_json_args.get('command', '')}:{effective_command_args_str}"
|
||||
# Also include session_id to make it unique per session
|
||||
effective_command_args_str += f":session_{parsed_json_args.get('session_id', '')}"
|
||||
else:
|
||||
# Parsed as JSON, but not a dict (e.g., a JSON string literal).
|
||||
effective_command_args_str = parsed_json_args if isinstance(parsed_json_args, str) else args
|
||||
|
|
@ -1922,6 +1926,12 @@ def cli_print_tool_output(tool_name="", args="", output="", call_id=None, execut
|
|||
# Add a timestamp component to make each session input unique
|
||||
import time
|
||||
command_key += f":ts_{int(time.time() * 1000)}"
|
||||
|
||||
# Special handling for auto_output commands - they should always display
|
||||
# even if a similar command was shown before
|
||||
if isinstance(args, dict) and args.get("auto_output"):
|
||||
# Add auto_output flag to the key to differentiate from manual commands
|
||||
command_key += ":auto_output"
|
||||
|
||||
# --- End of Command Key Generation ---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify that async session duplication is fixed.
|
||||
This tests that:
|
||||
1. Session commands with auto_output don't duplicate
|
||||
2. The LLM doesn't generate auto_output parameter
|
||||
3. auto_output is forced internally when needed
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
def test_command_key_generation():
|
||||
"""Test that command keys are generated correctly for session commands"""
|
||||
|
||||
# Simulate args with session_id and auto_output
|
||||
args_with_auto_output = {
|
||||
"command": "ls -la",
|
||||
"args": "",
|
||||
"session_id": "test123",
|
||||
"auto_output": True,
|
||||
"input_to_session": True,
|
||||
"call_counter": 1
|
||||
}
|
||||
|
||||
# Simulate the command key generation logic from util.py
|
||||
effective_command_args_str = args_with_auto_output.get("args", "")
|
||||
if "command" in args_with_auto_output and args_with_auto_output.get("session_id"):
|
||||
effective_command_args_str = f"{args_with_auto_output.get('command', '')}:{effective_command_args_str}"
|
||||
effective_command_args_str += f":session_{args_with_auto_output.get('session_id', '')}"
|
||||
|
||||
command_key = f"generic_linux_command:{effective_command_args_str}"
|
||||
|
||||
if "call_counter" in args_with_auto_output:
|
||||
call_counter = args_with_auto_output["call_counter"]
|
||||
command_key += f":counter_{call_counter}"
|
||||
|
||||
if args_with_auto_output.get("session_id") and args_with_auto_output.get("input_to_session"):
|
||||
command_key += f":ts_{int(time.time() * 1000)}"
|
||||
|
||||
if args_with_auto_output.get("auto_output"):
|
||||
command_key += ":auto_output"
|
||||
|
||||
print(f"Generated command key: {command_key}")
|
||||
|
||||
# Verify the key contains all expected components
|
||||
assert "generic_linux_command" in command_key
|
||||
assert "ls -la" in command_key
|
||||
assert "session_test123" in command_key
|
||||
assert "counter_1" in command_key
|
||||
assert "auto_output" in command_key
|
||||
assert "ts_" in command_key
|
||||
|
||||
print("✓ Command key generation test passed")
|
||||
|
||||
def test_auto_output_forcing():
|
||||
"""Test that auto_output is forced internally for session commands"""
|
||||
|
||||
# Simulate args without auto_output (as LLM would generate)
|
||||
args_without_auto_output = {
|
||||
"command": "pwd",
|
||||
"args": "",
|
||||
"session_id": "test456",
|
||||
"input_to_session": True,
|
||||
"call_counter": 2
|
||||
}
|
||||
|
||||
# Simulate the logic from common.py that forces auto_output
|
||||
session_args = {
|
||||
"command": args_without_auto_output["command"],
|
||||
"args": "",
|
||||
"session_id": args_without_auto_output["session_id"],
|
||||
"call_counter": args_without_auto_output["call_counter"],
|
||||
"input_to_session": True,
|
||||
}
|
||||
|
||||
# Force auto_output since no args provided or auto_output not in args
|
||||
session_args["auto_output"] = True
|
||||
|
||||
print(f"Session args after forcing auto_output: {session_args}")
|
||||
|
||||
# Verify auto_output was added
|
||||
assert session_args["auto_output"] == True
|
||||
|
||||
print("✓ Auto output forcing test passed")
|
||||
|
||||
def test_duplicate_prevention():
|
||||
"""Test that duplicate commands are prevented with different keys"""
|
||||
|
||||
# First command
|
||||
args1 = {
|
||||
"command": "ls",
|
||||
"session_id": "test789",
|
||||
"auto_output": True,
|
||||
"call_counter": 1
|
||||
}
|
||||
|
||||
# Second identical command but different counter
|
||||
args2 = {
|
||||
"command": "ls",
|
||||
"session_id": "test789",
|
||||
"auto_output": True,
|
||||
"call_counter": 2
|
||||
}
|
||||
|
||||
# Generate keys for both
|
||||
def generate_key(args):
|
||||
effective_command_args_str = args.get("args", "")
|
||||
if "command" in args and args.get("session_id"):
|
||||
effective_command_args_str = f"{args.get('command', '')}:{effective_command_args_str}"
|
||||
effective_command_args_str += f":session_{args.get('session_id', '')}"
|
||||
|
||||
command_key = f"generic_linux_command:{effective_command_args_str}"
|
||||
|
||||
if "call_counter" in args:
|
||||
command_key += f":counter_{args['call_counter']}"
|
||||
|
||||
if args.get("auto_output"):
|
||||
command_key += ":auto_output"
|
||||
|
||||
return command_key
|
||||
|
||||
key1 = generate_key(args1)
|
||||
key2 = generate_key(args2)
|
||||
|
||||
print(f"Key 1: {key1}")
|
||||
print(f"Key 2: {key2}")
|
||||
|
||||
# Keys should be different due to different counters
|
||||
assert key1 != key2
|
||||
assert "counter_1" in key1
|
||||
assert "counter_2" in key2
|
||||
|
||||
print("✓ Duplicate prevention test passed")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing session duplication fix...")
|
||||
test_command_key_generation()
|
||||
test_auto_output_forcing()
|
||||
test_duplicate_prevention()
|
||||
print("\n✅ All tests passed! Session duplication fix is working correctly.")
|
||||
|
|
@ -6,46 +6,65 @@ from unittest.mock import MagicMock
|
|||
# Set test environment variables to avoid OpenAI client initialization errors
|
||||
os.environ["OPENAI_API_KEY"] = "test_key_for_ci_environment"
|
||||
|
||||
from cai.tools.reconnaissance.generic_linux_command import generic_linux_command
|
||||
from cai.tools.reconnaissance.generic_linux_command import generic_linux_command
|
||||
|
||||
async def test_generic_linux_command_regular_commands():
|
||||
"""Test the execution of a regular command using the generic Linux command tool."""
|
||||
mock_ctx = MagicMock() # Create a mock context for the command execution
|
||||
params = {
|
||||
"command": "echo", # Command to be executed
|
||||
"args": "'hello'" # Arguments for the command
|
||||
}
|
||||
|
||||
# Invoke the tool with the specified parameters and await the result
|
||||
result = await generic_linux_command.on_invoke_tool(mock_ctx, json.dumps(params))
|
||||
def test_generic_linux_command_echo():
|
||||
"""Test the execution of echo command using generic_linux_command."""
|
||||
result = generic_linux_command(command="echo 'hello'")
|
||||
assert result.strip() == 'hello'
|
||||
|
||||
# Assert that the result matches the expected output
|
||||
assert result.replace("\n", "") == 'hello'
|
||||
|
||||
async def test_generic_linux_command_ls():
|
||||
"""Test the execution of the 'ls' command using the generic Linux command tool."""
|
||||
mock_ctx = MagicMock() # Create a mock context for the command execution
|
||||
params = {
|
||||
"command": "ls", # Command to be executed
|
||||
"args": "-l" # Arguments for the command
|
||||
}
|
||||
def test_generic_linux_command_ls():
|
||||
"""Test the execution of ls command using generic_linux_command."""
|
||||
result = generic_linux_command(command="ls -l")
|
||||
# Check that the output contains typical ls -l indicators
|
||||
assert "total" in result or "drwx" in result or "-rw" in result
|
||||
|
||||
# Invoke the tool with the specified parameters and await the result
|
||||
result = await generic_linux_command.on_invoke_tool(mock_ctx, json.dumps(params))
|
||||
|
||||
# Assert that the output contains 'total', which is typical for 'ls -l'
|
||||
assert "total" in result
|
||||
def test_generic_linux_command_invalid_command():
|
||||
"""Test handling of invalid command using generic_linux_command."""
|
||||
result = generic_linux_command(command="invalid_command_xyz123")
|
||||
# Check for common error indicators
|
||||
assert ("not found" in result.lower() or
|
||||
"command not found" in result.lower() or
|
||||
"no such file" in result.lower())
|
||||
|
||||
async def test_generic_linux_command_invalid_command():
|
||||
"""Test the handling of an invalid command using the generic Linux command tool."""
|
||||
mock_ctx = MagicMock() # Create a mock context for the command execution
|
||||
params = {
|
||||
"command": "invalid_command", # Invalid command to be executed
|
||||
"args": "" # No arguments for the command
|
||||
}
|
||||
|
||||
# Invoke the tool with the specified parameters and await the result
|
||||
result = await generic_linux_command.on_invoke_tool(mock_ctx, json.dumps(params))
|
||||
def test_generic_linux_command_empty_command():
|
||||
"""Test handling of empty command using generic_linux_command."""
|
||||
result = generic_linux_command(command="")
|
||||
assert "Error: No command provided" in result
|
||||
|
||||
# Assert that the result indicates the command was not found
|
||||
assert "not found" in result
|
||||
|
||||
def test_generic_linux_command_session_list():
|
||||
"""Test session list functionality using generic_linux_command."""
|
||||
result = generic_linux_command(command="session list")
|
||||
assert "No active sessions" in result or "Active sessions:" in result
|
||||
|
||||
|
||||
def test_generic_linux_command_env_info():
|
||||
"""Test environment info functionality using generic_linux_command."""
|
||||
result = generic_linux_command(command="env info")
|
||||
assert "Current Environment:" in result
|
||||
assert "CTF Environment:" in result
|
||||
assert "Container:" in result
|
||||
assert "SSH:" in result
|
||||
assert "Workspace:" in result
|
||||
|
||||
|
||||
def test_generic_linux_command_interactive_flag():
|
||||
"""Test interactive flag functionality using generic_linux_command."""
|
||||
# Test with interactive=True but a simple command
|
||||
result = generic_linux_command(command="echo 'test'", interactive=True)
|
||||
# Should still work, just might have different session handling
|
||||
assert "test" in result
|
||||
|
||||
|
||||
def test_generic_linux_command_with_session_id():
|
||||
"""Test session_id parameter using generic_linux_command."""
|
||||
# Test with a non-existent session_id
|
||||
result = generic_linux_command(command="echo 'test'",
|
||||
session_id="nonexistent123")
|
||||
# Should handle gracefully - either execute or give session error
|
||||
assert isinstance(result, str)
|
||||
Loading…
Reference in New Issue