import pytest from mcp_server_gitlab_python.server import create_server, GitLabPythonServer from unittest.mock import MagicMock, patch def test_create_server(): server = create_server() assert server is not None def make_mock_diff(old_path, new_path, diff): m = MagicMock() m.old_path = old_path m.new_path = new_path m.diff = diff return m @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") def test_get_mr_diff_small_diff(mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() mr = MagicMock() mock_gitlab.return_value.projects.get.return_value = proj proj.mergerequests.get.return_value = mr mr.diffs.list.return_value = [ make_mock_diff("file1.txt", "file1.txt", "diff content 1"), make_mock_diff("file2.py", "file2.py", "diff content 2"), ] result = server.get_mr_diff("project/path", 1, max_size_kb=100) assert "diff" in result assert "file1.txt" in result["diff"] assert result["temp_file_path"] is None assert result["size_kb"] < 100 @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") @patch("tempfile.NamedTemporaryFile") def test_get_mr_diff_large_diff_temp_file(mock_tempfile, mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() mr = MagicMock() mock_gitlab.return_value.projects.get.return_value = proj proj.mergerequests.get.return_value = mr # Create a large diff large_diff = "x" * (101 * 1024) mock_diff = make_mock_diff("bigfile.txt", "bigfile.txt", large_diff) mr.diffs.list.return_value = [mock_diff] mock_file = MagicMock() mock_file.name = "/tmp/mr_diff_12345.diff" mock_tempfile.return_value.__enter__.return_value = mock_file result = server.get_mr_diff("project/path", 1, max_size_kb=100) assert result["diff_too_large"] is True assert result["temp_file_path"] == "/tmp/mr_diff_12345.diff" assert result["size_kb"] > 100 assert "message" in result mock_file.write.assert_called_once() @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") def test_get_mr_diff_filter_extensions(mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() mr = MagicMock() mock_gitlab.return_value.projects.get.return_value = proj proj.mergerequests.get.return_value = mr # One file should be filtered out mr.diffs.list.return_value = [ make_mock_diff("file.lock", "file.lock", "should be filtered"), make_mock_diff("file.txt", "file.txt", "should be present"), ] result = server.get_mr_diff("project/path", 1, max_size_kb=100, filter_extensions=[".lock"]) assert "file.lock" not in result["diff"] assert "file.txt" in result["diff"] assert "should be present" in result["diff"] assert "should be filtered" not in result["diff"] @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") def test_get_mr_diff_error_handling(mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") mock_gitlab.return_value.projects.get.side_effect = Exception("Not found") result = server.get_mr_diff("project/path", 1) assert "error" in result assert "Not found" in result["error"] @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") def test_run_ci_pipeline_success_with_branch(mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() pipeline = MagicMock() pipeline.id = 123 pipeline.web_url = "https://gitlab.com/project/-/pipelines/123" proj.pipelines.create.return_value = pipeline mock_gitlab.return_value.projects.get.return_value = proj result = server.run_ci_pipeline( project="project/path", branch="main", variables={"FOO": "bar"}, web_mode=False, working_directory="/tmp" ) assert result["success"] is True assert result["pipeline_id"] == 123 assert result["pipeline_url"] == "https://gitlab.com/project/-/pipelines/123" assert result["branch"] == "main" assert result["web_mode"] is False proj.pipelines.create.assert_called_once() args = proj.pipelines.create.call_args[0][0] assert args["ref"] == "main" assert {"key": "FOO", "value": "bar"} in args["variables"] @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") @patch("subprocess.run") def test_run_ci_pipeline_success_current_branch(mock_subprocess, mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() pipeline = MagicMock() pipeline.id = 456 pipeline.web_url = "https://gitlab.com/project/-/pipelines/456" proj.pipelines.create.return_value = pipeline proj.default_branch = "main" mock_gitlab.return_value.projects.get.return_value = proj # Simulate git branch detection mock_git = MagicMock() mock_git.returncode = 0 mock_git.stdout = "feature-branch" mock_subprocess.return_value = mock_git result = server.run_ci_pipeline( project="project/path", branch=None, variables=None, web_mode=False, working_directory="/tmp" ) assert result["success"] is True assert result["pipeline_id"] == 456 assert result["pipeline_url"] == "https://gitlab.com/project/-/pipelines/456" assert result["branch"] == "feature-branch" mock_subprocess.assert_called_once_with([ "git", "branch", "--show-current" ], capture_output=True, text=True, check=False, cwd="/tmp") proj.pipelines.create.assert_called_once() @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") @patch("subprocess.run") def test_run_ci_pipeline_fallback_to_default_branch(mock_subprocess, mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() pipeline = MagicMock() pipeline.id = 789 pipeline.web_url = "https://gitlab.com/project/-/pipelines/789" proj.pipelines.create.return_value = pipeline proj.default_branch = "main" mock_gitlab.return_value.projects.get.return_value = proj # Simulate git branch detection failure mock_git = MagicMock() mock_git.returncode = 1 mock_git.stdout = "" mock_subprocess.return_value = mock_git result = server.run_ci_pipeline( project="project/path", branch=None, variables=None, web_mode=False, working_directory="/tmp" ) assert result["success"] is True assert result["branch"] == "main" proj.pipelines.create.assert_called_once() @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") def test_run_ci_pipeline_web_mode(mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") proj = MagicMock() pipeline = MagicMock() pipeline.id = 321 pipeline.web_url = "https://gitlab.com/project/-/pipelines/321" proj.pipelines.create.return_value = pipeline mock_gitlab.return_value.projects.get.return_value = proj result = server.run_ci_pipeline( project="project/path", branch="main", variables={"FOO": "bar"}, web_mode=True, working_directory="/tmp" ) assert result["success"] is True assert result["web_mode"] is True args = proj.pipelines.create.call_args[0][0] # Should include CI_PIPELINE_SOURCE=web assert {"key": "CI_PIPELINE_SOURCE", "value": "web"} in args["variables"] @patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token")) @patch("gitlab.Gitlab") def test_run_ci_pipeline_error_handling(mock_gitlab, mock_settings): server = GitLabPythonServer(working_directory="/tmp") mock_gitlab.return_value.projects.get.side_effect = Exception("Not found") result = server.run_ci_pipeline( project="project/path", branch="main", variables=None, web_mode=False, working_directory="/tmp" ) assert "error" in result assert "Not found" in result["error"]