mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
feat: introduce trigger listing and execution commands for local development (#3643)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
|
from importlib.metadata import version as get_version
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from importlib.metadata import version as get_version
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ from .reset_memories_command import reset_memories_command
|
|||||||
from .run_crew import run_crew
|
from .run_crew import run_crew
|
||||||
from .tools.main import ToolCommand
|
from .tools.main import ToolCommand
|
||||||
from .train_crew import train_crew
|
from .train_crew import train_crew
|
||||||
|
from .triggers.main import TriggersCommand
|
||||||
from .update_crew import update_crew
|
from .update_crew import update_crew
|
||||||
|
|
||||||
|
|
||||||
@@ -392,6 +393,26 @@ def flow_add_crew(crew_name):
|
|||||||
add_crew_to_flow(crew_name)
|
add_crew_to_flow(crew_name)
|
||||||
|
|
||||||
|
|
||||||
|
@crewai.group()
|
||||||
|
def triggers():
|
||||||
|
"""Trigger related commands. Use 'crewai triggers list' to see available triggers, or 'crewai triggers run app_slug/trigger_slug' to execute."""
|
||||||
|
|
||||||
|
|
||||||
|
@triggers.command(name="list")
|
||||||
|
def triggers_list():
|
||||||
|
"""List all available triggers from integrations."""
|
||||||
|
triggers_cmd = TriggersCommand()
|
||||||
|
triggers_cmd.list_triggers()
|
||||||
|
|
||||||
|
|
||||||
|
@triggers.command(name="run")
|
||||||
|
@click.argument("trigger_path")
|
||||||
|
def triggers_run(trigger_path: str):
|
||||||
|
"""Execute crew with trigger payload. Format: app_slug/trigger_slug"""
|
||||||
|
triggers_cmd = TriggersCommand()
|
||||||
|
triggers_cmd.execute_with_trigger(trigger_path)
|
||||||
|
|
||||||
|
|
||||||
@crewai.command()
|
@crewai.command()
|
||||||
def chat():
|
def chat():
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class PlusAPI:
|
|||||||
AGENTS_RESOURCE = "/crewai_plus/api/v1/agents"
|
AGENTS_RESOURCE = "/crewai_plus/api/v1/agents"
|
||||||
TRACING_RESOURCE = "/crewai_plus/api/v1/tracing"
|
TRACING_RESOURCE = "/crewai_plus/api/v1/tracing"
|
||||||
EPHEMERAL_TRACING_RESOURCE = "/crewai_plus/api/v1/tracing/ephemeral"
|
EPHEMERAL_TRACING_RESOURCE = "/crewai_plus/api/v1/tracing/ephemeral"
|
||||||
|
INTEGRATIONS_RESOURCE = "/crewai_plus/api/v1/integrations"
|
||||||
|
|
||||||
def __init__(self, api_key: str) -> None:
|
def __init__(self, api_key: str) -> None:
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
@@ -176,3 +177,13 @@ class PlusAPI:
|
|||||||
json={"status": "failed", "failure_reason": error_message},
|
json={"status": "failed", "failure_reason": error_message},
|
||||||
timeout=30,
|
timeout=30,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_triggers(self) -> requests.Response:
|
||||||
|
"""Get all available triggers from integrations."""
|
||||||
|
return self._make_request("GET", f"{self.INTEGRATIONS_RESOURCE}/apps")
|
||||||
|
|
||||||
|
def get_trigger_payload(self, app_slug: str, trigger_slug: str) -> requests.Response:
|
||||||
|
"""Get sample payload for a specific trigger."""
|
||||||
|
return self._make_request(
|
||||||
|
"GET", f"{self.INTEGRATIONS_RESOURCE}/{app_slug}/{trigger_slug}/payload"
|
||||||
|
)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def run():
|
|||||||
'topic': 'AI LLMs',
|
'topic': 'AI LLMs',
|
||||||
'current_year': str(datetime.now().year)
|
'current_year': str(datetime.now().year)
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
{{crew_name}}().crew().kickoff(inputs=inputs)
|
{{crew_name}}().crew().kickoff(inputs=inputs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -60,9 +60,35 @@ def test():
|
|||||||
"topic": "AI LLMs",
|
"topic": "AI LLMs",
|
||||||
"current_year": str(datetime.now().year)
|
"current_year": str(datetime.now().year)
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
{{crew_name}}().crew().test(n_iterations=int(sys.argv[1]), eval_llm=sys.argv[2], inputs=inputs)
|
{{crew_name}}().crew().test(n_iterations=int(sys.argv[1]), eval_llm=sys.argv[2], inputs=inputs)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"An error occurred while testing the crew: {e}")
|
raise Exception(f"An error occurred while testing the crew: {e}")
|
||||||
|
|
||||||
|
def run_with_trigger():
|
||||||
|
"""
|
||||||
|
Run the crew with trigger payload.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
raise Exception("No trigger payload provided. Please provide JSON payload as argument.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
trigger_payload = json.loads(sys.argv[1])
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
raise Exception("Invalid JSON payload provided as argument")
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
"crewai_trigger_payload": trigger_payload,
|
||||||
|
"topic": "",
|
||||||
|
"current_year": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = {{crew_name}}().crew().kickoff(inputs=inputs)
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"An error occurred while running the crew with trigger: {e}")
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ run_crew = "{{folder_name}}.main:run"
|
|||||||
train = "{{folder_name}}.main:train"
|
train = "{{folder_name}}.main:train"
|
||||||
replay = "{{folder_name}}.main:replay"
|
replay = "{{folder_name}}.main:replay"
|
||||||
test = "{{folder_name}}.main:test"
|
test = "{{folder_name}}.main:test"
|
||||||
|
run_with_trigger = "{{folder_name}}.main:run_with_trigger"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
|
|||||||
@@ -16,9 +16,16 @@ class PoemState(BaseModel):
|
|||||||
class PoemFlow(Flow[PoemState]):
|
class PoemFlow(Flow[PoemState]):
|
||||||
|
|
||||||
@start()
|
@start()
|
||||||
def generate_sentence_count(self):
|
def generate_sentence_count(self, crewai_trigger_payload: dict = None):
|
||||||
print("Generating sentence count")
|
print("Generating sentence count")
|
||||||
self.state.sentence_count = randint(1, 5)
|
|
||||||
|
# Use trigger payload if available
|
||||||
|
if crewai_trigger_payload:
|
||||||
|
# Example: use trigger data to influence sentence count
|
||||||
|
self.state.sentence_count = crewai_trigger_payload.get('sentence_count', randint(1, 5))
|
||||||
|
print(f"Using trigger payload: {crewai_trigger_payload}")
|
||||||
|
else:
|
||||||
|
self.state.sentence_count = randint(1, 5)
|
||||||
|
|
||||||
@listen(generate_sentence_count)
|
@listen(generate_sentence_count)
|
||||||
def generate_poem(self):
|
def generate_poem(self):
|
||||||
@@ -49,5 +56,32 @@ def plot():
|
|||||||
poem_flow.plot()
|
poem_flow.plot()
|
||||||
|
|
||||||
|
|
||||||
|
def run_with_trigger():
|
||||||
|
"""
|
||||||
|
Run the flow with trigger payload.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Get trigger payload from command line argument
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
raise Exception("No trigger payload provided. Please provide JSON payload as argument.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
trigger_payload = json.loads(sys.argv[1])
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
raise Exception("Invalid JSON payload provided as argument")
|
||||||
|
|
||||||
|
# Create flow and kickoff with trigger payload
|
||||||
|
# The @start() methods will automatically receive crewai_trigger_payload parameter
|
||||||
|
poem_flow = PoemFlow()
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = poem_flow.kickoff({"crewai_trigger_payload": trigger_payload})
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"An error occurred while running the flow with trigger: {e}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
kickoff()
|
kickoff()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ dependencies = [
|
|||||||
kickoff = "{{folder_name}}.main:kickoff"
|
kickoff = "{{folder_name}}.main:kickoff"
|
||||||
run_crew = "{{folder_name}}.main:kickoff"
|
run_crew = "{{folder_name}}.main:kickoff"
|
||||||
plot = "{{folder_name}}.main:plot"
|
plot = "{{folder_name}}.main:plot"
|
||||||
|
run_with_trigger = "{{folder_name}}.main:run_with_trigger"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
|
|||||||
6
lib/crewai/src/crewai/cli/triggers/__init__.py
Normal file
6
lib/crewai/src/crewai/cli/triggers/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
"""Triggers command module for CrewAI CLI."""
|
||||||
|
|
||||||
|
from .main import TriggersCommand
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["TriggersCommand"]
|
||||||
123
lib/crewai/src/crewai/cli/triggers/main.py
Normal file
123
lib/crewai/src/crewai/cli/triggers/main.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
|
from crewai.cli.command import BaseCommand, PlusAPIMixin
|
||||||
|
|
||||||
|
|
||||||
|
console = Console()
|
||||||
|
|
||||||
|
|
||||||
|
class TriggersCommand(BaseCommand, PlusAPIMixin):
|
||||||
|
"""
|
||||||
|
A class to handle trigger-related operations for CrewAI projects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
BaseCommand.__init__(self)
|
||||||
|
PlusAPIMixin.__init__(self, telemetry=self._telemetry)
|
||||||
|
|
||||||
|
def list_triggers(self) -> None:
|
||||||
|
"""List all available triggers from integrations."""
|
||||||
|
try:
|
||||||
|
console.print("[bold blue]Fetching available triggers...[/bold blue]")
|
||||||
|
response = self.plus_api_client.get_triggers()
|
||||||
|
self._validate_response(response)
|
||||||
|
|
||||||
|
triggers_data = response.json()
|
||||||
|
self._display_triggers(triggers_data)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[bold red]Error fetching triggers: {e}[/bold red]")
|
||||||
|
raise SystemExit(1) from e
|
||||||
|
|
||||||
|
def execute_with_trigger(self, trigger_path: str) -> None:
|
||||||
|
"""Execute crew with trigger payload."""
|
||||||
|
try:
|
||||||
|
# Parse app_slug/trigger_slug
|
||||||
|
if "/" not in trigger_path:
|
||||||
|
console.print(
|
||||||
|
"[bold red]Error: Trigger must be in format 'app_slug/trigger_slug'[/bold red]"
|
||||||
|
)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
app_slug, trigger_slug = trigger_path.split("/", 1)
|
||||||
|
|
||||||
|
console.print(f"[bold blue]Fetching trigger payload for {app_slug}/{trigger_slug}...[/bold blue]")
|
||||||
|
response = self.plus_api_client.get_trigger_payload(app_slug, trigger_slug)
|
||||||
|
|
||||||
|
if response.status_code == 404:
|
||||||
|
error_data = response.json()
|
||||||
|
console.print(f"[bold red]Error: {error_data.get('error', 'Trigger not found')}[/bold red]")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
self._validate_response(response)
|
||||||
|
|
||||||
|
trigger_data = response.json()
|
||||||
|
self._display_trigger_info(trigger_data)
|
||||||
|
|
||||||
|
# Run crew with trigger payload
|
||||||
|
self._run_crew_with_payload(trigger_data.get("sample_payload", {}))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[bold red]Error executing crew with trigger: {e}[/bold red]")
|
||||||
|
raise SystemExit(1) from e
|
||||||
|
|
||||||
|
def _display_triggers(self, triggers_data: dict[str, Any]) -> None:
|
||||||
|
"""Display triggers in a formatted table."""
|
||||||
|
apps = triggers_data.get("apps", [])
|
||||||
|
|
||||||
|
if not apps:
|
||||||
|
console.print("[yellow]No triggers found.[/yellow]")
|
||||||
|
return
|
||||||
|
|
||||||
|
for app in apps:
|
||||||
|
app_name = app.get("name", "Unknown App")
|
||||||
|
app_slug = app.get("slug", "unknown")
|
||||||
|
is_connected = app.get("is_connected", False)
|
||||||
|
connection_status = "[green]✓ Connected[/green]" if is_connected else "[red]✗ Not Connected[/red]"
|
||||||
|
|
||||||
|
console.print(f"\n[bold cyan]{app_name}[/bold cyan] ({app_slug}) - {connection_status}")
|
||||||
|
console.print(f"[dim]{app.get('description', 'No description available')}[/dim]")
|
||||||
|
|
||||||
|
triggers = app.get("triggers", [])
|
||||||
|
if triggers:
|
||||||
|
table = Table(show_header=True, header_style="bold magenta")
|
||||||
|
table.add_column("Trigger", style="cyan")
|
||||||
|
table.add_column("Name", style="green")
|
||||||
|
table.add_column("Description", style="dim")
|
||||||
|
|
||||||
|
for trigger in triggers:
|
||||||
|
trigger_path = f"{app_slug}/{trigger.get('slug', 'unknown')}"
|
||||||
|
table.add_row(
|
||||||
|
trigger_path,
|
||||||
|
trigger.get("name", "Unknown"),
|
||||||
|
trigger.get("description", "No description")
|
||||||
|
)
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
else:
|
||||||
|
console.print("[dim] No triggers available[/dim]")
|
||||||
|
|
||||||
|
def _display_trigger_info(self, trigger_data: dict[str, Any]) -> None:
|
||||||
|
"""Display trigger information before execution."""
|
||||||
|
sample_payload = trigger_data.get("sample_payload", {})
|
||||||
|
if sample_payload:
|
||||||
|
console.print("\n[bold yellow]Sample Payload:[/bold yellow]")
|
||||||
|
console.print(json.dumps(sample_payload, indent=2))
|
||||||
|
|
||||||
|
def _run_crew_with_payload(self, payload: dict[str, Any]) -> None:
|
||||||
|
"""Run the crew with the trigger payload using the run_with_trigger method."""
|
||||||
|
try:
|
||||||
|
subprocess.run( # noqa: S603
|
||||||
|
["uv", "run", "run_with_trigger", json.dumps(payload)], # noqa: S607
|
||||||
|
capture_output=False,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise SystemExit(1) from e
|
||||||
170
lib/crewai/tests/cli/triggers/test_main.py
Normal file
170
lib/crewai/tests/cli/triggers/test_main.py
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from crewai.cli.triggers.main import TriggersCommand
|
||||||
|
|
||||||
|
|
||||||
|
class TestTriggersCommand(unittest.TestCase):
|
||||||
|
@patch("crewai.cli.command.get_auth_token")
|
||||||
|
@patch("crewai.cli.command.PlusAPI")
|
||||||
|
def setUp(self, mock_plus_api, mock_get_auth_token):
|
||||||
|
self.mock_get_auth_token = mock_get_auth_token
|
||||||
|
self.mock_plus_api = mock_plus_api
|
||||||
|
|
||||||
|
self.mock_get_auth_token.return_value = "test_token"
|
||||||
|
|
||||||
|
self.triggers_command = TriggersCommand()
|
||||||
|
self.mock_client = self.triggers_command.plus_api_client
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_list_triggers_success(self, mock_console_print):
|
||||||
|
mock_response = Mock(spec=requests.Response)
|
||||||
|
mock_response.status_code = 200
|
||||||
|
mock_response.ok = True
|
||||||
|
mock_response.json.return_value = {
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"name": "Test App",
|
||||||
|
"slug": "test-app",
|
||||||
|
"description": "A test application",
|
||||||
|
"is_connected": True,
|
||||||
|
"triggers": [
|
||||||
|
{
|
||||||
|
"name": "Test Trigger",
|
||||||
|
"slug": "test-trigger",
|
||||||
|
"description": "A test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
self.mock_client.get_triggers.return_value = mock_response
|
||||||
|
|
||||||
|
self.triggers_command.list_triggers()
|
||||||
|
|
||||||
|
self.mock_client.get_triggers.assert_called_once()
|
||||||
|
mock_console_print.assert_any_call("[bold blue]Fetching available triggers...[/bold blue]")
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_list_triggers_no_apps(self, mock_console_print):
|
||||||
|
mock_response = Mock(spec=requests.Response)
|
||||||
|
mock_response.status_code = 200
|
||||||
|
mock_response.ok = True
|
||||||
|
mock_response.json.return_value = {"apps": []}
|
||||||
|
self.mock_client.get_triggers.return_value = mock_response
|
||||||
|
|
||||||
|
self.triggers_command.list_triggers()
|
||||||
|
|
||||||
|
mock_console_print.assert_any_call("[yellow]No triggers found.[/yellow]")
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_list_triggers_api_error(self, mock_console_print):
|
||||||
|
self.mock_client.get_triggers.side_effect = Exception("API Error")
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.triggers_command.list_triggers()
|
||||||
|
|
||||||
|
mock_console_print.assert_any_call("[bold red]Error fetching triggers: API Error[/bold red]")
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_execute_with_trigger_invalid_format(self, mock_console_print):
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.triggers_command.execute_with_trigger("invalid-format")
|
||||||
|
|
||||||
|
mock_console_print.assert_called_with(
|
||||||
|
"[bold red]Error: Trigger must be in format 'app_slug/trigger_slug'[/bold red]"
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
@patch.object(TriggersCommand, "_run_crew_with_payload")
|
||||||
|
def test_execute_with_trigger_success(self, mock_run_crew, mock_console_print):
|
||||||
|
mock_response = Mock(spec=requests.Response)
|
||||||
|
mock_response.status_code = 200
|
||||||
|
mock_response.ok = True
|
||||||
|
mock_response.json.return_value = {
|
||||||
|
"sample_payload": {"key": "value", "data": "test"}
|
||||||
|
}
|
||||||
|
self.mock_client.get_trigger_payload.return_value = mock_response
|
||||||
|
|
||||||
|
self.triggers_command.execute_with_trigger("test-app/test-trigger")
|
||||||
|
|
||||||
|
self.mock_client.get_trigger_payload.assert_called_once_with("test-app", "test-trigger")
|
||||||
|
mock_run_crew.assert_called_once_with({"key": "value", "data": "test"})
|
||||||
|
mock_console_print.assert_any_call(
|
||||||
|
"[bold blue]Fetching trigger payload for test-app/test-trigger...[/bold blue]"
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_execute_with_trigger_not_found(self, mock_console_print):
|
||||||
|
mock_response = Mock(spec=requests.Response)
|
||||||
|
mock_response.status_code = 404
|
||||||
|
mock_response.json.return_value = {"error": "Trigger not found"}
|
||||||
|
self.mock_client.get_trigger_payload.return_value = mock_response
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.triggers_command.execute_with_trigger("test-app/nonexistent-trigger")
|
||||||
|
|
||||||
|
mock_console_print.assert_any_call("[bold red]Error: Trigger not found[/bold red]")
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_execute_with_trigger_api_error(self, mock_console_print):
|
||||||
|
self.mock_client.get_trigger_payload.side_effect = Exception("API Error")
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.triggers_command.execute_with_trigger("test-app/test-trigger")
|
||||||
|
|
||||||
|
mock_console_print.assert_any_call(
|
||||||
|
"[bold red]Error executing crew with trigger: API Error[/bold red]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("subprocess.run")
|
||||||
|
def test_run_crew_with_payload_success(self, mock_subprocess):
|
||||||
|
payload = {"key": "value", "data": "test"}
|
||||||
|
mock_subprocess.return_value = None
|
||||||
|
|
||||||
|
self.triggers_command._run_crew_with_payload(payload)
|
||||||
|
|
||||||
|
mock_subprocess.assert_called_once_with(
|
||||||
|
["uv", "run", "run_with_trigger", json.dumps(payload)],
|
||||||
|
capture_output=False,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("subprocess.run")
|
||||||
|
def test_run_crew_with_payload_failure(self, mock_subprocess):
|
||||||
|
payload = {"key": "value"}
|
||||||
|
mock_subprocess.side_effect = subprocess.CalledProcessError(1, "uv")
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.triggers_command._run_crew_with_payload(payload)
|
||||||
|
|
||||||
|
@patch("subprocess.run")
|
||||||
|
def test_run_crew_with_payload_empty_payload(self, mock_subprocess):
|
||||||
|
payload = {}
|
||||||
|
mock_subprocess.return_value = None
|
||||||
|
|
||||||
|
self.triggers_command._run_crew_with_payload(payload)
|
||||||
|
|
||||||
|
mock_subprocess.assert_called_once_with(
|
||||||
|
["uv", "run", "run_with_trigger", "{}"],
|
||||||
|
capture_output=False,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("crewai.cli.triggers.main.console.print")
|
||||||
|
def test_execute_with_trigger_with_default_error_message(self, mock_console_print):
|
||||||
|
mock_response = Mock(spec=requests.Response)
|
||||||
|
mock_response.status_code = 404
|
||||||
|
mock_response.json.return_value = {}
|
||||||
|
self.mock_client.get_trigger_payload.return_value = mock_response
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.triggers_command.execute_with_trigger("test-app/test-trigger")
|
||||||
|
|
||||||
|
mock_console_print.assert_any_call("[bold red]Error: Trigger not found[/bold red]")
|
||||||
Reference in New Issue
Block a user