run ruff
This commit is contained in:
@@ -5,15 +5,15 @@ Tests the complete MCP tool workflow for git-based config fetching
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, MagicMock, patch, Mock
|
||||
|
||||
# Test if MCP is available
|
||||
try:
|
||||
import mcp
|
||||
from mcp.types import TextContent
|
||||
|
||||
MCP_AVAILABLE = True
|
||||
except ImportError:
|
||||
MCP_AVAILABLE = False
|
||||
@@ -31,11 +31,7 @@ def temp_dirs(tmp_path):
|
||||
cache_dir.mkdir()
|
||||
dest_dir.mkdir()
|
||||
|
||||
return {
|
||||
"config": config_dir,
|
||||
"cache": cache_dir,
|
||||
"dest": dest_dir
|
||||
}
|
||||
return {"config": config_dir, "cache": cache_dir, "dest": dest_dir}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -46,18 +42,10 @@ def mock_git_repo(temp_dirs):
|
||||
(repo_path / ".git").mkdir()
|
||||
|
||||
# Create sample config files
|
||||
react_config = {
|
||||
"name": "react",
|
||||
"description": "React framework",
|
||||
"base_url": "https://react.dev/"
|
||||
}
|
||||
react_config = {"name": "react", "description": "React framework", "base_url": "https://react.dev/"}
|
||||
(repo_path / "react.json").write_text(json.dumps(react_config, indent=2))
|
||||
|
||||
vue_config = {
|
||||
"name": "vue",
|
||||
"description": "Vue framework",
|
||||
"base_url": "https://vuejs.org/"
|
||||
}
|
||||
vue_config = {"name": "vue", "description": "Vue framework", "base_url": "https://vuejs.org/"}
|
||||
(repo_path / "vue.json").write_text(json.dumps(vue_config, indent=2))
|
||||
|
||||
return repo_path
|
||||
@@ -72,15 +60,15 @@ class TestFetchConfigModes:
|
||||
"""Test API mode - listing available configs."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.httpx.AsyncClient') as mock_client:
|
||||
with patch("skill_seekers.mcp.server.httpx.AsyncClient") as mock_client:
|
||||
# Mock API response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"configs": [
|
||||
{"name": "react", "category": "web-frameworks", "description": "React framework", "type": "single"},
|
||||
{"name": "vue", "category": "web-frameworks", "description": "Vue framework", "type": "single"}
|
||||
{"name": "vue", "category": "web-frameworks", "description": "Vue framework", "type": "single"},
|
||||
],
|
||||
"total": 2
|
||||
"total": 2,
|
||||
}
|
||||
mock_client.return_value.__aenter__.return_value.get.return_value = mock_response
|
||||
|
||||
@@ -96,28 +84,22 @@ class TestFetchConfigModes:
|
||||
"""Test API mode - downloading specific config."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.httpx.AsyncClient') as mock_client:
|
||||
with patch("skill_seekers.mcp.server.httpx.AsyncClient") as mock_client:
|
||||
# Mock API responses
|
||||
mock_detail_response = MagicMock()
|
||||
mock_detail_response.json.return_value = {
|
||||
"name": "react",
|
||||
"category": "web-frameworks",
|
||||
"description": "React framework"
|
||||
"description": "React framework",
|
||||
}
|
||||
|
||||
mock_download_response = MagicMock()
|
||||
mock_download_response.json.return_value = {
|
||||
"name": "react",
|
||||
"base_url": "https://react.dev/"
|
||||
}
|
||||
mock_download_response.json.return_value = {"name": "react", "base_url": "https://react.dev/"}
|
||||
|
||||
mock_client_instance = mock_client.return_value.__aenter__.return_value
|
||||
mock_client_instance.get.side_effect = [mock_detail_response, mock_download_response]
|
||||
|
||||
args = {
|
||||
"config_name": "react",
|
||||
"destination": str(temp_dirs["dest"])
|
||||
}
|
||||
args = {"config_name": "react", "destination": str(temp_dirs["dest"])}
|
||||
result = await fetch_config_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
@@ -128,7 +110,7 @@ class TestFetchConfigModes:
|
||||
config_file = temp_dirs["dest"] / "react.json"
|
||||
assert config_file.exists()
|
||||
|
||||
@patch('skill_seekers.mcp.server.GitConfigRepo')
|
||||
@patch("skill_seekers.mcp.server.GitConfigRepo")
|
||||
async def test_fetch_config_git_url_mode(self, mock_git_repo_class, temp_dirs):
|
||||
"""Test Git URL mode - direct git clone."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
@@ -149,7 +131,7 @@ class TestFetchConfigModes:
|
||||
args = {
|
||||
"config_name": "react",
|
||||
"git_url": "https://github.com/myorg/configs.git",
|
||||
"destination": str(temp_dirs["dest"])
|
||||
"destination": str(temp_dirs["dest"]),
|
||||
}
|
||||
result = await fetch_config_tool(args)
|
||||
|
||||
@@ -165,8 +147,8 @@ class TestFetchConfigModes:
|
||||
config_file = temp_dirs["dest"] / "react.json"
|
||||
assert config_file.exists()
|
||||
|
||||
@patch('skill_seekers.mcp.server.GitConfigRepo')
|
||||
@patch('skill_seekers.mcp.server.SourceManager')
|
||||
@patch("skill_seekers.mcp.server.GitConfigRepo")
|
||||
@patch("skill_seekers.mcp.server.SourceManager")
|
||||
async def test_fetch_config_source_mode(self, mock_source_manager_class, mock_git_repo_class, temp_dirs):
|
||||
"""Test Source mode - using named source from registry."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
@@ -177,7 +159,7 @@ class TestFetchConfigModes:
|
||||
"name": "team",
|
||||
"git_url": "https://github.com/myorg/configs.git",
|
||||
"branch": "main",
|
||||
"token_env": "GITHUB_TOKEN"
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
}
|
||||
mock_source_manager_class.return_value = mock_source_manager
|
||||
|
||||
@@ -193,11 +175,7 @@ class TestFetchConfigModes:
|
||||
mock_repo_instance.get_config.return_value = react_config
|
||||
mock_git_repo_class.return_value = mock_repo_instance
|
||||
|
||||
args = {
|
||||
"config_name": "react",
|
||||
"source": "team",
|
||||
"destination": str(temp_dirs["dest"])
|
||||
}
|
||||
args = {"config_name": "react", "source": "team", "destination": str(temp_dirs["dest"])}
|
||||
result = await fetch_config_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
@@ -216,22 +194,19 @@ class TestFetchConfigModes:
|
||||
"""Test error when source doesn't exist."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.get_source.side_effect = KeyError("Source 'nonexistent' not found")
|
||||
mock_sm_class.return_value = mock_sm
|
||||
|
||||
args = {
|
||||
"config_name": "react",
|
||||
"source": "nonexistent"
|
||||
}
|
||||
args = {"config_name": "react", "source": "nonexistent"}
|
||||
result = await fetch_config_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
assert "❌" in result[0].text
|
||||
assert "not found" in result[0].text
|
||||
|
||||
@patch('skill_seekers.mcp.server.GitConfigRepo')
|
||||
@patch("skill_seekers.mcp.server.GitConfigRepo")
|
||||
async def test_fetch_config_config_not_found_in_repo(self, mock_git_repo_class, temp_dirs):
|
||||
"""Test error when config doesn't exist in repository."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
@@ -247,10 +222,7 @@ class TestFetchConfigModes:
|
||||
)
|
||||
mock_git_repo_class.return_value = mock_repo_instance
|
||||
|
||||
args = {
|
||||
"config_name": "django",
|
||||
"git_url": "https://github.com/myorg/configs.git"
|
||||
}
|
||||
args = {"config_name": "django", "git_url": "https://github.com/myorg/configs.git"}
|
||||
result = await fetch_config_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
@@ -258,7 +230,7 @@ class TestFetchConfigModes:
|
||||
assert "not found" in result[0].text
|
||||
assert "Available configs" in result[0].text
|
||||
|
||||
@patch('skill_seekers.mcp.server.GitConfigRepo')
|
||||
@patch("skill_seekers.mcp.server.GitConfigRepo")
|
||||
async def test_fetch_config_invalid_git_url(self, mock_git_repo_class):
|
||||
"""Test error handling for invalid git URL."""
|
||||
from skill_seekers.mcp.server import fetch_config_tool
|
||||
@@ -268,10 +240,7 @@ class TestFetchConfigModes:
|
||||
mock_repo_instance.clone_or_pull.side_effect = ValueError("Invalid git URL: not-a-url")
|
||||
mock_git_repo_class.return_value = mock_repo_instance
|
||||
|
||||
args = {
|
||||
"config_name": "react",
|
||||
"git_url": "not-a-url"
|
||||
}
|
||||
args = {"config_name": "react", "git_url": "not-a-url"}
|
||||
result = await fetch_config_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
@@ -288,7 +257,7 @@ class TestSourceManagementTools:
|
||||
"""Test adding a new config source."""
|
||||
from skill_seekers.mcp.server import add_config_source_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.add_source.return_value = {
|
||||
"name": "team",
|
||||
@@ -298,14 +267,11 @@ class TestSourceManagementTools:
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
"priority": 100,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T10:00:00+00:00"
|
||||
"added_at": "2025-12-21T10:00:00+00:00",
|
||||
}
|
||||
mock_sm_class.return_value = mock_sm
|
||||
|
||||
args = {
|
||||
"name": "team",
|
||||
"git_url": "https://github.com/myorg/configs.git"
|
||||
}
|
||||
args = {"name": "team", "git_url": "https://github.com/myorg/configs.git"}
|
||||
result = await add_config_source_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
@@ -344,17 +310,14 @@ class TestSourceManagementTools:
|
||||
"""Test error when source name is invalid."""
|
||||
from skill_seekers.mcp.server import add_config_source_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.add_source.side_effect = ValueError(
|
||||
"Invalid source name 'team@company'. Must be alphanumeric with optional hyphens/underscores."
|
||||
)
|
||||
mock_sm_class.return_value = mock_sm
|
||||
|
||||
args = {
|
||||
"name": "team@company",
|
||||
"git_url": "https://github.com/myorg/configs.git"
|
||||
}
|
||||
args = {"name": "team@company", "git_url": "https://github.com/myorg/configs.git"}
|
||||
result = await add_config_source_tool(args)
|
||||
|
||||
assert len(result) == 1
|
||||
@@ -365,7 +328,7 @@ class TestSourceManagementTools:
|
||||
"""Test listing config sources."""
|
||||
from skill_seekers.mcp.server import list_config_sources_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.list_sources.return_value = [
|
||||
{
|
||||
@@ -376,7 +339,7 @@ class TestSourceManagementTools:
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
"priority": 1,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T10:00:00+00:00"
|
||||
"added_at": "2025-12-21T10:00:00+00:00",
|
||||
},
|
||||
{
|
||||
"name": "company",
|
||||
@@ -386,8 +349,8 @@ class TestSourceManagementTools:
|
||||
"token_env": "GITLAB_TOKEN",
|
||||
"priority": 2,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T11:00:00+00:00"
|
||||
}
|
||||
"added_at": "2025-12-21T11:00:00+00:00",
|
||||
},
|
||||
]
|
||||
mock_sm_class.return_value = mock_sm
|
||||
|
||||
@@ -404,7 +367,7 @@ class TestSourceManagementTools:
|
||||
"""Test listing when no sources registered."""
|
||||
from skill_seekers.mcp.server import list_config_sources_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.list_sources.return_value = []
|
||||
mock_sm_class.return_value = mock_sm
|
||||
@@ -419,7 +382,7 @@ class TestSourceManagementTools:
|
||||
"""Test listing only enabled sources."""
|
||||
from skill_seekers.mcp.server import list_config_sources_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.list_sources.return_value = [
|
||||
{
|
||||
@@ -430,7 +393,7 @@ class TestSourceManagementTools:
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
"priority": 1,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T10:00:00+00:00"
|
||||
"added_at": "2025-12-21T10:00:00+00:00",
|
||||
}
|
||||
]
|
||||
mock_sm_class.return_value = mock_sm
|
||||
@@ -448,7 +411,7 @@ class TestSourceManagementTools:
|
||||
"""Test removing a config source."""
|
||||
from skill_seekers.mcp.server import remove_config_source_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.remove_source.return_value = True
|
||||
mock_sm_class.return_value = mock_sm
|
||||
@@ -468,12 +431,12 @@ class TestSourceManagementTools:
|
||||
"""Test removing non-existent source."""
|
||||
from skill_seekers.mcp.server import remove_config_source_tool
|
||||
|
||||
with patch('skill_seekers.mcp.server.SourceManager') as mock_sm_class:
|
||||
with patch("skill_seekers.mcp.server.SourceManager") as mock_sm_class:
|
||||
mock_sm = MagicMock()
|
||||
mock_sm.remove_source.return_value = False
|
||||
mock_sm.list_sources.return_value = [
|
||||
{"name": "team", "git_url": "https://example.com/1.git"},
|
||||
{"name": "company", "git_url": "https://example.com/2.git"}
|
||||
{"name": "company", "git_url": "https://example.com/2.git"},
|
||||
]
|
||||
mock_sm_class.return_value = mock_sm
|
||||
|
||||
@@ -503,15 +466,15 @@ class TestSourceManagementTools:
|
||||
class TestCompleteWorkflow:
|
||||
"""Test complete workflow of add → fetch → remove."""
|
||||
|
||||
@patch('skill_seekers.mcp.server.GitConfigRepo')
|
||||
@patch('skill_seekers.mcp.server.SourceManager')
|
||||
@patch("skill_seekers.mcp.server.GitConfigRepo")
|
||||
@patch("skill_seekers.mcp.server.SourceManager")
|
||||
async def test_add_fetch_remove_workflow(self, mock_sm_class, mock_git_repo_class, temp_dirs):
|
||||
"""Test complete workflow: add source → fetch config → remove source."""
|
||||
from skill_seekers.mcp.server import (
|
||||
add_config_source_tool,
|
||||
fetch_config_tool,
|
||||
list_config_sources_tool,
|
||||
remove_config_source_tool
|
||||
remove_config_source_tool,
|
||||
)
|
||||
|
||||
# Step 1: Add source
|
||||
@@ -524,14 +487,11 @@ class TestCompleteWorkflow:
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
"priority": 100,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T10:00:00+00:00"
|
||||
"added_at": "2025-12-21T10:00:00+00:00",
|
||||
}
|
||||
mock_sm_class.return_value = mock_sm
|
||||
|
||||
add_result = await add_config_source_tool({
|
||||
"name": "team",
|
||||
"git_url": "https://github.com/myorg/configs.git"
|
||||
})
|
||||
add_result = await add_config_source_tool({"name": "team", "git_url": "https://github.com/myorg/configs.git"})
|
||||
assert "✅" in add_result[0].text
|
||||
|
||||
# Step 2: Fetch config from source
|
||||
@@ -539,7 +499,7 @@ class TestCompleteWorkflow:
|
||||
"name": "team",
|
||||
"git_url": "https://github.com/myorg/configs.git",
|
||||
"branch": "main",
|
||||
"token_env": "GITHUB_TOKEN"
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
}
|
||||
|
||||
mock_repo = MagicMock()
|
||||
@@ -553,27 +513,27 @@ class TestCompleteWorkflow:
|
||||
mock_repo.get_config.return_value = react_config
|
||||
mock_git_repo_class.return_value = mock_repo
|
||||
|
||||
fetch_result = await fetch_config_tool({
|
||||
"config_name": "react",
|
||||
"source": "team",
|
||||
"destination": str(temp_dirs["dest"])
|
||||
})
|
||||
fetch_result = await fetch_config_tool(
|
||||
{"config_name": "react", "source": "team", "destination": str(temp_dirs["dest"])}
|
||||
)
|
||||
assert "✅" in fetch_result[0].text
|
||||
|
||||
# Verify config file created
|
||||
assert (temp_dirs["dest"] / "react.json").exists()
|
||||
|
||||
# Step 3: List sources
|
||||
mock_sm.list_sources.return_value = [{
|
||||
"name": "team",
|
||||
"git_url": "https://github.com/myorg/configs.git",
|
||||
"type": "github",
|
||||
"branch": "main",
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
"priority": 100,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T10:00:00+00:00"
|
||||
}]
|
||||
mock_sm.list_sources.return_value = [
|
||||
{
|
||||
"name": "team",
|
||||
"git_url": "https://github.com/myorg/configs.git",
|
||||
"type": "github",
|
||||
"branch": "main",
|
||||
"token_env": "GITHUB_TOKEN",
|
||||
"priority": 100,
|
||||
"enabled": True,
|
||||
"added_at": "2025-12-21T10:00:00+00:00",
|
||||
}
|
||||
]
|
||||
|
||||
list_result = await list_config_sources_tool({})
|
||||
assert "team" in list_result[0].text
|
||||
|
||||
Reference in New Issue
Block a user