feat: update Composio components (#8868)

Co-authored-by: Uday-sidagana <uday.sidgana@gmail.com>
Co-authored-by: Uday Sidagana <udaysidagana@Mac.lan>
Co-authored-by: Edwin Jose <edwin.jose@datastax.com>
Co-authored-by: Uday Sidagana <129588963+Uday-sidagana@users.noreply.github.com>
This commit is contained in:
Abhishek Patil 2025-08-18 21:27:13 +05:30 • committed by GitHub
commit f6e6edbc37
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 1749 additions and 4627 deletions

View file

@ -1,132 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from langflow.base.composio.composio_base import ComposioBaseComponent
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
class MockComposioToolSet:
def __init__(self, api_key=None):
self.api_key = api_key
self.client = MagicMock()
def get_tools(self, *_):
return []
def execute_action(self, *_, **__):
return {"successful": True, "data": {"result": "mocked response"}}
class TestComposioBase(ComponentTestBaseWithoutClient):
@pytest.fixture
def component_class(self):
class TestComponent(ComposioBaseComponent):
def execute_action(self):
return []
return TestComponent
@pytest.fixture(autouse=True)
def mock_composio_toolset(self):
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
yield
@pytest.fixture
def default_kwargs(self):
return {
"api_key": "",
"entity_id": "default",
"action": None,
}
@pytest.fixture
def file_names_mapping(self):
# Component not yet released, mark all versions as non-existent
return [
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
]
def test_build_wrapper_no_api_key(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
with pytest.raises(ValueError, match="Please provide a valid Composio API Key in the component settings"):
component._build_wrapper()
def test_build_wrapper_with_api_key(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
component.api_key = "test_key"
wrapper = component._build_wrapper()
assert isinstance(wrapper, MockComposioToolSet)
assert wrapper.api_key == "test_key"
def test_build_action_maps(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
# Test with empty actions data
component._actions_data = {}
component._build_action_maps()
assert component._display_to_key_map == {}
assert component._key_to_display_map == {}
assert component._sanitized_names == {}
# Test with sample actions data
component._actions_data = {
"ACTION_1": {"display_name": "Action One"},
"ACTION_2": {"display_name": "Action Two"},
}
component._build_action_maps()
assert component._display_to_key_map == {
"Action One": "ACTION_1",
"Action Two": "ACTION_2",
}
assert component._key_to_display_map == {
"ACTION_1": "Action One",
"ACTION_2": "Action Two",
}
def test_get_action_fields(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
component._actions_data = {
"ACTION_1": {"action_fields": ["field1", "field2"]},
"ACTION_2": {"action_fields": ["field3"]},
}
# Test with valid action key
fields = component._get_action_fields("ACTION_1")
assert fields == {"field1", "field2"}
# Test with non-existent action key
fields = component._get_action_fields("NON_EXISTENT")
assert fields == set()
# Test with None action key
fields = component._get_action_fields(None)
assert fields == set()
def test_show_hide_fields(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
component._all_fields = {"field1", "field2"}
component._bool_variables = {"field2"}
component._actions_data = {
"ACTION_1": {"display_name": "Action One", "action_fields": ["field1"]},
}
build_config = {
"field1": {"show": False, "value": "old_value"},
"field2": {"show": False, "value": True},
}
# Test with no field value
component.show_hide_fields(build_config, None)
assert not build_config["field1"]["show"]
assert not build_config["field2"]["show"]
assert build_config["field1"]["value"] == ""
assert build_config["field2"]["value"] is False
# Test with valid action
component.show_hide_fields(build_config, [{"name": "Action One"}])
assert build_config["field1"]["show"] # Should be shown since it's in ACTION_1's fields
assert not build_config["field2"]["show"] # Should remain hidden

View file

@ -1,226 +0,0 @@
from unittest.mock import patch
import pytest
from composio import Action
from langflow.components.composio.github_composio import ComposioGitHubAPIComponent
from langflow.schema.dataframe import DataFrame
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
from .test_base import MockComposioToolSet
class MockAction:
GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER = "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"
GITHUB_LIST_BRANCHES = "GITHUB_LIST_BRANCHES"
GITHUB_LIST_REPOSITORY_ISSUES = "GITHUB_LIST_REPOSITORY_ISSUES"
class TestGitHubComponent(ComponentTestBaseWithoutClient):
@pytest.fixture(autouse=True)
def mock_composio_toolset(self):
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
yield
@pytest.fixture
def component_class(self):
return ComposioGitHubAPIComponent
@pytest.fixture
def default_kwargs(self):
return {
"api_key": "",
"entity_id": "default",
"action": None,
}
@pytest.fixture
def file_names_mapping(self):
# Component not yet released, mark all versions as non-existent
return [
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
]
def test_init(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
assert component.display_name == "GitHub"
assert component.app_name == "github"
assert "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER" in component._actions_data
assert "GITHUB_LIST_BRANCHES" in component._actions_data
def test_execute_action_star_a_repo(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(
Action,
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER",
MockAction.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER,
)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Star A Repository"}]
component.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_owner = "langflow-ai"
component.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_repo = "langflow"
# For this specific test, customize the _actions_data to not use get_result_field
component._actions_data = {
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER": {
"display_name": "Star A Repository",
"action_fields": [
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_owner",
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_repo",
],
},
}
# Execute action
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_list_branches(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GITHUB_LIST_BRANCHES", MockAction.GITHUB_LIST_BRANCHES)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Branches"}]
component.GITHUB_LIST_BRANCHES_owner = "langflow-ai"
component.GITHUB_LIST_BRANCHES_repo = "langflow"
# For this specific test, customize the _actions_data to not use get_result_field
component._actions_data = {
"GITHUB_LIST_BRANCHES": {
"display_name": "List Branches",
"action_fields": [
"GITHUB_LIST_BRANCHES_owner",
"GITHUB_LIST_BRANCHES_repo",
"GITHUB_LIST_BRANCHES_protected",
"GITHUB_LIST_BRANCHES_per_page",
"GITHUB_LIST_BRANCHES_page",
],
},
}
# Execute action
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_list_repo_issues(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GITHUB_LIST_REPOSITORY_ISSUES", MockAction.GITHUB_LIST_REPOSITORY_ISSUES)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Repository Issues"}]
component.GITHUB_LIST_REPOSITORY_ISSUES_owner = "langflow-ai"
component.GITHUB_LIST_REPOSITORY_ISSUES_repo = "langflow"
# For this specific test, customize the _actions_data to not use get_result_field
component._actions_data = {
"GITHUB_LIST_REPOSITORY_ISSUES": {
"display_name": "List Repository Issues",
"action_fields": [
"GITHUB_LIST_REPOSITORY_ISSUES_owner",
"GITHUB_LIST_REPOSITORY_ISSUES_repo",
"GITHUB_LIST_REPOSITORY_ISSUES_milestone",
"GITHUB_LIST_REPOSITORY_ISSUES_state",
"GITHUB_LIST_REPOSITORY_ISSUES_assignee",
"GITHUB_LIST_REPOSITORY_ISSUES_creator",
"GITHUB_LIST_REPOSITORY_ISSUES_mentioned",
"GITHUB_LIST_REPOSITORY_ISSUES_labels",
"GITHUB_LIST_REPOSITORY_ISSUES_sort",
"GITHUB_LIST_REPOSITORY_ISSUES_direction",
"GITHUB_LIST_REPOSITORY_ISSUES_since",
"GITHUB_LIST_REPOSITORY_ISSUES_per_page",
"GITHUB_LIST_REPOSITORY_ISSUES_page",
],
},
}
# Execute action
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_invalid_action(self, component_class, default_kwargs):
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Invalid Action"}]
# Execute action should raise ValueError
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
component.execute_action()
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GITHUB_LIST_REPOSITORY_ISSUES", MockAction.GITHUB_LIST_REPOSITORY_ISSUES)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Repository Issues"}]
component.max_results = 10
# Create mock email data that would be returned by execute_action
mock_issues = [
{
"url": "url1",
"repository_url": "repository_url1",
"id": "id1",
"title": "test issue",
"state": "open",
},
{
"url": "url2",
"repository_url": "repository_url2",
"id": "id2",
"title": "test issue",
"state": "open",
},
]
# Mock the execute_action method to return our mock data
with patch.object(component, "execute_action", return_value=mock_issues):
# Test as_dataframe method
result = component.as_dataframe()
# Verify the result is a DataFrame
assert isinstance(result, DataFrame)
# Verify the DataFrame is not empty
assert not result.empty
# Check for expected content in the DataFrame string representation
data_str = str(result)
assert "test issue" in data_str
def test_update_build_config(self, component_class, default_kwargs):
# Test that the GitHub component properly inherits and uses the base component's
# update_build_config method
component = component_class(**default_kwargs)
build_config = {
"auth_link": {"value": "", "auth_tooltip": ""},
"action": {
"options": [],
"helper_text": "",
"helper_text_metadata": {},
},
}
# Test with empty API key
result = component.update_build_config(build_config, "", "api_key")
assert result["auth_link"]["value"] == ""
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
assert result["action"]["options"] == []
# Test with valid API key
component.api_key = "test_key"
result = component.update_build_config(build_config, "test_key", "api_key")
assert len(result["action"]["options"]) > 0 # Should have GitHub actions

View file

@ -1,218 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from composio import Action
from langflow.components.composio.gmail_composio import ComposioGmailAPIComponent
from langflow.schema.dataframe import DataFrame
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
from .test_base import MockComposioToolSet
class MockAction:
GMAIL_SEND_EMAIL = "GMAIL_SEND_EMAIL"
GMAIL_FETCH_EMAILS = "GMAIL_FETCH_EMAILS"
GMAIL_GET_PROFILE = "GMAIL_GET_PROFILE"
class TestGmailComponent(ComponentTestBaseWithoutClient):
@pytest.fixture(autouse=True)
def mock_composio_toolset(self):
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
yield
@pytest.fixture
def component_class(self):
return ComposioGmailAPIComponent
@pytest.fixture
def default_kwargs(self):
return {
"api_key": "",
"entity_id": "default",
"action": None,
"recipient_email": "",
"subject": "",
"body": "",
"is_html": False,
"max_results": 10,
"query": "",
}
@pytest.fixture
def file_names_mapping(self):
# Component not yet released, mark all versions as non-existent
return [
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
]
def test_init(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
assert component.display_name == "Gmail"
assert component.name == "GmailAPI"
assert component.app_name == "gmail"
assert "GMAIL_SEND_EMAIL" in component._actions_data
assert "GMAIL_FETCH_EMAILS" in component._actions_data
def test_execute_action_send_email(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GMAIL_SEND_EMAIL", MockAction.GMAIL_SEND_EMAIL)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Send Email"}]
component.recipient_email = "test@example.com"
component.subject = "Test Subject"
component.body = "Test Body"
component.is_html = False
# For this specific test, customize the _actions_data to not use get_result_field
component._actions_data = {
"GMAIL_SEND_EMAIL": {
"display_name": "Send Email",
"action_fields": ["recipient_email", "subject", "body", "is_html"],
"get_result_field": False,
}
}
# Execute action
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_fetch_emails(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GMAIL_FETCH_EMAILS", MockAction.GMAIL_FETCH_EMAILS)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Fetch Emails"}]
component.max_results = 10
component.query = "from:test@example.com"
# For this specific test, we need to customize the action_data to handle results field
component._actions_data = {
"GMAIL_FETCH_EMAILS": {
"display_name": "Fetch Emails",
"action_fields": ["max_results", "query"],
"result_field": "messages",
"get_result_field": True,
}
}
# Create a mock for the toolset with specific structure for this test
mock_toolset = MagicMock()
mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}}
# Patch the _build_wrapper method
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
result = component.execute_action()
# Based on the component's actual behavior, it returns the result_field directly
assert result == "mocked response"
def test_execute_action_get_profile(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GMAIL_GET_PROFILE", MockAction.GMAIL_GET_PROFILE)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Get User Profile"}]
# For this specific test, customize the _actions_data to not use get_result_field
component._actions_data = {
"GMAIL_GET_PROFILE": {
"display_name": "Get User Profile",
"action_fields": ["gmail_user_id"],
"get_result_field": False,
}
}
# Execute action
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_invalid_action(self, component_class, default_kwargs):
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Invalid Action"}]
# Execute action should raise ValueError
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
component.execute_action()
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
# Mock Action enum
monkeypatch.setattr(Action, "GMAIL_FETCH_EMAILS", MockAction.GMAIL_FETCH_EMAILS)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Fetch Emails"}]
component.max_results = 10
# Create mock email data that would be returned by execute_action
mock_emails = [
{
"id": "1",
"threadId": "thread1",
"subject": "Test Email 1",
"from": "sender1@example.com",
"date": "2023-01-01",
"snippet": "This is a test email",
},
{
"id": "2",
"threadId": "thread2",
"subject": "Test Email 2",
"from": "sender2@example.com",
"date": "2023-01-02",
"snippet": "This is another test email",
},
]
# Mock the execute_action method to return our mock data
with patch.object(component, "execute_action", return_value=mock_emails):
# Test as_dataframe method
result = component.as_dataframe()
# Verify the result is a DataFrame
assert isinstance(result, DataFrame)
# Verify the DataFrame is not empty
assert not result.empty
# Check for expected content in the DataFrame string representation
data_str = str(result)
assert "test email" in data_str
def test_update_build_config(self, component_class, default_kwargs):
# Test that the Gmail component properly inherits and uses the base component's
# update_build_config method
component = component_class(**default_kwargs)
build_config = {
"auth_link": {"value": "", "auth_tooltip": ""},
"action": {
"options": [],
"helper_text": "",
"helper_text_metadata": {},
},
}
# Test with empty API key
result = component.update_build_config(build_config, "", "api_key")
assert result["auth_link"]["value"] == ""
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
assert result["action"]["options"] == []
# Test with valid API key
component.api_key = "test_key"
result = component.update_build_config(build_config, "test_key", "api_key")
assert len(result["action"]["options"]) > 0 # Should have Gmail actions

