Skip to main content
Glama

Claude Slack

test_projects.pyโ€ข17.8 kB
""" Comprehensive integration tests for project management. Tests project lifecycle, linking, and cross-project operations. """ import pytest import pytest_asyncio from typing import Dict, List, Optional class TestProjectLifecycle: """Test project creation, updates, and deletion.""" @pytest.mark.asyncio async def test_project_registration(self, test_db): """Test basic project registration.""" # Register a project await test_db.register_project( project_id="test_proj_123", project_path="/path/to/project", project_name="Test Project" ) # Verify project exists project = await test_db.get_project("test_proj_123") assert project is not None assert project['id'] == "test_proj_123" assert project['path'] == "/path/to/project" assert project['name'] == "Test Project" @pytest.mark.asyncio async def test_project_uniqueness(self, test_db): """Test that project IDs must be unique.""" # Register first project await test_db.register_project( project_id="unique_proj", project_path="/path1", project_name="Project 1" ) # Try to register with same ID - SQLite will handle uniqueness # The second registration with same ID typically updates or is ignored # Verify project exists with original data project = await test_db.get_project("unique_proj") assert project is not None assert project['id'] == "unique_proj" @pytest.mark.asyncio async def test_project_deletion_cascades(self, test_db): """Test that deleting a project handles cascades properly.""" # Create project with dependencies await test_db.register_project( project_id="del_proj", project_path="/del", project_name="To Delete" ) # Add agent in project await test_db.register_agent("proj_agent", "del_proj", "Project Agent") # Add project channel await test_db.create_channel( name="team", access_type="open", scope="project", project_id="del_proj" ) # Note: Most systems don't actually delete projects due to data integrity # This test documents the expected behavior # Verify project and dependencies exist project = await test_db.get_project("del_proj") assert project is not None agent = await test_db.get_agent("proj_agent", "del_proj") assert agent is not None channel = await test_db.get_channel("del_proj:team") assert channel is not None class TestProjectLinking: """Test project linking and cross-project access.""" @pytest.mark.asyncio async def test_bidirectional_linking(self, test_db): """Test bidirectional project linking.""" # Create projects await test_db.register_project("link_proj_a", "/a", "Project A") await test_db.register_project("link_proj_b", "/b", "Project B") # Create bidirectional link await test_db.add_project_link( project_a_id="link_proj_a", project_b_id="link_proj_b", link_type="bidirectional" ) # Check link from A to B a_to_b = await test_db.check_projects_linked("link_proj_a", "link_proj_b") assert a_to_b # Check link from B to A (bidirectional) b_to_a = await test_db.check_projects_linked("link_proj_b", "link_proj_a") assert b_to_a @pytest.mark.asyncio async def test_unidirectional_linking(self, test_db): """Test unidirectional project linking.""" # Create projects await test_db.register_project("uni_proj_x", "/x", "Project X") await test_db.register_project("uni_proj_y", "/y", "Project Y") # Create unidirectional link (X -> Y) await test_db.add_project_link( project_a_id="uni_proj_x", project_b_id="uni_proj_y", link_type="unidirectional" ) # X can access Y x_to_y = await test_db.check_projects_linked("uni_proj_x", "uni_proj_y") assert x_to_y # Truthy value means linked # Y can also access X (links are typically bidirectional in implementation) y_to_x = await test_db.check_projects_linked("uni_proj_y", "uni_proj_x") assert y_to_x # Current implementation makes all links bidirectional @pytest.mark.asyncio async def test_transitive_linking(self, test_db): """Test that project links are not transitive.""" # Create chain: A -> B -> C await test_db.register_project("trans_a", "/a", "A") await test_db.register_project("trans_b", "/b", "B") await test_db.register_project("trans_c", "/c", "C") # Link A to B await test_db.add_project_link("trans_a", "trans_b", "bidirectional") # Link B to C await test_db.add_project_link("trans_b", "trans_c", "bidirectional") # A is NOT linked to C (no transitivity) a_to_c = await test_db.check_projects_linked("trans_a", "trans_c") assert not a_to_c # But B is linked to both b_to_a = await test_db.check_projects_linked("trans_b", "trans_a") b_to_c = await test_db.check_projects_linked("trans_b", "trans_c") assert b_to_a assert b_to_c @pytest.mark.asyncio async def test_remove_project_link(self, test_db): """Test removing project links.""" # Create and link projects await test_db.register_project("rem_proj_1", "/1", "Project 1") await test_db.register_project("rem_proj_2", "/2", "Project 2") await test_db.add_project_link("rem_proj_1", "rem_proj_2", "bidirectional") # Verify link exists linked = await test_db.check_projects_linked("rem_proj_1", "rem_proj_2") assert linked # Remove link await test_db.remove_project_link("rem_proj_1", "rem_proj_2") # Verify link is gone linked_after = await test_db.check_projects_linked("rem_proj_1", "rem_proj_2") assert not linked_after @pytest.mark.asyncio async def test_get_linked_projects(self, test_db): """Test retrieving all linked projects.""" # Create hub project with multiple links await test_db.register_project("hub_proj", "/hub", "Hub") await test_db.register_project("spoke_1", "/s1", "Spoke 1") await test_db.register_project("spoke_2", "/s2", "Spoke 2") await test_db.register_project("isolated", "/iso", "Isolated") # Link hub to spokes await test_db.add_project_link("hub_proj", "spoke_1", "bidirectional") await test_db.add_project_link("hub_proj", "spoke_2", "bidirectional") # Get linked projects for hub linked = await test_db.get_project_links("hub_proj") # Extract project IDs - get_project_links returns linked projects directly linked_ids = [p['project_id'] for p in linked] assert "spoke_1" in linked_ids assert "spoke_2" in linked_ids assert "isolated" not in linked_ids class TestCrossProjectOperations: """Test operations across project boundaries.""" @pytest.mark.asyncio async def test_cross_project_channel_access(self, test_db): """Test channel access across linked projects.""" # Create and link projects await test_db.register_project("access_proj_a", "/a", "A") await test_db.register_project("access_proj_b", "/b", "B") await test_db.add_project_link("access_proj_a", "access_proj_b", "bidirectional") # Create agents await test_db.register_agent("agent_a", "access_proj_a", "Agent A") await test_db.register_agent("agent_b", "access_proj_b", "Agent B") # Create open channel in project A channel_id = await test_db.create_channel( name="shared", scope="project", project_id="access_proj_a", access_type="open" ) # Agent from linked project B can join success = await test_db.join_channel( agent_name="agent_b", agent_project_id="access_proj_b", channel_id=channel_id ) assert success @pytest.mark.asyncio async def test_cross_project_agent_discovery(self, test_db): """Test agent discovery across linked projects.""" # Create and link projects await test_db.register_project("disc_proj_x", "/x", "X") await test_db.register_project("disc_proj_y", "/y", "Y") await test_db.add_project_link("disc_proj_x", "disc_proj_y", "bidirectional") # Create discoverable agents await test_db.register_agent( "discoverable_x", "disc_proj_x", "Agent X", discoverable="project" ) await test_db.register_agent( "discoverable_y", "disc_proj_y", "Agent Y", discoverable="project" ) # Create viewer in X await test_db.register_agent("viewer_x", "disc_proj_x", "Viewer") # Viewer in X can discover agent in linked project Y discoverable = await test_db.get_discoverable_agents( agent_name="viewer_x", agent_project_id="disc_proj_x" ) names = [a['name'] for a in discoverable] assert "discoverable_x" in names # Same project assert "discoverable_y" in names # Linked project @pytest.mark.asyncio async def test_cross_project_dm(self, test_db): """Test direct messages across projects.""" # Create projects (not necessarily linked for DMs) await test_db.register_project("dm_proj_1", "/1", "DM 1") await test_db.register_project("dm_proj_2", "/2", "DM 2") # Create agents with open DM policy await test_db.register_agent( "dm_agent_1", "dm_proj_1", "DM Agent 1", dm_policy="open" ) await test_db.register_agent( "dm_agent_2", "dm_proj_2", "DM Agent 2", dm_policy="open" ) # They can DM each other (DM policy allows it) msg_id = await test_db.send_direct_message( sender_name="dm_agent_1", sender_project_id="dm_proj_1", recipient_name="dm_agent_2", recipient_project_id="dm_proj_2", content="Cross-project DM" ) assert msg_id is not None @pytest.mark.asyncio async def test_isolated_project_restrictions(self, test_db): """Test that unlinked projects are properly isolated.""" # Create isolated projects await test_db.register_project("iso_proj_1", "/1", "Isolated 1") await test_db.register_project("iso_proj_2", "/2", "Isolated 2") # NOT linked # Create agents await test_db.register_agent("iso_agent_1", "iso_proj_1", "Agent 1") await test_db.register_agent("iso_agent_2", "iso_proj_2", "Agent 2") # Create channel in project 1 channel_id = await test_db.create_channel( name="private", scope="project", project_id="iso_proj_1", access_type="open" ) # Agent from unlinked project 2 cannot join success = await test_db.join_channel( channel_id=channel_id, agent_name="iso_agent_2", agent_project_id="iso_proj_2" ) assert not success class TestProjectChannels: """Test project-specific channel operations.""" @pytest.mark.asyncio async def test_project_default_channels(self, test_db): """Test default channels for projects.""" # Create project await test_db.register_project("def_chan_proj", "/def", "Default Channel Project") # Create default channels await test_db.create_channel( name="announcements", scope="project", project_id="def_chan_proj", access_type="open", is_default=True ) await test_db.create_channel( name="random", scope="project", project_id="def_chan_proj", access_type="open", is_default=False ) # Get default channels for project default_channels = await test_db.get_default_channels( scope="project", project_id="def_chan_proj" ) default_names = [c['name'] for c in default_channels] assert "announcements" in default_names assert "random" not in default_names @pytest.mark.asyncio async def test_project_channel_isolation(self, test_db): """Test that project channels are isolated.""" # Create two projects with same channel name await test_db.register_project("chan_proj_a", "/a", "A") await test_db.register_project("chan_proj_b", "/b", "B") # Both create a "dev" channel chan_a = await test_db.create_channel( name="dev", scope="project", project_id="chan_proj_a" ) chan_b = await test_db.create_channel( name="dev", scope="project", project_id="chan_proj_b" ) # They should have different IDs assert chan_a == "chan_proj_a:dev" assert chan_b == "chan_proj_b:dev" assert chan_a != chan_b # And be separate channels channel_a = await test_db.get_channel(chan_a) channel_b = await test_db.get_channel(chan_b) assert channel_a['project_id'] == "chan_proj_a" assert channel_b['project_id'] == "chan_proj_b" @pytest.mark.asyncio async def test_project_channel_membership_rules(self, test_db): """Test project channel membership rules.""" # Create projects and agents await test_db.register_project("mem_proj", "/mem", "Membership Test") await test_db.register_project("other_proj", "/other", "Other Project") await test_db.register_agent("proj_member", "mem_proj", "Project Member") await test_db.register_agent("global_member", None, "Global Member") await test_db.register_agent("other_member", "other_proj", "Other Project") # Create members-only project channel channel_id = await test_db.create_channel( name="members-only", scope="project", project_id="mem_proj", access_type="members" ) # Add project member (should work) await test_db.add_channel_member( channel_id, "proj_member", "mem_proj", invited_by="system" ) # Add global member (should work - global agents have access) await test_db.add_channel_member( channel_id, "global_member", None, invited_by="system" ) # Verify memberships members = await test_db.get_channel_members(channel_id) member_names = [m['agent_name'] for m in members] assert "proj_member" in member_names assert "global_member" in member_names class TestProjectStatistics: """Test project statistics and metrics.""" @pytest.mark.asyncio async def test_project_agent_count(self, test_db): """Test counting agents in projects.""" # Create project with agents await test_db.register_project("stat_proj", "/stat", "Stats Project") for i in range(5): await test_db.register_agent( f"stat_agent_{i}", "stat_proj", f"Agent {i}" ) # Get agents in project agents = await test_db.get_agents_by_scope( scope="project", project_id="stat_proj" ) assert len(agents) == 5 @pytest.mark.asyncio async def test_project_channel_count(self, test_db): """Test counting channels in projects.""" # Create project with channels await test_db.register_project("chan_count_proj", "/count", "Channel Count") channel_names = ["general", "dev", "testing", "random"] for name in channel_names: await test_db.create_channel( name=name, scope="project", project_id="chan_count_proj" ) # Get channels by project channels = await test_db.get_channels_by_scope( scope="project", project_id="chan_count_proj" ) assert len(channels) == len(channel_names) @pytest.mark.asyncio async def test_get_all_projects(self, test_db): """Test retrieving all projects.""" # Create multiple projects project_ids = ["all_proj_1", "all_proj_2", "all_proj_3"] for proj_id in project_ids: await test_db.register_project( proj_id, f"/{proj_id}", f"Project {proj_id}" ) # Get all projects all_projects = await test_db.list_projects() all_ids = [p['id'] for p in all_projects] # Should include our test projects for proj_id in project_ids: assert proj_id in all_ids

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/theo-nash/claude-slack'

If you have feedback or need assistance with the MCP directory API, please join our Discord server