mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-03 14:09:24 +00:00
Merge branch 'main' of github.com:crewAIInc/crewAI into lorenze/imp/streaming
This commit is contained in:
@@ -146,6 +146,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[tool.crewai]
|
||||
type = "crew"
|
||||
definition = "crew.jsonc"
|
||||
""".strip()
|
||||
+ "\n"
|
||||
)
|
||||
@@ -176,10 +177,11 @@ def test_create_project_zip_keeps_json_project_root_shape(tmp_path: Path):
|
||||
[project]
|
||||
name = "json_crew"
|
||||
version = "0.1.0"
|
||||
dependencies = ["crewai[tools]==1.14.8a1"]
|
||||
dependencies = ["crewai[tools]>=1.15.0,<2.0.0"]
|
||||
|
||||
[tool.crewai]
|
||||
type = "crew"
|
||||
definition = "crew.jsonc"
|
||||
""".strip()
|
||||
+ "\n"
|
||||
)
|
||||
@@ -221,6 +223,7 @@ custom = "custom.module:main"
|
||||
|
||||
[tool.crewai]
|
||||
type = "crew"
|
||||
definition = "crew.jsonc"
|
||||
""".strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
@@ -167,6 +167,36 @@ def test_prepare_project_for_deploy_creates_missing_lock_after_validation(
|
||||
assert validators == []
|
||||
|
||||
|
||||
def test_deployment_page_url_prefers_deployment_id():
|
||||
assert (
|
||||
deploy_main._deployment_page_url(
|
||||
"https://app.crewai.com",
|
||||
{"uuid": "crew-uuid", "deployment_id": 128687},
|
||||
)
|
||||
== "https://app.crewai.com/crewai_plus/deployments/128687"
|
||||
)
|
||||
|
||||
|
||||
def test_deployment_page_url_prefers_nested_deployment_id_over_crew_uuid():
|
||||
assert (
|
||||
deploy_main._deployment_page_url(
|
||||
"https://app.crewai.com",
|
||||
{"uuid": "crew-uuid", "deployment": {"deployment_id": 128687}},
|
||||
)
|
||||
== "https://app.crewai.com/crewai_plus/deployments/128687"
|
||||
)
|
||||
|
||||
|
||||
def test_deployment_page_url_falls_back_to_nested_uuid():
|
||||
assert (
|
||||
deploy_main._deployment_page_url(
|
||||
"https://app.crewai.com/",
|
||||
{"deployment": {"uuid": "deployment-uuid"}},
|
||||
)
|
||||
== "https://app.crewai.com/crewai_plus/deployments/deployment-uuid"
|
||||
)
|
||||
|
||||
|
||||
class TestDeployCommand(unittest.TestCase):
|
||||
@patch("crewai_cli.command.get_auth_token")
|
||||
@patch("crewai_cli.deploy.main.get_project_name")
|
||||
@@ -186,6 +216,12 @@ class TestDeployCommand(unittest.TestCase):
|
||||
|
||||
self.deploy_command = deploy_main.DeployCommand()
|
||||
self.mock_client = self.deploy_command.plus_api_client
|
||||
self.mock_client.base_url = "https://app.crewai.com"
|
||||
self.mock_browser_open_patcher = patch(
|
||||
"crewai_cli.deploy.main.webbrowser.open"
|
||||
)
|
||||
self.mock_browser_open = self.mock_browser_open_patcher.start()
|
||||
self.addCleanup(self.mock_browser_open_patcher.stop)
|
||||
|
||||
def test_init_success(self):
|
||||
self.assertEqual(self.deploy_command.project_name, "test_project")
|
||||
@@ -272,11 +308,50 @@ class TestDeployCommand(unittest.TestCase):
|
||||
def test_display_deployment_info(self):
|
||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||
self.deploy_command._display_deployment_info(
|
||||
{"uuid": "test-uuid", "status": "deployed"}
|
||||
{"uuid": "test-uuid", "id": 128687, "status": "deployed"}
|
||||
)
|
||||
self.assertIn("Deploying the crew...", fake_out.getvalue())
|
||||
self.assertIn("test-uuid", fake_out.getvalue())
|
||||
self.assertIn("deployed", fake_out.getvalue())
|
||||
self.assertIn(
|
||||
"https://app.crewai.com/crewai_plus/deployments/128687",
|
||||
fake_out.getvalue(),
|
||||
)
|
||||
self.mock_browser_open.assert_called_once_with(
|
||||
"https://app.crewai.com/crewai_plus/deployments/128687"
|
||||
)
|
||||
|
||||
def test_display_deployment_info_warns_when_browser_open_returns_false(self):
|
||||
self.mock_browser_open.return_value = False
|
||||
|
||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||
self.deploy_command._display_deployment_info(
|
||||
{"uuid": "test-uuid", "id": 128687, "status": "deployed"}
|
||||
)
|
||||
self.assertIn(
|
||||
"Could not open the deployment page automatically.",
|
||||
fake_out.getvalue(),
|
||||
)
|
||||
|
||||
self.mock_browser_open.assert_called_once_with(
|
||||
"https://app.crewai.com/crewai_plus/deployments/128687"
|
||||
)
|
||||
|
||||
def test_display_deployment_info_warns_when_browser_open_raises(self):
|
||||
self.mock_browser_open.side_effect = RuntimeError("no browser")
|
||||
|
||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||
self.deploy_command._display_deployment_info(
|
||||
{"uuid": "test-uuid", "id": 128687, "status": "deployed"}
|
||||
)
|
||||
self.assertIn(
|
||||
"Could not open the deployment page automatically.",
|
||||
fake_out.getvalue(),
|
||||
)
|
||||
|
||||
self.mock_browser_open.assert_called_once_with(
|
||||
"https://app.crewai.com/crewai_plus/deployments/128687"
|
||||
)
|
||||
|
||||
def test_display_logs(self):
|
||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||
|
||||
@@ -111,7 +111,12 @@ def _run_without_import_check(root: Path) -> DeployValidator:
|
||||
|
||||
|
||||
def _scaffold_json_crew(root: Path, *, task_agent: str = "researcher") -> None:
|
||||
(root / "pyproject.toml").write_text(_make_pyproject(name="json_crew"))
|
||||
(root / "pyproject.toml").write_text(
|
||||
_make_pyproject(
|
||||
name="json_crew",
|
||||
extra='[tool.crewai]\ntype = "crew"\ndefinition = "crew.jsonc"',
|
||||
)
|
||||
)
|
||||
(root / "uv.lock").write_text("# dummy uv lockfile\n")
|
||||
agents_dir = root / "agents"
|
||||
agents_dir.mkdir()
|
||||
@@ -221,7 +226,6 @@ def test_json_crew_reports_project_metadata_before_invalid_json(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_scaffold_json_crew(tmp_path)
|
||||
(tmp_path / "pyproject.toml").unlink()
|
||||
(tmp_path / "uv.lock").unlink()
|
||||
(tmp_path / "crew.jsonc").write_text('{"agents": ["researcher"], "tasks": []}\n')
|
||||
|
||||
@@ -229,7 +233,6 @@ def test_json_crew_reports_project_metadata_before_invalid_json(
|
||||
v.run()
|
||||
|
||||
codes = _codes(v)
|
||||
assert "missing_pyproject" in codes
|
||||
assert "missing_lockfile" in codes
|
||||
assert "invalid_crew_json" in codes
|
||||
assert "missing_src_dir" not in codes
|
||||
@@ -546,17 +549,43 @@ def test_is_json_crew_defers_to_declared_flow_type(tmp_path):
|
||||
assert DeployValidator(project_root=tmp_path)._is_json_crew is False
|
||||
|
||||
|
||||
def test_is_json_crew_true_for_declared_crew_type(tmp_path):
|
||||
def test_is_json_crew_true_for_declared_crew_definition(tmp_path):
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
'[project]\nname = "demo"\nversion = "0.1.0"\n\n'
|
||||
'[tool.crewai]\ntype = "crew"\ndefinition = "crew.jsonc"\n'
|
||||
)
|
||||
|
||||
assert DeployValidator(project_root=tmp_path)._is_json_crew is True
|
||||
|
||||
|
||||
def test_is_json_crew_false_for_declared_crew_without_definition(tmp_path):
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
'[project]\nname = "demo"\nversion = "0.1.0"\n\n'
|
||||
'[tool.crewai]\ntype = "crew"\n'
|
||||
)
|
||||
|
||||
assert DeployValidator(project_root=tmp_path)._is_json_crew is True
|
||||
assert DeployValidator(project_root=tmp_path)._is_json_crew is False
|
||||
|
||||
|
||||
def test_is_json_crew_true_without_pyproject(tmp_path):
|
||||
def test_json_crew_non_string_definition_reports_invalid_definition(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
'[project]\nname = "demo"\nversion = "0.1.0"\n\n'
|
||||
'[tool.crewai]\ntype = "crew"\ndefinition = ["crew.jsonc"]\n'
|
||||
)
|
||||
|
||||
v = DeployValidator(project_root=tmp_path)
|
||||
v.run()
|
||||
|
||||
finding = next(r for r in v.results if r.code == "invalid_crew_definition")
|
||||
assert finding.severity is Severity.ERROR
|
||||
assert "must be a string" in finding.detail
|
||||
|
||||
|
||||
def test_is_json_crew_false_without_pyproject(tmp_path):
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
|
||||
assert DeployValidator(project_root=tmp_path)._is_json_crew is True
|
||||
assert DeployValidator(project_root=tmp_path)._is_json_crew is False
|
||||
|
||||
@@ -12,6 +12,8 @@ from packaging.version import Version
|
||||
import crewai_cli.create_json_crew as json_crew
|
||||
import crewai_cli.tui_picker as tui_picker
|
||||
from crewai_cli.create_crew import create_crew, create_folder_structure
|
||||
from crewai_cli.utils import render_template
|
||||
from crewai_cli.version import get_crewai_tools_dependency
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -735,11 +737,16 @@ def test_json_create_provider_preselects_default_model(tmp_path, monkeypatch):
|
||||
|
||||
pyproject = tomli.loads((tmp_path / "json_crew" / "pyproject.toml").read_text())
|
||||
dependency = pyproject["project"]["dependencies"][0]
|
||||
assert dependency == "crewai[tools]==1.14.8a1"
|
||||
assert Version("1.14.8a1") in Requirement(dependency).specifier
|
||||
assert dependency == get_crewai_tools_dependency()
|
||||
assert Version("1.15.0") in Requirement(dependency).specifier
|
||||
assert Version("2.0.0") not in Requirement(dependency).specifier
|
||||
assert pyproject["tool"]["hatch"]["build"]["targets"]["wheel"][
|
||||
"only-include"
|
||||
] == ["agents", "crew.jsonc", "tools", "knowledge", "skills"]
|
||||
assert pyproject["tool"]["crewai"] == {
|
||||
"type": "crew",
|
||||
"definition": "crew.jsonc",
|
||||
}
|
||||
|
||||
crew_template = (tmp_path / "json_crew" / "crew.jsonc").read_text()
|
||||
assert (
|
||||
@@ -811,6 +818,37 @@ def test_json_create_provider_preselects_default_model(tmp_path, monkeypatch):
|
||||
assert '"knowledge_sources": []' in agent_template
|
||||
|
||||
|
||||
def test_json_crew_uses_template_files():
|
||||
template_names = {
|
||||
"pyproject.toml",
|
||||
"README.md",
|
||||
".gitignore",
|
||||
"agent.jsonc",
|
||||
"agent_settings.jsonc",
|
||||
"task.jsonc",
|
||||
"crew.jsonc",
|
||||
"knowledge/user_preference.txt",
|
||||
}
|
||||
|
||||
for template_name in template_names:
|
||||
assert (json_crew._TEMPLATES_DIR / template_name).is_file()
|
||||
|
||||
|
||||
def test_render_template_does_not_replace_tokens_inside_replacement_values(tmp_path):
|
||||
template = tmp_path / "template.txt"
|
||||
template.write_text("{{first}} {{second}}", encoding="utf-8")
|
||||
|
||||
rendered = render_template(
|
||||
template,
|
||||
{
|
||||
"first": "{{second}}",
|
||||
"second": "done",
|
||||
},
|
||||
)
|
||||
|
||||
assert rendered == "{{second}} done"
|
||||
|
||||
|
||||
def test_json_provider_default_model_helper():
|
||||
assert json_crew._default_model_for_provider("openai") == "openai/gpt-5.5"
|
||||
assert json_crew._default_model_for_provider("anthropic/claude-custom") == (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from datetime import datetime
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -127,6 +129,37 @@ def test_chain_deploy_does_not_login_for_deploy_exit(monkeypatch, capsys) -> Non
|
||||
assert "Deploy failed with exit code 42" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_view_traces_button_click_records_telemetry(monkeypatch) -> None:
|
||||
app = CrewRunApp()
|
||||
app._status = "completed"
|
||||
app._trace_url = "https://app.crewai.com/traces/test"
|
||||
app._telemetry = Mock()
|
||||
opened_urls: list[str] = []
|
||||
|
||||
monkeypatch.setattr("webbrowser.open", lambda url: opened_urls.append(url))
|
||||
|
||||
app.on_button_pressed(SimpleNamespace(button=SimpleNamespace(id="btn-traces")))
|
||||
|
||||
app._telemetry.feature_usage_span.assert_called_once_with("cli_usage:view_traces")
|
||||
assert opened_urls == ["https://app.crewai.com/traces/test"]
|
||||
|
||||
|
||||
def test_deploy_button_click_records_telemetry() -> None:
|
||||
app = CrewRunApp()
|
||||
app._status = "completed"
|
||||
app._crew_result = object()
|
||||
app._telemetry = Mock()
|
||||
app._unsubscribe = lambda: None # type: ignore[method-assign]
|
||||
exits: list[object] = []
|
||||
app.exit = lambda result: exits.append(result) # type: ignore[method-assign]
|
||||
|
||||
app.on_button_pressed(SimpleNamespace(button=SimpleNamespace(id="btn-deploy")))
|
||||
|
||||
app._telemetry.feature_usage_span.assert_called_once_with("cli_usage:deploy")
|
||||
assert app._want_deploy is True
|
||||
assert exits == [app._crew_result]
|
||||
|
||||
|
||||
def test_conversation_turn_done_records_assistant_message() -> None:
|
||||
class RawResult:
|
||||
raw = "hello from the flow"
|
||||
|
||||
@@ -26,6 +26,7 @@ name = "json_crew"
|
||||
|
||||
[tool.crewai]
|
||||
type = "crew"
|
||||
definition = "crew.jsonc"
|
||||
""".strip()
|
||||
)
|
||||
(tmp_path / "crew.jsonc").write_text("{}\n")
|
||||
@@ -45,6 +46,7 @@ name = "hybrid-crew"
|
||||
|
||||
[tool.crewai]
|
||||
type = "crew"
|
||||
definition = "crew.jsonc"
|
||||
""".strip()
|
||||
)
|
||||
(tmp_path / "crew.jsonc").write_text("{}\n")
|
||||
|
||||
@@ -16,29 +16,37 @@ def test_missing_crewai_package_shows_full_install_hint(monkeypatch):
|
||||
def missing_crewai_package():
|
||||
raise ModuleNotFoundError("No module named 'crewai'", name="crewai")
|
||||
|
||||
monkeypatch.setattr(
|
||||
run_crew_module, "_import_find_crew_json_file", missing_crewai_package
|
||||
)
|
||||
real_import = __import__
|
||||
|
||||
def fake_import(name, *args, **kwargs):
|
||||
if name == "crewai.project.crew_loader":
|
||||
missing_crewai_package()
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr("builtins.__import__", fake_import)
|
||||
|
||||
with pytest.raises(click.ClickException) as exc_info:
|
||||
run_crew_module.find_crew_json_file()
|
||||
run_crew_module._load_json_crew(Path("crew.jsonc"))
|
||||
|
||||
message = exc_info.value.message
|
||||
assert "CrewAI CLI is installed without the `crewai` package" in message
|
||||
assert (
|
||||
"uv tool install --force --prerelease=allow 'crewai[tools]==1.14.8a1'"
|
||||
in message
|
||||
)
|
||||
assert "uv tool install --force 'crewai[tools]>=1.15.0,<2.0.0'" in message
|
||||
assert "quotes are required in zsh" in message
|
||||
|
||||
|
||||
def test_run_crew_forwards_trained_agents_file_to_json_crews(monkeypatch):
|
||||
"""crewai run -f must reach JSON crews, not only classic subprocess crews."""
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: True)
|
||||
monkeypatch.setattr(run_crew_module, "read_toml", lambda: {})
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"configured_project_json_crew",
|
||||
lambda pyproject_data=None, project_root=None: Path("crew.jsonc"),
|
||||
)
|
||||
called: dict = {}
|
||||
|
||||
def fake_run_json_crew_in_project_env(trained_agents_file=None):
|
||||
def fake_run_json_crew_in_project_env(trained_agents_file=None, crew_path=None):
|
||||
called["trained_agents_file"] = trained_agents_file
|
||||
called["crew_path"] = crew_path
|
||||
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
@@ -48,7 +56,10 @@ def test_run_crew_forwards_trained_agents_file_to_json_crews(monkeypatch):
|
||||
|
||||
run_crew_module.run_crew(trained_agents_file="some.pkl")
|
||||
|
||||
assert called == {"trained_agents_file": "some.pkl"}
|
||||
assert called == {
|
||||
"trained_agents_file": "some.pkl",
|
||||
"crew_path": Path("crew.jsonc"),
|
||||
}
|
||||
|
||||
|
||||
def test_json_run_uses_project_env_when_pyproject_exists(monkeypatch, tmp_path: Path):
|
||||
@@ -74,8 +85,10 @@ def test_json_run_uses_project_env_when_pyproject_exists(monkeypatch, tmp_path:
|
||||
|
||||
monkeypatch.setattr(run_crew_module.subprocess, "run", fake_subprocess_run)
|
||||
|
||||
crew_path = tmp_path / "crew.jsonc"
|
||||
run_crew_module._run_json_crew_in_project_env(
|
||||
trained_agents_file="trained.pkl"
|
||||
trained_agents_file="trained.pkl",
|
||||
crew_path=crew_path,
|
||||
)
|
||||
|
||||
expected_env = {
|
||||
@@ -84,6 +97,7 @@ def test_json_run_uses_project_env_when_pyproject_exists(monkeypatch, tmp_path:
|
||||
Path(run_crew_module.__file__).resolve().parent
|
||||
),
|
||||
CREWAI_TRAINED_AGENTS_FILE_ENV: "trained.pkl",
|
||||
run_crew_module._CREWAI_JSON_CREW_DEFINITION_ENV: str(crew_path),
|
||||
}
|
||||
if local_crewai_source_dir := run_crew_module._find_local_crewai_source_dir():
|
||||
expected_env[run_crew_module._CREWAI_RUNNER_SOURCE_DIR_ENV] = str(
|
||||
@@ -213,12 +227,87 @@ def test_json_runner_code_loads_current_cli_package_over_project_env(tmp_path: P
|
||||
assert marker.read_text() == "current:trained.pkl"
|
||||
|
||||
|
||||
def test_json_runner_imports_with_older_project_env_crewai_core(tmp_path: Path):
|
||||
old_parent = tmp_path / "old_env"
|
||||
old_crewai_core = old_parent / "crewai_core"
|
||||
old_crewai_core.mkdir(parents=True)
|
||||
(old_crewai_core / "__init__.py").write_text("")
|
||||
(old_crewai_core / "constants.py").write_text(
|
||||
"CREWAI_TRAINED_AGENTS_FILE_ENV = 'CREWAI_TRAINED_AGENTS_FILE'\n"
|
||||
)
|
||||
(old_crewai_core / "project.py").write_text(
|
||||
"def read_toml(*args, **kwargs):\n"
|
||||
" return {}\n"
|
||||
"def parse_toml(*args, **kwargs):\n"
|
||||
" return {}\n"
|
||||
"def get_project_description(*args, **kwargs):\n"
|
||||
" return None\n"
|
||||
"def get_project_name(*args, **kwargs):\n"
|
||||
" return None\n"
|
||||
"def get_project_version(*args, **kwargs):\n"
|
||||
" return None\n"
|
||||
)
|
||||
(old_crewai_core / "tool_credentials.py").write_text(
|
||||
"def build_env_with_all_tool_credentials(*args, **kwargs):\n"
|
||||
" return {}\n"
|
||||
"def build_env_with_tool_repository_credentials(*args, **kwargs):\n"
|
||||
" return {}\n"
|
||||
)
|
||||
(old_crewai_core / "version.py").write_text(
|
||||
"def check_version(*args, **kwargs):\n"
|
||||
" return None\n"
|
||||
"def get_crewai_version(*args, **kwargs):\n"
|
||||
" return '1.0.0'\n"
|
||||
"def get_latest_version_from_pypi(*args, **kwargs):\n"
|
||||
" return None\n"
|
||||
"def is_current_version_yanked(*args, **kwargs):\n"
|
||||
" return False\n"
|
||||
"def is_newer_version_available(*args, **kwargs):\n"
|
||||
" return False\n"
|
||||
)
|
||||
|
||||
marker = tmp_path / "marker.txt"
|
||||
old_crewai_project = old_parent / "crewai" / "project"
|
||||
old_crewai_project.mkdir(parents=True)
|
||||
(old_parent / "crewai" / "__init__.py").write_text("")
|
||||
(old_crewai_project / "__init__.py").write_text("")
|
||||
(old_crewai_project / "crew_loader.py").write_text(
|
||||
"from pathlib import Path\n"
|
||||
"class Crew:\n"
|
||||
" agents = []\n"
|
||||
" tasks = []\n"
|
||||
" def kickoff(self, inputs):\n"
|
||||
f" Path({str(marker)!r}).write_text('ran')\n"
|
||||
" return 'done'\n"
|
||||
"def load_crew(path):\n"
|
||||
" return Crew(), {}\n"
|
||||
)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = str(old_parent)
|
||||
env["CREWAI_DMN"] = "true"
|
||||
env[run_crew_module._CREWAI_CLI_RUNNER_PACKAGE_DIR_ENV] = str(
|
||||
Path(run_crew_module.__file__).resolve().parent
|
||||
)
|
||||
env[run_crew_module._CREWAI_JSON_CREW_DEFINITION_ENV] = "crew.jsonc"
|
||||
|
||||
subprocess.run(
|
||||
[sys.executable, "-c", run_crew_module._JSON_CREW_RUNNER_CODE],
|
||||
check=True,
|
||||
env=env,
|
||||
cwd=tmp_path,
|
||||
)
|
||||
|
||||
assert marker.read_text() == "ran"
|
||||
|
||||
|
||||
def test_json_run_without_pyproject_runs_in_process(monkeypatch, tmp_path: Path):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
called: dict = {}
|
||||
|
||||
def fake_run_json_crew(trained_agents_file=None):
|
||||
def fake_run_json_crew(trained_agents_file=None, crew_path=None):
|
||||
called["trained_agents_file"] = trained_agents_file
|
||||
called["crew_path"] = crew_path
|
||||
return "result"
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_run_json_crew", fake_run_json_crew)
|
||||
@@ -229,7 +318,7 @@ def test_json_run_without_pyproject_runs_in_process(monkeypatch, tmp_path: Path)
|
||||
)
|
||||
== "result"
|
||||
)
|
||||
assert called == {"trained_agents_file": "trained.pkl"}
|
||||
assert called == {"trained_agents_file": "trained.pkl", "crew_path": None}
|
||||
|
||||
|
||||
def test_json_project_env_run_failure_exits_nonzero(monkeypatch, tmp_path: Path):
|
||||
@@ -438,7 +527,7 @@ def _patch_tui_run(monkeypatch, status: str):
|
||||
|
||||
crew = SimpleNamespace(name="Demo", tasks=[], agents=[])
|
||||
monkeypatch.setattr(
|
||||
run_crew_module, "find_crew_json_file", lambda: Path("crew.jsonc")
|
||||
run_crew_module, "configured_project_json_crew", lambda: Path("crew.jsonc")
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
@@ -492,7 +581,9 @@ def test_run_json_crew_dmn_mode_bypasses_tui(monkeypatch, tmp_path: Path, capsys
|
||||
kickoff_calls.append(inputs)
|
||||
return "plain result"
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "find_crew_json_file", lambda: crew_path)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module, "configured_project_json_crew", lambda: crew_path
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"_load_json_crew",
|
||||
@@ -531,7 +622,9 @@ def test_run_json_crew_dmn_mode_exits_on_missing_inputs(
|
||||
tasks=[],
|
||||
)
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "find_crew_json_file", lambda: crew_path)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module, "configured_project_json_crew", lambda: crew_path
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"_load_json_crew",
|
||||
@@ -546,28 +639,47 @@ def test_run_json_crew_dmn_mode_exits_on_missing_inputs(
|
||||
assert "Missing runtime inputs for CREWAI_DMN mode: topic" in captured.err
|
||||
|
||||
|
||||
def test_has_json_crew_defers_to_declared_flow_type(monkeypatch, tmp_path: Path):
|
||||
def test_configured_project_json_crew_defers_to_declared_flow_type(
|
||||
monkeypatch, tmp_path: Path
|
||||
):
|
||||
"""A flow project containing a stray crew.jsonc must still run as a flow."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
(tmp_path / "pyproject.toml").write_text('[tool.crewai]\ntype = "flow"\n')
|
||||
|
||||
assert run_crew_module._has_json_crew() is False
|
||||
assert run_crew_module.configured_project_json_crew() is None
|
||||
|
||||
|
||||
def test_has_json_crew_true_for_declared_crew_type(monkeypatch, tmp_path: Path):
|
||||
def test_configured_project_json_crew_returns_declared_crew_definition(
|
||||
monkeypatch, tmp_path: Path
|
||||
):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
crew_path = tmp_path / "crew.jsonc"
|
||||
crew_path.write_text("{}")
|
||||
(tmp_path / "pyproject.toml").write_text(
|
||||
'[tool.crewai]\ntype = "crew"\ndefinition = "crew.jsonc"\n'
|
||||
)
|
||||
|
||||
assert run_crew_module.configured_project_json_crew() == crew_path.resolve()
|
||||
|
||||
|
||||
def test_configured_project_json_crew_ignores_declared_crew_without_definition(
|
||||
monkeypatch, tmp_path: Path
|
||||
):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
(tmp_path / "pyproject.toml").write_text('[tool.crewai]\ntype = "crew"\n')
|
||||
|
||||
assert run_crew_module._has_json_crew() is True
|
||||
assert run_crew_module.configured_project_json_crew() is None
|
||||
|
||||
|
||||
def test_has_json_crew_true_without_pyproject(monkeypatch, tmp_path: Path):
|
||||
def test_configured_project_json_crew_ignores_missing_pyproject(
|
||||
monkeypatch, tmp_path: Path
|
||||
):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
(tmp_path / "crew.jsonc").write_text("{}")
|
||||
|
||||
assert run_crew_module._has_json_crew() is True
|
||||
assert run_crew_module.configured_project_json_crew() is None
|
||||
|
||||
|
||||
def test_run_crew_rejects_inputs_without_definition():
|
||||
@@ -608,7 +720,6 @@ def test_run_crew_runs_explicit_declarative_definition(monkeypatch, capsys):
|
||||
def test_run_crew_runs_classic_crew_project(monkeypatch, capsys):
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
@@ -634,7 +745,6 @@ def test_run_crew_runs_classic_crew_project(monkeypatch, capsys):
|
||||
def test_run_crew_runs_python_flow_project(monkeypatch, capsys):
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
@@ -663,7 +773,6 @@ def test_run_crew_runs_conversational_flow_tui(monkeypatch, capsys):
|
||||
flow = Flow()
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
@@ -692,7 +801,6 @@ def test_run_crew_runs_conversational_flow_tui(monkeypatch, capsys):
|
||||
|
||||
|
||||
def test_run_crew_rejects_filename_for_flow_project(monkeypatch):
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
@@ -713,7 +821,6 @@ def test_run_crew_runs_configured_declarative_flow_project(
|
||||
monkeypatch.chdir(tmp_path)
|
||||
definition_path = tmp_path / "flow.yaml"
|
||||
definition_path.write_text("schema: crewai.flow/v1\n", encoding="utf-8")
|
||||
monkeypatch.setattr(run_crew_module, "_has_json_crew", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
run_crew_module,
|
||||
"read_toml",
|
||||
|
||||
@@ -7,6 +7,8 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from crewai_cli.version import get_crewai_version as _get_ver
|
||||
from crewai_cli.version import (
|
||||
get_crewai_dependency_range,
|
||||
get_crewai_tools_dependency,
|
||||
get_crewai_version,
|
||||
get_latest_version_from_pypi,
|
||||
is_current_version_yanked,
|
||||
@@ -31,6 +33,11 @@ def test_dynamic_versioning_consistency() -> None:
|
||||
assert len(package_version.strip()) > 0
|
||||
|
||||
|
||||
def test_generated_project_dependency_uses_next_major_upper_bound() -> None:
|
||||
assert get_crewai_dependency_range("1.15.0") == ">=1.15.0,<2.0.0"
|
||||
assert get_crewai_tools_dependency("1.15.0") == "crewai[tools]>=1.15.0,<2.0.0"
|
||||
|
||||
|
||||
class TestVersionChecking:
|
||||
"""Test version checking utilities."""
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ def test_create_success(mock_subprocess, capsys, tool_command):
|
||||
)
|
||||
assert os.path.isfile(os.path.join("test_tool", "src", "test_tool", "tool.py"))
|
||||
|
||||
with open(os.path.join("test_tool", "pyproject.toml"), "r") as f:
|
||||
content = f.read()
|
||||
assert '"crewai[tools]>=1.15.0,<2.0.0"' in content
|
||||
|
||||
with open(os.path.join("test_tool", "src", "test_tool", "tool.py"), "r") as f:
|
||||
content = f.read()
|
||||
assert "class TestTool" in content
|
||||
|
||||
Reference in New Issue
Block a user