View file

@ -1,182 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from composio import Action
from langflow.components.composio.googlecalendar_composio import ComposioGoogleCalendarAPIComponent
from langflow.schema.dataframe import DataFrame
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
from .test_base import MockComposioToolSet
class MockAction:
GOOGLECALENDAR_CREATE_EVENT = "GOOGLECALENDAR_CREATE_EVENT"
GOOGLECALENDAR_LIST_CALENDARS = "GOOGLECALENDAR_LIST_CALENDARS"
class TestGoogleCalendarComponent(ComponentTestBaseWithoutClient):
@pytest.fixture(autouse=True)
def mock_composio_toolset(self):
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
yield
@pytest.fixture
def component_class(self):
return ComposioGoogleCalendarAPIComponent
@pytest.fixture
def default_kwargs(self):
return {
"api_key": "",
"entity_id": "default",
"action": None,
}
@pytest.fixture
def file_names_mapping(self):
return [
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
]
def test_init(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
assert component.display_name == "Google Calendar"
assert component.app_name == "googlecalendar"
assert "GOOGLECALENDAR_CREATE_EVENT" in component._actions_data
assert "GOOGLECALENDAR_LIST_CALENDARS" in component._actions_data
def test_execute_action_create_event(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(Action, "GOOGLECALENDAR_CREATE_EVENT", MockAction.GOOGLECALENDAR_CREATE_EVENT)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Create Event"}]
component.GOOGLECALENDAR_CREATE_EVENT_attendees = "test@example.com"
component.GOOGLECALENDAR_CREATE_EVENT_start_datetime = "2025-01-16T15:00:00"
component.GOOGLECALENDAR_CREATE_EVENT_summary = "test title"
component._actions_data = {
"GOOGLECALENDAR_CREATE_EVENT": {
"display_name": "Create Event",
"action_fields": [
"GOOGLECALENDAR_CREATE_EVENT_description",
"GOOGLECALENDAR_CREATE_EVENT_eventType",
"GOOGLECALENDAR_CREATE_EVENT_create_meeting_room",
"GOOGLECALENDAR_CREATE_EVENT_guestsCanSeeOtherGuests",
"GOOGLECALENDAR_CREATE_EVENT_guestsCanInviteOthers",
"GOOGLECALENDAR_CREATE_EVENT_location",
"GOOGLECALENDAR_CREATE_EVENT_summary",
"GOOGLECALENDAR_CREATE_EVENT_transparency",
"GOOGLECALENDAR_CREATE_EVENT_visibility",
"GOOGLECALENDAR_CREATE_EVENT_timezone",
"GOOGLECALENDAR_CREATE_EVENT_recurrence",
"GOOGLECALENDAR_CREATE_EVENT_guests_can_modify",
"GOOGLECALENDAR_CREATE_EVENT_attendees",
"GOOGLECALENDAR_CREATE_EVENT_send_updates",
"GOOGLECALENDAR_CREATE_EVENT_start_datetime",
"GOOGLECALENDAR_CREATE_EVENT_event_duration_hour",
"GOOGLECALENDAR_CREATE_EVENT_event_duration_minutes",
"GOOGLECALENDAR_CREATE_EVENT_calendar_id",
],
}
}
result = component.execute_action()
assert result == "mocked response"
def test_execute_action_list_calendars(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(Action, "GOOGLECALENDAR_LIST_CALENDARS", MockAction.GOOGLECALENDAR_LIST_CALENDARS)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Google Calendars"}]
component.GOOGLECALENDAR_LIST_CALENDARS_max_results = 1
component._actions_data = {
"GOOGLECALENDAR_LIST_CALENDARS": {
"display_name": "List Google Calendars",
"action_fields": [
"GOOGLECALENDAR_LIST_CALENDARS_max_results",
"GOOGLECALENDAR_LIST_CALENDARS_min_access_role",
"GOOGLECALENDAR_LIST_CALENDARS_page_token",
"GOOGLECALENDAR_LIST_CALENDARS_show_deleted",
"GOOGLECALENDAR_LIST_CALENDARS_show_hidden",
"GOOGLECALENDAR_LIST_CALENDARS_sync_token",
],
}
}
mock_toolset = MagicMock()
mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}}
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
result = component.execute_action()
assert result == "mocked response"
def test_execute_action_invalid_action(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Invalid Action"}]
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
component.execute_action()
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(Action, "GOOGLECALENDAR_LIST_CALENDARS", MockAction.GOOGLECALENDAR_LIST_CALENDARS)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Google Calendars"}]
component.GOOGLECALENDAR_LIST_CALENDARS_max_results = 10
mock_emails = [
{
"kind": "test kind 1",
"etag": "1",
"id": "1",
"summary": "test summary 1",
"description": "test description 1",
},
{
"kind": "test kind 2",
"etag": "2",
"id": "2",
"summary": "test summary 2",
"description": "test description 2",
},
]
with patch.object(component, "execute_action", return_value=mock_emails):
result = component.as_dataframe()
assert isinstance(result, DataFrame)
assert not result.empty
data_str = str(result)
assert "test summary 2" in data_str
def test_update_build_config(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
build_config = {
"auth_link": {"value": "", "auth_tooltip": ""},
"action": {
"options": [],
"helper_text": "",
"helper_text_metadata": {},
},
}
result = component.update_build_config(build_config, "", "api_key")
assert result["auth_link"]["value"] == ""
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
assert result["action"]["options"] == []
component.api_key = "test_key"
result = component.update_build_config(build_config, "test_key", "api_key")
assert len(result["action"]["options"]) > 0

View file

@ -1,184 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from composio import Action
from langflow.components.composio.outlook_composio import ComposioOutlookAPIComponent
from langflow.schema.dataframe import DataFrame
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
from .test_base import MockComposioToolSet
class MockAction:
OUTLOOK_OUTLOOK_SEND_EMAIL = "OUTLOOK_OUTLOOK_SEND_EMAIL"
OUTLOOK_OUTLOOK_LIST_MESSAGES = "OUTLOOK_OUTLOOK_LIST_MESSAGES"
class TestOutlookComponent(ComponentTestBaseWithoutClient):
@pytest.fixture(autouse=True)
def mock_composio_toolset(self):
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
yield
@pytest.fixture
def component_class(self):
return ComposioOutlookAPIComponent
@pytest.fixture
def default_kwargs(self):
return {
"api_key": "",
"entity_id": "default",
"action": None,
}
@pytest.fixture
def file_names_mapping(self):
return [
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
]
def test_init(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
assert component.display_name == "Outlook"
assert component.app_name == "outlook"
assert "OUTLOOK_OUTLOOK_SEND_EMAIL" in component._actions_data
assert "OUTLOOK_OUTLOOK_LIST_MESSAGES" in component._actions_data
def test_execute_action_send_email(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(Action, "OUTLOOK_OUTLOOK_SEND_EMAIL", MockAction.OUTLOOK_OUTLOOK_SEND_EMAIL)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Send Email"}]
component.OUTLOOK_OUTLOOK_SEND_EMAIL_subject = "Test Subject"
component.OUTLOOK_OUTLOOK_SEND_EMAIL_body = "Test Body"
component.OUTLOOK_OUTLOOK_SEND_EMAIL_to_email = "test@example.com"
component._actions_data = {
"OUTLOOK_OUTLOOK_SEND_EMAIL": {
"display_name": "Send Email",
"action_fields": [
"OUTLOOK_OUTLOOK_SEND_EMAIL_user_id",
"OUTLOOK_OUTLOOK_SEND_EMAIL_subject",
"OUTLOOK_OUTLOOK_SEND_EMAIL_body",
"OUTLOOK_OUTLOOK_SEND_EMAIL_to_email",
"OUTLOOK_OUTLOOK_SEND_EMAIL_to_name",
"OUTLOOK_OUTLOOK_SEND_EMAIL_cc_emails",
"OUTLOOK_OUTLOOK_SEND_EMAIL_bcc_emails",
"OUTLOOK_OUTLOOK_SEND_EMAIL_is_html",
"OUTLOOK_OUTLOOK_SEND_EMAIL_save_to_sent_items",
"OUTLOOK_OUTLOOK_SEND_EMAIL_attachment",
],
},
}
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_fetch_emails(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(Action, "OUTLOOK_OUTLOOK_LIST_MESSAGES", MockAction.OUTLOOK_OUTLOOK_LIST_MESSAGES)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Messages"}]
component.OUTLOOK_OUTLOOK_LIST_MESSAGES_folder = "Inbox"
component.OUTLOOK_OUTLOOK_LIST_MESSAGES_top = 10
component._actions_data = {
"OUTLOOK_OUTLOOK_LIST_MESSAGES": {
"display_name": "List Messages",
"action_fields": [
"OUTLOOK_OUTLOOK_LIST_MESSAGES_user_id",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_folder",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_top",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_skip",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_is_read",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_importance",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_gt",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_startswith",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_endswith",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_contains",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_ge",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_lt",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_le",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_from_address",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_has_attachments",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_body_preview_contains",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_gt",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_lt",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_categories",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_select",
"OUTLOOK_OUTLOOK_LIST_MESSAGES_orderby",
],
"get_result_field": True,
"result_field": "value",
},
}
mock_toolset = MagicMock()
mock_toolset.execute_action.return_value = {
"successful": True,
"data": {"response_data": {"value": [{"subject": "Test Email", "from": "test@example.com"}]}},
}
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
result = component.execute_action()
assert result == [{"subject": "Test Email", "from": "test@example.com"}]
def test_execute_action_invalid_action(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Invalid Action"}]
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
component.execute_action()
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(Action, "OUTLOOK_OUTLOOK_SEND_EMAIL", MockAction.OUTLOOK_OUTLOOK_SEND_EMAIL)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Send Email"}]
component.OUTLOOK_OUTLOOK_SEND_EMAIL_subject = "Test Subject"
component.OUTLOOK_OUTLOOK_SEND_EMAIL_body = "Test Body"
component.OUTLOOK_OUTLOOK_SEND_EMAIL_to_email = "test@example.com"
mock_emails = [
{
"message": "Email sent successfully.",
}
]
with patch.object(component, "execute_action", return_value=mock_emails):
result = component.as_dataframe()
assert isinstance(result, DataFrame)
assert not result.empty
data_str = str(result)
assert "Email sent successfully." in data_str
def test_update_build_config(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
build_config = {
"auth_link": {"value": "", "auth_tooltip": ""},
"action": {
"options": [],
"helper_text": "",
"helper_text_metadata": {},
},
}
result = component.update_build_config(build_config, "", "api_key")
assert result["auth_link"]["value"] == ""
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
assert result["action"]["options"] == []
component.api_key = "test_key"
result = component.update_build_config(build_config, "test_key", "api_key")
assert len(result["action"]["options"]) > 0

View file

@ -1,176 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from composio import Action
from langflow.components.composio.slack_composio import ComposioSlackAPIComponent
from langflow.schema.dataframe import DataFrame
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
from .test_base import MockComposioToolSet
class MockAction:
SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL = "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL"
SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION = "SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION"
class TestSlackComponent(ComponentTestBaseWithoutClient):
@pytest.fixture(autouse=True)
def mock_composio_toolset(self):
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
yield
@pytest.fixture
def component_class(self):
return ComposioSlackAPIComponent
@pytest.fixture
def default_kwargs(self):
return {
"api_key": "",
"entity_id": "default",
"action": None,
}
@pytest.fixture
def file_names_mapping(self):
return [
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
]
def test_init(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
assert component.display_name == "Slack"
assert component.app_name == "slack"
assert "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL" in component._actions_data
assert "SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION" in component._actions_data
def test_execute_action_send_message_to_channel(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(
Action, "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL", MockAction.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL
)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Post Message To Channel"}]
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel = "random"
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text = "Test Body"
component._actions_data = {
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL": {
"display_name": "Post Message To Channel",
"action_fields": [
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_as_user",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_attachments",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_blocks",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_emoji",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_url",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_link_names",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_mrkdwn",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_parse",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_reply_broadcast",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_thread_ts",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_links",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_media",
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_username",
],
},
}
result = component.execute_action()
assert result == {"result": "mocked response"}
def test_execute_action_list_all_slack_team_users(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(
Action,
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION",
MockAction.SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION,
)
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "List Users"}]
component.SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_limit = 1
component._actions_data = {
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION": {
"display_name": "List Users",
"action_fields": [
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_limit",
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_cursor",
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_include_locale",
],
},
}
mock_toolset = MagicMock()
mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}}
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
result = component.execute_action()
assert result == {"messages": "mocked response"}
def test_execute_action_invalid_action(self, component_class, default_kwargs):
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Invalid Action"}]
# Execute action should raise ValueError
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
component.execute_action()
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
monkeypatch.setattr(
Action, "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL", MockAction.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL
)
# Setup component
component = component_class(**default_kwargs)
component.api_key = "test_key"
component.action = [{"name": "Post Message To Channel"}]
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel = "random"
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text = "Test Body"
mock_slack_messages = [
{"channel": "channel1", "user": "user1", "text": "text message 1", "ts": "ts1", "team": "team1"},
{"channel": "channel2", "user": "user2", "text": "text message 2", "ts": "ts2", "team": "team2"},
]
with patch.object(component, "execute_action", return_value=mock_slack_messages):
result = component.as_dataframe()
assert isinstance(result, DataFrame)
assert not result.empty
data_str = str(result)
assert "text message 1" in data_str
assert "text message 2" in data_str
def test_update_build_config(self, component_class, default_kwargs):
component = component_class(**default_kwargs)
build_config = {
"auth_link": {"value": "", "auth_tooltip": ""},
"action": {
"options": [],
"helper_text": "",
"helper_text_metadata": {},
},
}
result = component.update_build_config(build_config, "", "api_key")
assert result["auth_link"]["value"] == ""
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
assert result["action"]["options"] == []
component.api_key = "test_key"
result = component.update_build_config(build_config, "test_key", "api_key")
assert len(result["action"]["options"]) > 0