mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 08:08:32 +00:00
Add --force option to crewai tool publish (#1383)
This commit adds an option to bypass Git remote validations when publishing tools.
This commit is contained in:
@@ -276,12 +276,13 @@ def tool_install(handle: str):
|
|||||||
|
|
||||||
|
|
||||||
@tool.command(name="publish")
|
@tool.command(name="publish")
|
||||||
|
@click.option("--force", is_flag=True, show_default=True, default=False, help="Bypasses Git remote validations")
|
||||||
@click.option("--public", "is_public", flag_value=True, default=False)
|
@click.option("--public", "is_public", flag_value=True, default=False)
|
||||||
@click.option("--private", "is_public", flag_value=False)
|
@click.option("--private", "is_public", flag_value=False)
|
||||||
def tool_publish(is_public: bool):
|
def tool_publish(is_public: bool, force: bool):
|
||||||
tool_cmd = ToolCommand()
|
tool_cmd = ToolCommand()
|
||||||
tool_cmd.login()
|
tool_cmd.login()
|
||||||
tool_cmd.publish(is_public)
|
tool_cmd.publish(is_public, force)
|
||||||
|
|
||||||
|
|
||||||
@crewai.group()
|
@crewai.group()
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
|||||||
finally:
|
finally:
|
||||||
os.chdir(old_directory)
|
os.chdir(old_directory)
|
||||||
|
|
||||||
def publish(self, is_public: bool):
|
def publish(self, is_public: bool, force: bool = False):
|
||||||
if not git.Repository().is_synced():
|
if not git.Repository().is_synced() and not force:
|
||||||
console.print(
|
console.print(
|
||||||
"[bold red]Failed to publish tool.[/bold red]\n"
|
"[bold red]Failed to publish tool.[/bold red]\n"
|
||||||
"Local changes need to be resolved before publishing. Please do the following:\n"
|
"Local changes need to be resolved before publishing. Please do the following:\n"
|
||||||
|
|||||||
@@ -1,300 +1,344 @@
|
|||||||
from contextlib import contextmanager
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
import os
|
import os
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from pytest import raises
|
||||||
from crewai.cli.tools.main import ToolCommand
|
from crewai.cli.tools.main import ToolCommand
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def in_temp_dir():
|
||||||
|
original_dir = os.getcwd()
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
os.chdir(temp_dir)
|
||||||
|
try:
|
||||||
|
yield temp_dir
|
||||||
|
finally:
|
||||||
|
os.chdir(original_dir)
|
||||||
|
|
||||||
class TestToolCommand(unittest.TestCase):
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
@contextmanager
|
def test_create_success(mock_subprocess):
|
||||||
def in_temp_dir(self):
|
with in_temp_dir():
|
||||||
original_dir = os.getcwd()
|
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
|
||||||
os.chdir(temp_dir)
|
|
||||||
try:
|
|
||||||
yield temp_dir
|
|
||||||
finally:
|
|
||||||
os.chdir(original_dir)
|
|
||||||
|
|
||||||
@patch("crewai.cli.tools.main.subprocess.run")
|
|
||||||
def test_create_success(self, mock_subprocess):
|
|
||||||
with self.in_temp_dir():
|
|
||||||
tool_command = ToolCommand()
|
|
||||||
|
|
||||||
with patch.object(tool_command, "login") as mock_login, patch(
|
|
||||||
"sys.stdout", new=StringIO()
|
|
||||||
) as fake_out:
|
|
||||||
tool_command.create("test-tool")
|
|
||||||
output = fake_out.getvalue()
|
|
||||||
|
|
||||||
self.assertTrue(os.path.isdir("test_tool"))
|
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(os.path.join("test_tool", "README.md")))
|
|
||||||
self.assertTrue(os.path.isfile(os.path.join("test_tool", "pyproject.toml")))
|
|
||||||
self.assertTrue(
|
|
||||||
os.path.isfile(
|
|
||||||
os.path.join("test_tool", "src", "test_tool", "__init__.py")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.assertTrue(
|
|
||||||
os.path.isfile(os.path.join("test_tool", "src", "test_tool", "tool.py"))
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(
|
|
||||||
os.path.join("test_tool", "src", "test_tool", "tool.py"), "r"
|
|
||||||
) as f:
|
|
||||||
content = f.read()
|
|
||||||
self.assertIn("class TestTool", content)
|
|
||||||
|
|
||||||
mock_login.assert_called_once()
|
|
||||||
mock_subprocess.assert_called_once_with(["git", "init"], check=True)
|
|
||||||
|
|
||||||
self.assertIn("Creating custom tool test_tool...", output)
|
|
||||||
|
|
||||||
@patch("crewai.cli.tools.main.subprocess.run")
|
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
|
|
||||||
def test_install_success(self, mock_get, mock_subprocess_run):
|
|
||||||
mock_get_response = MagicMock()
|
|
||||||
mock_get_response.status_code = 200
|
|
||||||
mock_get_response.json.return_value = {
|
|
||||||
"handle": "sample-tool",
|
|
||||||
"repository": {"handle": "sample-repo", "url": "https://example.com/repo"},
|
|
||||||
}
|
|
||||||
mock_get.return_value = mock_get_response
|
|
||||||
mock_subprocess_run.return_value = MagicMock(stderr=None)
|
|
||||||
|
|
||||||
tool_command = ToolCommand()
|
tool_command = ToolCommand()
|
||||||
|
|
||||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
with patch.object(tool_command, "login") as mock_login, patch(
|
||||||
tool_command.install("sample-tool")
|
"sys.stdout", new=StringIO()
|
||||||
|
) as fake_out:
|
||||||
|
tool_command.create("test-tool")
|
||||||
output = fake_out.getvalue()
|
output = fake_out.getvalue()
|
||||||
|
|
||||||
mock_get.assert_called_once_with("sample-tool")
|
assert os.path.isdir("test_tool")
|
||||||
mock_subprocess_run.assert_any_call(
|
assert os.path.isfile(os.path.join("test_tool", "README.md"))
|
||||||
["poetry", "add", "--source", "crewai-sample-repo", "sample-tool"],
|
assert os.path.isfile(os.path.join("test_tool", "pyproject.toml"))
|
||||||
capture_output=False,
|
assert os.path.isfile(
|
||||||
text=True,
|
os.path.join("test_tool", "src", "test_tool", "__init__.py")
|
||||||
check=True,
|
|
||||||
)
|
)
|
||||||
|
assert os.path.isfile(os.path.join("test_tool", "src", "test_tool", "tool.py"))
|
||||||
|
|
||||||
self.assertIn("Succesfully installed sample-tool", output)
|
with open(
|
||||||
|
os.path.join("test_tool", "src", "test_tool", "tool.py"), "r"
|
||||||
|
) as f:
|
||||||
|
content = f.read()
|
||||||
|
assert "class TestTool" in content
|
||||||
|
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
|
mock_login.assert_called_once()
|
||||||
def test_install_tool_not_found(self, mock_get):
|
mock_subprocess.assert_called_once_with(["git", "init"], check=True)
|
||||||
mock_get_response = MagicMock()
|
|
||||||
mock_get_response.status_code = 404
|
|
||||||
mock_get.return_value = mock_get_response
|
|
||||||
|
|
||||||
tool_command = ToolCommand()
|
assert "Creating custom tool test_tool..." in output
|
||||||
|
|
||||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
with self.assertRaises(SystemExit):
|
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
|
||||||
tool_command.install("non-existent-tool")
|
def test_install_success(mock_get, mock_subprocess_run):
|
||||||
output = fake_out.getvalue()
|
mock_get_response = MagicMock()
|
||||||
|
mock_get_response.status_code = 200
|
||||||
|
mock_get_response.json.return_value = {
|
||||||
|
"handle": "sample-tool",
|
||||||
|
"repository": {"handle": "sample-repo", "url": "https://example.com/repo"},
|
||||||
|
}
|
||||||
|
mock_get.return_value = mock_get_response
|
||||||
|
mock_subprocess_run.return_value = MagicMock(stderr=None)
|
||||||
|
|
||||||
mock_get.assert_called_once_with("non-existent-tool")
|
tool_command = ToolCommand()
|
||||||
self.assertIn("No tool found with this name", output)
|
|
||||||
|
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||||
def test_install_api_error(self, mock_get):
|
tool_command.install("sample-tool")
|
||||||
mock_get_response = MagicMock()
|
output = fake_out.getvalue()
|
||||||
mock_get_response.status_code = 500
|
|
||||||
mock_get.return_value = mock_get_response
|
|
||||||
|
|
||||||
tool_command = ToolCommand()
|
mock_get.assert_called_once_with("sample-tool")
|
||||||
|
mock_subprocess_run.assert_any_call(
|
||||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
["poetry", "add", "--source", "crewai-sample-repo", "sample-tool"],
|
||||||
with self.assertRaises(SystemExit):
|
capture_output=False,
|
||||||
tool_command.install("error-tool")
|
text=True,
|
||||||
output = fake_out.getvalue()
|
check=True,
|
||||||
|
|
||||||
mock_get.assert_called_once_with("error-tool")
|
|
||||||
self.assertIn("Failed to get tool details", output)
|
|
||||||
|
|
||||||
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
|
||||||
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
|
||||||
@patch(
|
|
||||||
"crewai.cli.tools.main.get_project_description", return_value="A sample tool"
|
|
||||||
)
|
)
|
||||||
@patch("crewai.cli.tools.main.subprocess.run")
|
|
||||||
@patch(
|
|
||||||
"crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"]
|
|
||||||
)
|
|
||||||
@patch(
|
|
||||||
"crewai.cli.tools.main.open",
|
|
||||||
new_callable=unittest.mock.mock_open,
|
|
||||||
read_data=b"sample tarball content",
|
|
||||||
)
|
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
|
||||||
@patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=True)
|
|
||||||
def test_publish_success(
|
|
||||||
self,
|
|
||||||
mock_is_synced,
|
|
||||||
mock_publish,
|
|
||||||
mock_open,
|
|
||||||
mock_listdir,
|
|
||||||
mock_subprocess_run,
|
|
||||||
mock_get_project_description,
|
|
||||||
mock_get_project_version,
|
|
||||||
mock_get_project_name,
|
|
||||||
):
|
|
||||||
mock_publish_response = MagicMock()
|
|
||||||
mock_publish_response.status_code = 200
|
|
||||||
mock_publish_response.json.return_value = {"handle": "sample-tool"}
|
|
||||||
mock_publish.return_value = mock_publish_response
|
|
||||||
|
|
||||||
|
assert "Succesfully installed sample-tool" in output
|
||||||
|
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
|
||||||
|
def test_install_tool_not_found(mock_get):
|
||||||
|
mock_get_response = MagicMock()
|
||||||
|
mock_get_response.status_code = 404
|
||||||
|
mock_get.return_value = mock_get_response
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
|
||||||
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||||
|
try:
|
||||||
|
tool_command.install("non-existent-tool")
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
output = fake_out.getvalue()
|
||||||
|
|
||||||
|
mock_get.assert_called_once_with("non-existent-tool")
|
||||||
|
assert "No tool found with this name" in output
|
||||||
|
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.get_tool")
|
||||||
|
def test_install_api_error(mock_get):
|
||||||
|
mock_get_response = MagicMock()
|
||||||
|
mock_get_response.status_code = 500
|
||||||
|
mock_get.return_value = mock_get_response
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
|
||||||
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||||
|
try:
|
||||||
|
tool_command.install("error-tool")
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
output = fake_out.getvalue()
|
||||||
|
|
||||||
|
mock_get.assert_called_once_with("error-tool")
|
||||||
|
assert "Failed to get tool details" in output
|
||||||
|
|
||||||
|
@patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=False)
|
||||||
|
def test_publish_when_not_in_sync(mock_is_synced):
|
||||||
|
with patch("sys.stdout", new=StringIO()) as fake_out, \
|
||||||
|
raises(SystemExit):
|
||||||
tool_command = ToolCommand()
|
tool_command = ToolCommand()
|
||||||
tool_command.publish(is_public=True)
|
tool_command.publish(is_public=True)
|
||||||
|
|
||||||
mock_get_project_name.assert_called_with(require=True)
|
assert "Local changes need to be resolved before publishing" in fake_out.getvalue()
|
||||||
mock_get_project_version.assert_called_with(require=True)
|
|
||||||
mock_get_project_description.assert_called_with(require=False)
|
|
||||||
mock_subprocess_run.assert_called_with(
|
|
||||||
["poetry", "build", "-f", "sdist", "--output", unittest.mock.ANY],
|
|
||||||
check=True,
|
|
||||||
capture_output=False,
|
|
||||||
)
|
|
||||||
mock_open.assert_called_with(unittest.mock.ANY, "rb")
|
|
||||||
mock_publish.assert_called_with(
|
|
||||||
handle="sample-tool",
|
|
||||||
is_public=True,
|
|
||||||
version="1.0.0",
|
|
||||||
description="A sample tool",
|
|
||||||
encoded_file=unittest.mock.ANY,
|
|
||||||
)
|
|
||||||
|
|
||||||
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
||||||
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
||||||
@patch(
|
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
|
||||||
"crewai.cli.tools.main.get_project_description", return_value="A sample tool"
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
|
@patch("crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"])
|
||||||
|
@patch(
|
||||||
|
"crewai.cli.tools.main.open",
|
||||||
|
new_callable=unittest.mock.mock_open,
|
||||||
|
read_data=b"sample tarball content",
|
||||||
|
)
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
||||||
|
@patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=False)
|
||||||
|
def test_publish_when_not_in_sync_and_force(
|
||||||
|
mock_is_synced,
|
||||||
|
mock_publish,
|
||||||
|
mock_open,
|
||||||
|
mock_listdir,
|
||||||
|
mock_subprocess_run,
|
||||||
|
mock_get_project_description,
|
||||||
|
mock_get_project_version,
|
||||||
|
mock_get_project_name,
|
||||||
|
):
|
||||||
|
mock_publish_response = MagicMock()
|
||||||
|
mock_publish_response.status_code = 200
|
||||||
|
mock_publish_response.json.return_value = {"handle": "sample-tool"}
|
||||||
|
mock_publish.return_value = mock_publish_response
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
tool_command.publish(is_public=True, force=True)
|
||||||
|
|
||||||
|
mock_get_project_name.assert_called_with(require=True)
|
||||||
|
mock_get_project_version.assert_called_with(require=True)
|
||||||
|
mock_get_project_description.assert_called_with(require=False)
|
||||||
|
mock_subprocess_run.assert_called_with(
|
||||||
|
["poetry", "build", "-f", "sdist", "--output", unittest.mock.ANY],
|
||||||
|
check=True,
|
||||||
|
capture_output=False,
|
||||||
)
|
)
|
||||||
@patch("crewai.cli.tools.main.subprocess.run")
|
mock_open.assert_called_with(unittest.mock.ANY, "rb")
|
||||||
@patch(
|
mock_publish.assert_called_with(
|
||||||
"crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"]
|
handle="sample-tool",
|
||||||
|
is_public=True,
|
||||||
|
version="1.0.0",
|
||||||
|
description="A sample tool",
|
||||||
|
encoded_file=unittest.mock.ANY,
|
||||||
)
|
)
|
||||||
@patch(
|
|
||||||
"crewai.cli.tools.main.open",
|
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
||||||
new_callable=unittest.mock.mock_open,
|
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
||||||
read_data=b"sample tarball content",
|
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
|
||||||
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
|
@patch("crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"])
|
||||||
|
@patch(
|
||||||
|
"crewai.cli.tools.main.open",
|
||||||
|
new_callable=unittest.mock.mock_open,
|
||||||
|
read_data=b"sample tarball content",
|
||||||
|
)
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
||||||
|
@patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=True)
|
||||||
|
def test_publish_success(
|
||||||
|
mock_is_synced,
|
||||||
|
mock_publish,
|
||||||
|
mock_open,
|
||||||
|
mock_listdir,
|
||||||
|
mock_subprocess_run,
|
||||||
|
mock_get_project_description,
|
||||||
|
mock_get_project_version,
|
||||||
|
mock_get_project_name,
|
||||||
|
):
|
||||||
|
mock_publish_response = MagicMock()
|
||||||
|
mock_publish_response.status_code = 200
|
||||||
|
mock_publish_response.json.return_value = {"handle": "sample-tool"}
|
||||||
|
mock_publish.return_value = mock_publish_response
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
tool_command.publish(is_public=True)
|
||||||
|
|
||||||
|
mock_get_project_name.assert_called_with(require=True)
|
||||||
|
mock_get_project_version.assert_called_with(require=True)
|
||||||
|
mock_get_project_description.assert_called_with(require=False)
|
||||||
|
mock_subprocess_run.assert_called_with(
|
||||||
|
["poetry", "build", "-f", "sdist", "--output", unittest.mock.ANY],
|
||||||
|
check=True,
|
||||||
|
capture_output=False,
|
||||||
)
|
)
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
mock_open.assert_called_with(unittest.mock.ANY, "rb")
|
||||||
def test_publish_failure(
|
mock_publish.assert_called_with(
|
||||||
self,
|
handle="sample-tool",
|
||||||
mock_publish,
|
is_public=True,
|
||||||
mock_open,
|
version="1.0.0",
|
||||||
mock_listdir,
|
description="A sample tool",
|
||||||
mock_subprocess_run,
|
encoded_file=unittest.mock.ANY,
|
||||||
mock_get_project_description,
|
|
||||||
mock_get_project_version,
|
|
||||||
mock_get_project_name,
|
|
||||||
):
|
|
||||||
mock_publish_response = MagicMock()
|
|
||||||
mock_publish_response.status_code = 422
|
|
||||||
mock_publish_response.json.return_value = {"name": ["is already taken"]}
|
|
||||||
mock_publish.return_value = mock_publish_response
|
|
||||||
|
|
||||||
tool_command = ToolCommand()
|
|
||||||
|
|
||||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
||||||
with self.assertRaises(SystemExit):
|
|
||||||
tool_command.publish(is_public=True)
|
|
||||||
output = fake_out.getvalue()
|
|
||||||
|
|
||||||
mock_publish.assert_called_once()
|
|
||||||
self.assertIn("Failed to complete operation", output)
|
|
||||||
self.assertIn("Name is already taken", output)
|
|
||||||
|
|
||||||
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
|
||||||
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
|
||||||
@patch(
|
|
||||||
"crewai.cli.tools.main.get_project_description", return_value="A sample tool"
|
|
||||||
)
|
)
|
||||||
@patch("crewai.cli.tools.main.subprocess.run")
|
|
||||||
@patch(
|
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
||||||
"crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"]
|
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
||||||
|
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
|
||||||
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
|
@patch("crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"])
|
||||||
|
@patch(
|
||||||
|
"crewai.cli.tools.main.open",
|
||||||
|
new_callable=unittest.mock.mock_open,
|
||||||
|
read_data=b"sample tarball content",
|
||||||
|
)
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
||||||
|
def test_publish_failure(
|
||||||
|
mock_publish,
|
||||||
|
mock_open,
|
||||||
|
mock_listdir,
|
||||||
|
mock_subprocess_run,
|
||||||
|
mock_get_project_description,
|
||||||
|
mock_get_project_version,
|
||||||
|
mock_get_project_name,
|
||||||
|
):
|
||||||
|
mock_publish_response = MagicMock()
|
||||||
|
mock_publish_response.status_code = 422
|
||||||
|
mock_publish_response.json.return_value = {"name": ["is already taken"]}
|
||||||
|
mock_publish.return_value = mock_publish_response
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
|
||||||
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||||
|
try:
|
||||||
|
tool_command.publish(is_public=True)
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
output = fake_out.getvalue()
|
||||||
|
|
||||||
|
mock_publish.assert_called_once()
|
||||||
|
assert "Failed to complete operation" in output
|
||||||
|
assert "Name is already taken" in output
|
||||||
|
|
||||||
|
@patch("crewai.cli.tools.main.get_project_name", return_value="sample-tool")
|
||||||
|
@patch("crewai.cli.tools.main.get_project_version", return_value="1.0.0")
|
||||||
|
@patch("crewai.cli.tools.main.get_project_description", return_value="A sample tool")
|
||||||
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
|
@patch("crewai.cli.tools.main.os.listdir", return_value=["sample-tool-1.0.0.tar.gz"])
|
||||||
|
@patch(
|
||||||
|
"crewai.cli.tools.main.open",
|
||||||
|
new_callable=unittest.mock.mock_open,
|
||||||
|
read_data=b"sample tarball content",
|
||||||
|
)
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
||||||
|
def test_publish_api_error(
|
||||||
|
mock_publish,
|
||||||
|
mock_open,
|
||||||
|
mock_listdir,
|
||||||
|
mock_subprocess_run,
|
||||||
|
mock_get_project_description,
|
||||||
|
mock_get_project_version,
|
||||||
|
mock_get_project_name,
|
||||||
|
):
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.status_code = 500
|
||||||
|
mock_response.json.return_value = {"error": "Internal Server Error"}
|
||||||
|
mock_response.ok = False
|
||||||
|
mock_publish.return_value = mock_response
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
|
||||||
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||||
|
try:
|
||||||
|
tool_command.publish(is_public=True)
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
output = fake_out.getvalue()
|
||||||
|
|
||||||
|
mock_publish.assert_called_once()
|
||||||
|
assert "Request to Enterprise API failed" in output
|
||||||
|
|
||||||
|
@patch("crewai.cli.plus_api.PlusAPI.login_to_tool_repository")
|
||||||
|
@patch("crewai.cli.tools.main.subprocess.run")
|
||||||
|
def test_login_success(mock_subprocess_run, mock_login):
|
||||||
|
mock_login_response = MagicMock()
|
||||||
|
mock_login_response.status_code = 200
|
||||||
|
mock_login_response.json.return_value = {
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"handle": "tools",
|
||||||
|
"url": "https://example.com/repo",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credential": {"username": "user", "password": "pass"},
|
||||||
|
}
|
||||||
|
mock_login.return_value = mock_login_response
|
||||||
|
|
||||||
|
mock_subprocess_run.return_value = MagicMock(stderr=None)
|
||||||
|
|
||||||
|
tool_command = ToolCommand()
|
||||||
|
|
||||||
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
||||||
|
tool_command.login()
|
||||||
|
output = fake_out.getvalue()
|
||||||
|
|
||||||
|
mock_login.assert_called_once()
|
||||||
|
mock_subprocess_run.assert_any_call(
|
||||||
|
[
|
||||||
|
"poetry",
|
||||||
|
"source",
|
||||||
|
"add",
|
||||||
|
"--priority=explicit",
|
||||||
|
"crewai-tools",
|
||||||
|
"https://example.com/repo",
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
)
|
)
|
||||||
@patch(
|
mock_subprocess_run.assert_any_call(
|
||||||
"crewai.cli.tools.main.open",
|
[
|
||||||
new_callable=unittest.mock.mock_open,
|
"poetry",
|
||||||
read_data=b"sample tarball content",
|
"config",
|
||||||
|
"http-basic.crewai-tools",
|
||||||
|
"user",
|
||||||
|
"pass",
|
||||||
|
],
|
||||||
|
capture_output=False,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
)
|
)
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.publish_tool")
|
assert "Succesfully authenticated to the tool repository" in output
|
||||||
def test_publish_api_error(
|
|
||||||
self,
|
|
||||||
mock_publish,
|
|
||||||
mock_open,
|
|
||||||
mock_listdir,
|
|
||||||
mock_subprocess_run,
|
|
||||||
mock_get_project_description,
|
|
||||||
mock_get_project_version,
|
|
||||||
mock_get_project_name,
|
|
||||||
):
|
|
||||||
mock_response = MagicMock()
|
|
||||||
mock_response.status_code = 500
|
|
||||||
mock_response.json.return_value = {"error": "Internal Server Error"}
|
|
||||||
mock_response.ok = False
|
|
||||||
mock_publish.return_value = mock_response
|
|
||||||
|
|
||||||
tool_command = ToolCommand()
|
|
||||||
|
|
||||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
||||||
with self.assertRaises(SystemExit):
|
|
||||||
tool_command.publish(is_public=True)
|
|
||||||
output = fake_out.getvalue()
|
|
||||||
|
|
||||||
mock_publish.assert_called_once()
|
|
||||||
self.assertIn("Request to Enterprise API failed", output)
|
|
||||||
|
|
||||||
@patch("crewai.cli.plus_api.PlusAPI.login_to_tool_repository")
|
|
||||||
@patch("crewai.cli.tools.main.subprocess.run")
|
|
||||||
def test_login_success(self, mock_subprocess_run, mock_login):
|
|
||||||
mock_login_response = MagicMock()
|
|
||||||
mock_login_response.status_code = 200
|
|
||||||
mock_login_response.json.return_value = {
|
|
||||||
"repositories": [
|
|
||||||
{
|
|
||||||
"handle": "tools",
|
|
||||||
"url": "https://example.com/repo",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"credential": {"username": "user", "password": "pass"},
|
|
||||||
}
|
|
||||||
mock_login.return_value = mock_login_response
|
|
||||||
|
|
||||||
mock_subprocess_run.return_value = MagicMock(stderr=None)
|
|
||||||
|
|
||||||
tool_command = ToolCommand()
|
|
||||||
|
|
||||||
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
||||||
tool_command.login()
|
|
||||||
output = fake_out.getvalue()
|
|
||||||
|
|
||||||
mock_login.assert_called_once()
|
|
||||||
mock_subprocess_run.assert_any_call(
|
|
||||||
[
|
|
||||||
"poetry",
|
|
||||||
"source",
|
|
||||||
"add",
|
|
||||||
"--priority=explicit",
|
|
||||||
"crewai-tools",
|
|
||||||
"https://example.com/repo",
|
|
||||||
],
|
|
||||||
text=True,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
mock_subprocess_run.assert_any_call(
|
|
||||||
[
|
|
||||||
"poetry",
|
|
||||||
"config",
|
|
||||||
"http-basic.crewai-tools",
|
|
||||||
"user",
|
|
||||||
"pass",
|
|
||||||
],
|
|
||||||
capture_output=False,
|
|
||||||
text=True,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
self.assertIn("Succesfully authenticated to the tool repository", output)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user