summaryrefslogtreecommitdiff
path: root/servers/gitlab_glab/tests
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-03-28 21:39:04 +0100
committerDawid Rycerz <dawid@rycerz.xyz>2025-03-28 21:39:04 +0100
commit903f0d9ca388533ab44615e414379fa5b305a7d1 (patch)
treed4225b3b07e11792d06660b31da97f786b5578e9 /servers/gitlab_glab/tests
parent1745749cd2745c94c3f852e9c02dfde19d8d9c20 (diff)
Add basic glab mcp server
Diffstat (limited to 'servers/gitlab_glab/tests')
-rw-r--r--servers/gitlab_glab/tests/conftest.py105
-rw-r--r--servers/gitlab_glab/tests/test_cli.py159
-rw-r--r--servers/gitlab_glab/tests/test_cli_integration.py133
-rw-r--r--servers/gitlab_glab/tests/test_integration.py85
-rw-r--r--servers/gitlab_glab/tests/test_main.py15
-rw-r--r--servers/gitlab_glab/tests/test_server.py221
6 files changed, 718 insertions, 0 deletions
diff --git a/servers/gitlab_glab/tests/conftest.py b/servers/gitlab_glab/tests/conftest.py
new file mode 100644
index 0000000..c7afaf9
--- /dev/null
+++ b/servers/gitlab_glab/tests/conftest.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+"""Pytest configuration and fixtures for GitLab CLI MCP server tests."""
+
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from mcp_server_gitlab_glab.server import GitLabServer, create_server
+
+
+@pytest.fixture
+def gitlab_server() -> GitLabServer:
+ """Fixture for a GitLabServer instance."""
+ return GitLabServer()
+
+
+@pytest.fixture
+def mock_subprocess_run() -> MagicMock:
+ """Fixture for mocking subprocess.run."""
+ with patch("subprocess.run") as mock_run:
+ yield mock_run
+
+
+@pytest.fixture
+def mock_successful_command(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking a successful command execution."""
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = "command output"
+ mock_process.stderr = ""
+ mock_subprocess_run.return_value = mock_process
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mock_failed_command(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking a failed command execution."""
+ mock_process = MagicMock()
+ mock_process.returncode = 1
+ mock_process.stdout = ""
+ mock_process.stderr = "command failed"
+ mock_subprocess_run.return_value = mock_process
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mock_auth_error_command(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking an authentication error command execution."""
+ mock_process = MagicMock()
+ mock_process.returncode = 1
+ mock_process.stdout = ""
+ mock_process.stderr = "authentication required"
+ mock_subprocess_run.return_value = mock_process
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mock_successful_api_command(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking a successful API command execution."""
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = '[{"id": 1, "name": "test-project"}]'
+ mock_process.stderr = ""
+ mock_subprocess_run.return_value = mock_process
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mock_empty_api_command(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking an API command with empty results."""
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = "[]"
+ mock_process.stderr = ""
+ mock_subprocess_run.return_value = mock_process
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mock_invalid_json_api_command(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking an API command with invalid JSON response."""
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = "invalid json"
+ mock_process.stderr = ""
+ mock_subprocess_run.return_value = mock_process
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mock_command_not_found(mock_subprocess_run: MagicMock) -> MagicMock:
+ """Fixture for mocking a command not found error."""
+ mock_subprocess_run.side_effect = FileNotFoundError(
+ "No such file or directory: 'glab'"
+ )
+ return mock_subprocess_run
+
+
+@pytest.fixture
+def mcp_server() -> MagicMock:
+ """Fixture for a mocked MCP server."""
+ with patch("mcp.server.fastmcp.FastMCP") as mock_fastmcp:
+ mock_server = MagicMock()
+ mock_fastmcp.return_value = mock_server
+ yield create_server()
diff --git a/servers/gitlab_glab/tests/test_cli.py b/servers/gitlab_glab/tests/test_cli.py
new file mode 100644
index 0000000..316556c
--- /dev/null
+++ b/servers/gitlab_glab/tests/test_cli.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+"""Tests for the GitLab CLI MCP server CLI functionality."""
+
+import argparse
+import logging
+from unittest.mock import MagicMock, patch
+
+from mcp_server_gitlab_glab.cli import parse_args, setup_logging, validate_args
+
+
+class TestCli:
+ """Tests for the CLI functionality."""
+
+ def test_parse_args_defaults(self) -> None:
+ """Test parsing command-line arguments with defaults."""
+ # Mock sys.argv
+ with patch("sys.argv", ["mcp-gitlab-glab"]):
+ args = parse_args()
+ assert args.transport == "stdio"
+ assert args.host == "127.0.0.1"
+ assert args.port == 8080
+ assert args.log_level == "INFO"
+
+ def test_parse_args_custom(self) -> None:
+ """Test parsing command-line arguments with custom values."""
+ # Mock sys.argv
+ with patch(
+ "sys.argv",
+ [
+ "mcp-gitlab-glab",
+ "--transport",
+ "remote",
+ "--host",
+ "0.0.0.0",
+ "--port",
+ "9000",
+ "--log-level",
+ "DEBUG",
+ ],
+ ):
+ args = parse_args()
+ assert args.transport == "remote"
+ assert args.host == "0.0.0.0"
+ assert args.port == 9000
+ assert args.log_level == "DEBUG"
+
+ def test_validate_args_low_port_warning(self) -> None:
+ """Test validation warning for low port on Unix-like systems."""
+ # Create args with low port
+ args = argparse.Namespace(transport="remote", port=80, host="0.0.0.0")
+
+ # Mock logger and platform
+ with patch("mcp_server_gitlab_glab.cli.logger") as mock_logger:
+ with patch("sys.platform", "linux"):
+ validate_args(args)
+ mock_logger.warning.assert_called_once()
+
+ def test_validate_args_no_warning_windows(self) -> None:
+ """Test no validation warning for low port on Windows."""
+ # Create args with low port
+ args = argparse.Namespace(transport="remote", port=80, host="0.0.0.0")
+
+ # Mock logger and platform
+ with patch("mcp_server_gitlab_glab.cli.logger") as mock_logger:
+ with patch("sys.platform", "win32"):
+ validate_args(args)
+ mock_logger.warning.assert_not_called()
+
+ def test_validate_args_no_warning_high_port(self) -> None:
+ """Test no validation warning for high port."""
+ # Create args with high port
+ args = argparse.Namespace(transport="remote", port=8080, host="0.0.0.0")
+
+ # Mock logger
+ with patch("mcp_server_gitlab_glab.cli.logger") as mock_logger:
+ validate_args(args)
+ mock_logger.warning.assert_not_called()
+
+ def test_setup_logging_stdio(self) -> None:
+ """Test logging setup for stdio transport."""
+ # Mock os.makedirs and logging configuration
+ with patch("os.makedirs") as mock_makedirs:
+ with patch("logging.FileHandler") as mock_file_handler:
+ with patch("logging.StreamHandler") as mock_stream_handler:
+ with patch("logging.getLogger") as mock_get_logger:
+ # Mock root logger
+ mock_root_logger = MagicMock()
+ mock_get_logger.return_value = mock_root_logger
+
+ # Mock handlers
+ mock_file_handler_instance = MagicMock()
+ mock_file_handler.return_value = mock_file_handler_instance
+ mock_stream_handler_instance = MagicMock()
+ mock_stream_handler.return_value = mock_stream_handler_instance
+
+ # Call setup_logging
+ setup_logging("INFO", "stdio")
+
+ # Verify logs directory creation
+ mock_makedirs.assert_called_once_with("logs", exist_ok=True)
+
+ # Verify file handler setup
+ mock_file_handler.assert_called_once_with("logs/mcp_server.log")
+ mock_file_handler_instance.setLevel.assert_called_once_with(
+ logging.INFO
+ )
+ mock_file_handler_instance.setFormatter.assert_called_once()
+
+ # Verify stream handler setup for stdio
+ mock_stream_handler_instance.setLevel.assert_called_once_with(
+ logging.WARNING
+ )
+ mock_stream_handler_instance.setFormatter.assert_called_once()
+
+ # Verify root logger setup
+ mock_root_logger.setLevel.assert_called_once_with(logging.INFO)
+ assert mock_root_logger.handlers == []
+ assert mock_root_logger.addHandler.call_count == 2
+
+ def test_setup_logging_remote(self) -> None:
+ """Test logging setup for remote transport."""
+ # Mock os.makedirs and logging configuration
+ with patch("os.makedirs") as mock_makedirs:
+ with patch("logging.FileHandler") as mock_file_handler:
+ with patch("logging.StreamHandler") as mock_stream_handler:
+ with patch("logging.getLogger") as mock_get_logger:
+ # Mock root logger
+ mock_root_logger = MagicMock()
+ mock_get_logger.return_value = mock_root_logger
+
+ # Mock handlers
+ mock_file_handler_instance = MagicMock()
+ mock_file_handler.return_value = mock_file_handler_instance
+ mock_stream_handler_instance = MagicMock()
+ mock_stream_handler.return_value = mock_stream_handler_instance
+
+ # Call setup_logging
+ setup_logging("DEBUG", "remote")
+
+ # Verify logs directory creation
+ mock_makedirs.assert_called_once_with("logs", exist_ok=True)
+
+ # Verify file handler setup
+ mock_file_handler.assert_called_once_with("logs/mcp_server.log")
+ mock_file_handler_instance.setLevel.assert_called_once_with(
+ logging.DEBUG
+ )
+ mock_file_handler_instance.setFormatter.assert_called_once()
+
+ # Verify stream handler setup for remote
+ mock_stream_handler_instance.setLevel.assert_called_once_with(
+ logging.DEBUG
+ )
+ mock_stream_handler_instance.setFormatter.assert_called_once()
+
+ # Verify root logger setup
+ mock_root_logger.setLevel.assert_called_once_with(logging.DEBUG)
+ assert mock_root_logger.handlers == []
+ assert mock_root_logger.addHandler.call_count == 2
diff --git a/servers/gitlab_glab/tests/test_cli_integration.py b/servers/gitlab_glab/tests/test_cli_integration.py
new file mode 100644
index 0000000..c9493ac
--- /dev/null
+++ b/servers/gitlab_glab/tests/test_cli_integration.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+"""Integration tests for the GitLab CLI MCP server CLI functionality."""
+
+from unittest.mock import MagicMock, patch
+
+from mcp_server_gitlab_glab.cli import run_server
+
+
+class TestCliIntegration:
+ """Integration tests for the CLI functionality."""
+
+ @patch("mcp_server_gitlab_glab.cli.validate_args")
+ @patch("mcp_server_gitlab_glab.cli.parse_args")
+ @patch("mcp_server_gitlab_glab.cli.setup_logging")
+ @patch("mcp_server_gitlab_glab.cli.asyncio.run")
+ def test_run_server_success(
+ self,
+ mock_asyncio_run: MagicMock,
+ mock_setup_logging: MagicMock,
+ mock_parse_args: MagicMock,
+ mock_validate_args: MagicMock,
+ ) -> None:
+ """Test successful server run."""
+ # Mock args
+ mock_args = MagicMock()
+ mock_args.transport = "stdio"
+ mock_args.host = "127.0.0.1"
+ mock_args.port = 8080
+ mock_args.log_level = "INFO"
+ mock_parse_args.return_value = mock_args
+ mock_validate_args.return_value = mock_args
+
+ # Call run_server
+ run_server()
+
+ # Verify args were parsed and validated
+ mock_parse_args.assert_called_once()
+ mock_validate_args.assert_called_once_with(mock_args)
+
+ # Verify logging was set up
+ mock_setup_logging.assert_called_once_with("INFO", "stdio")
+
+ # Verify server was run
+ mock_asyncio_run.assert_called_once()
+
+ @patch("mcp_server_gitlab_glab.cli.validate_args")
+ @patch("mcp_server_gitlab_glab.cli.parse_args")
+ @patch("mcp_server_gitlab_glab.cli.setup_logging")
+ @patch("mcp_server_gitlab_glab.cli.asyncio.run")
+ @patch("mcp_server_gitlab_glab.cli.logger")
+ def test_run_server_keyboard_interrupt(
+ self,
+ mock_logger: MagicMock,
+ mock_asyncio_run: MagicMock,
+ mock_setup_logging: MagicMock,
+ mock_parse_args: MagicMock,
+ mock_validate_args: MagicMock,
+ ) -> None:
+ """Test server run with keyboard interrupt."""
+ # Mock args
+ mock_args = MagicMock()
+ mock_args.transport = "stdio"
+ mock_args.host = "127.0.0.1"
+ mock_args.port = 8080
+ mock_args.log_level = "INFO"
+ mock_parse_args.return_value = mock_args
+ mock_validate_args.return_value = mock_args
+
+ # Mock KeyboardInterrupt
+ mock_asyncio_run.side_effect = KeyboardInterrupt()
+
+ # Call run_server
+ run_server()
+
+ # Verify args were parsed and validated
+ mock_parse_args.assert_called_once()
+ mock_validate_args.assert_called_once_with(mock_args)
+
+ # Verify logging was set up
+ mock_setup_logging.assert_called_once_with("INFO", "stdio")
+
+ # Verify server was run
+ mock_asyncio_run.assert_called_once()
+
+ # Verify keyboard interrupt was logged
+ mock_logger.info.assert_called_once_with("Server stopped by user")
+
+ @patch("mcp_server_gitlab_glab.cli.validate_args")
+ @patch("mcp_server_gitlab_glab.cli.parse_args")
+ @patch("mcp_server_gitlab_glab.cli.setup_logging")
+ @patch("mcp_server_gitlab_glab.cli.asyncio.run")
+ @patch("mcp_server_gitlab_glab.cli.logger")
+ @patch("mcp_server_gitlab_glab.cli.sys.exit")
+ def test_run_server_exception(
+ self,
+ mock_sys_exit: MagicMock,
+ mock_logger: MagicMock,
+ mock_asyncio_run: MagicMock,
+ mock_setup_logging: MagicMock,
+ mock_parse_args: MagicMock,
+ mock_validate_args: MagicMock,
+ ) -> None:
+ """Test server run with exception."""
+ # Mock args
+ mock_args = MagicMock()
+ mock_args.transport = "stdio"
+ mock_args.host = "127.0.0.1"
+ mock_args.port = 8080
+ mock_args.log_level = "INFO"
+ mock_parse_args.return_value = mock_args
+ mock_validate_args.return_value = mock_args
+
+ # Mock Exception
+ mock_asyncio_run.side_effect = Exception("Test error")
+
+ # Call run_server
+ run_server()
+
+ # Verify args were parsed and validated
+ mock_parse_args.assert_called_once()
+ mock_validate_args.assert_called_once_with(mock_args)
+
+ # Verify logging was set up
+ mock_setup_logging.assert_called_once_with("INFO", "stdio")
+
+ # Verify server was run
+ mock_asyncio_run.assert_called_once()
+
+ # Verify exception was logged
+ mock_logger.error.assert_called_once_with("Error running server: Test error")
+
+ # Verify sys.exit was called
+ mock_sys_exit.assert_called_once_with(1)
diff --git a/servers/gitlab_glab/tests/test_integration.py b/servers/gitlab_glab/tests/test_integration.py
new file mode 100644
index 0000000..3bad1cc
--- /dev/null
+++ b/servers/gitlab_glab/tests/test_integration.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+"""Integration tests for the GitLab CLI MCP server."""
+
+from unittest.mock import AsyncMock, MagicMock, patch
+
+from mcp_server_gitlab_glab.server import create_server, main
+
+
+class TestIntegration:
+ """Integration tests for the GitLab CLI MCP server."""
+
+ @patch("mcp_server_gitlab_glab.server.FastMCP")
+ def test_create_server(self, mock_fastmcp: MagicMock) -> None:
+ """Test server creation with default parameters."""
+ # Mock FastMCP instance
+ mock_server = MagicMock()
+ mock_fastmcp.return_value = mock_server
+
+ # Call create_server
+ create_server()
+
+ # Verify FastMCP was created with correct parameters
+ mock_fastmcp.assert_called_once_with("GitLab CLI", host="127.0.0.1", port=8080)
+
+ # Verify tools were registered
+ assert mock_server.tool.call_count == 2
+
+ @patch("mcp_server_gitlab_glab.server.FastMCP")
+ def test_create_server_custom_params(self, mock_fastmcp: MagicMock) -> None:
+ """Test server creation with custom parameters."""
+ # Mock FastMCP instance
+ mock_server = MagicMock()
+ mock_fastmcp.return_value = mock_server
+
+ # Call create_server with custom parameters
+ create_server(host="0.0.0.0", port=9000)
+
+ # Verify FastMCP was created with correct parameters
+ mock_fastmcp.assert_called_once_with("GitLab CLI", host="0.0.0.0", port=9000)
+
+ @patch("mcp_server_gitlab_glab.server.create_server")
+ @patch("mcp_server_gitlab_glab.server.logger")
+ async def test_main_stdio(
+ self, mock_logger: MagicMock, mock_create_server: MagicMock
+ ) -> None:
+ """Test main function with stdio transport."""
+ # Mock server
+ mock_server = AsyncMock()
+ mock_create_server.return_value = mock_server
+
+ # Call main with stdio transport
+ await main("stdio", "127.0.0.1", 8080)
+
+ # Verify server was created with correct parameters
+ mock_create_server.assert_called_once_with(host="127.0.0.1", port=8080)
+
+ # Verify stdio transport was used
+ mock_server.run_stdio_async.assert_called_once()
+ mock_server.run_sse_async.assert_not_called()
+
+ # Verify logging
+ assert mock_logger.info.call_count >= 2
+
+ @patch("mcp_server_gitlab_glab.server.create_server")
+ @patch("mcp_server_gitlab_glab.server.logger")
+ async def test_main_remote(
+ self, mock_logger: MagicMock, mock_create_server: MagicMock
+ ) -> None:
+ """Test main function with remote transport."""
+ # Mock server
+ mock_server = AsyncMock()
+ mock_create_server.return_value = mock_server
+
+ # Call main with remote transport
+ await main("remote", "0.0.0.0", 9000)
+
+ # Verify server was created with correct parameters
+ mock_create_server.assert_called_once_with(host="0.0.0.0", port=9000)
+
+ # Verify remote transport was used
+ mock_server.run_stdio_async.assert_not_called()
+ mock_server.run_sse_async.assert_called_once()
+
+ # Verify logging
+ assert mock_logger.info.call_count >= 2
diff --git a/servers/gitlab_glab/tests/test_main.py b/servers/gitlab_glab/tests/test_main.py
new file mode 100644
index 0000000..2302018
--- /dev/null
+++ b/servers/gitlab_glab/tests/test_main.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+"""Tests for the GitLab CLI MCP server main module."""
+
+
+
+class TestMain:
+ """Tests for the main module."""
+
+ def test_main_module_import(self) -> None:
+ """Test main module can be imported."""
+ # Simply import the module to ensure it can be imported without errors
+ import mcp_server_gitlab_glab.__main__
+
+ # Verify the module has the expected attributes
+ assert hasattr(mcp_server_gitlab_glab.__main__, "run_server")
diff --git a/servers/gitlab_glab/tests/test_server.py b/servers/gitlab_glab/tests/test_server.py
new file mode 100644
index 0000000..1c27ea5
--- /dev/null
+++ b/servers/gitlab_glab/tests/test_server.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python3
+"""Tests for the GitLab CLI MCP server implementation."""
+
+import json
+from unittest.mock import MagicMock, patch
+
+from mcp_server_gitlab_glab.server import GitLabServer
+
+
+class TestGitLabServer:
+ """Tests for the GitLabServer class."""
+
+ def test_init(self) -> None:
+ """Test initialization of GitLabServer."""
+ server = GitLabServer()
+ assert hasattr(server, "auth_message")
+ assert "glab auth login" in server.auth_message
+
+ @patch("subprocess.run")
+ def test_execute_glab_command_success(self, mock_run: MagicMock) -> None:
+ """Test successful execution of a glab command."""
+ # Mock successful command execution
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = "command output"
+ mock_process.stderr = ""
+ mock_run.return_value = mock_process
+
+ server = GitLabServer()
+ success, result = server.execute_glab_command(["--version"])
+
+ assert success is True
+ assert result == "command output"
+ mock_run.assert_called_once_with(
+ ["glab", "--version"],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+ @patch("subprocess.run")
+ def test_execute_glab_command_failure(self, mock_run: MagicMock) -> None:
+ """Test failed execution of a glab command."""
+ # Mock failed command execution
+ mock_process = MagicMock()
+ mock_process.returncode = 1
+ mock_process.stdout = ""
+ mock_process.stderr = "command failed"
+ mock_run.return_value = mock_process
+
+ server = GitLabServer()
+ success, result = server.execute_glab_command(["--version"])
+
+ assert success is False
+ assert result == {"error": "command failed"}
+ mock_run.assert_called_once_with(
+ ["glab", "--version"],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+ @patch("subprocess.run")
+ def test_execute_glab_command_auth_error(self, mock_run: MagicMock) -> None:
+ """Test authentication error during glab command execution."""
+ # Mock authentication error
+ mock_process = MagicMock()
+ mock_process.returncode = 1
+ mock_process.stdout = ""
+ mock_process.stderr = "authentication required"
+ mock_run.return_value = mock_process
+
+ server = GitLabServer()
+ success, result = server.execute_glab_command(["api", "/projects"])
+
+ assert success is False
+ assert "error" in result
+ assert "auth login" in result["error"]
+ mock_run.assert_called_once_with(
+ ["glab", "api", "/projects"],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+ @patch("subprocess.run")
+ def test_execute_glab_command_not_found(self, mock_run: MagicMock) -> None:
+ """Test glab command not found error."""
+ # Mock FileNotFoundError
+ mock_run.side_effect = FileNotFoundError("No such file or directory: 'glab'")
+
+ server = GitLabServer()
+ success, result = server.execute_glab_command(["--version"])
+
+ assert success is False
+ assert "error" in result
+ assert "glab command not found" in result["error"]
+
+ @patch("subprocess.run")
+ def test_execute_glab_api_command_success(self, mock_run: MagicMock) -> None:
+ """Test successful execution of a glab api command with JSON response."""
+ # Mock successful API command execution with JSON response
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = json.dumps([{"id": 1, "name": "test-project"}])
+ mock_process.stderr = ""
+ mock_run.return_value = mock_process
+
+ server = GitLabServer()
+ success, result = server.execute_glab_command(["api", "/projects"])
+
+ assert success is True
+ assert isinstance(result, list)
+ assert len(result) == 1
+ assert result[0]["id"] == 1
+ assert result[0]["name"] == "test-project"
+ mock_run.assert_called_once_with(
+ ["glab", "api", "/projects"],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+
+ @patch("subprocess.run")
+ def test_execute_glab_api_command_invalid_json(self, mock_run: MagicMock) -> None:
+ """Test glab api command with invalid JSON response."""
+ # Mock API command execution with invalid JSON response
+ mock_process = MagicMock()
+ mock_process.returncode = 0
+ mock_process.stdout = "invalid json"
+ mock_process.stderr = ""
+ mock_run.return_value = mock_process
+
+ server = GitLabServer()
+ success, result = server.execute_glab_command(["api", "/projects"])
+
+ assert success is False
+ assert "error" in result
+ assert "Failed to parse JSON response" in result["error"]
+
+ @patch.object(GitLabServer, "execute_glab_command")
+ def test_check_availability_success(self, mock_execute: MagicMock) -> None:
+ """Test successful check_availability."""
+ # Mock successful command execution
+ mock_execute.return_value = (True, "glab version 1.0.0")
+
+ server = GitLabServer()
+ result = server.check_availability()
+
+ assert result["available"] is True
+ assert result["version"] == "glab version 1.0.0"
+ mock_execute.assert_called_once_with(["--version"])
+
+ @patch.object(GitLabServer, "execute_glab_command")
+ def test_check_availability_failure(self, mock_execute: MagicMock) -> None:
+ """Test failed check_availability."""
+ # Mock failed command execution
+ mock_execute.return_value = (False, {"error": "glab command not found"})
+
+ server = GitLabServer()
+ result = server.check_availability()
+
+ assert result["available"] is False
+ assert result["error"] == "glab command not found"
+ mock_execute.assert_called_once_with(["--version"])
+
+ @patch.object(GitLabServer, "execute_glab_command")
+ def test_find_project_success(self, mock_execute: MagicMock) -> None:
+ """Test successful find_project."""
+ # Mock successful API response with a project
+ mock_execute.return_value = (
+ True,
+ [
+ {
+ "id": 1,
+ "name": "test-project",
+ "path_with_namespace": "group/test-project",
+ "web_url": "https://gitlab.com/group/test-project",
+ "description": "A test project",
+ }
+ ],
+ )
+
+ server = GitLabServer()
+ result = server.find_project("test-project")
+
+ assert "id" in result
+ assert result["id"] == 1
+ assert result["name"] == "test-project"
+ assert result["path_with_namespace"] == "group/test-project"
+ assert result["web_url"] == "https://gitlab.com/group/test-project"
+ assert result["description"] == "A test project"
+ mock_execute.assert_called_once_with(["api", "/projects?search=test-project"])
+
+ @patch.object(GitLabServer, "execute_glab_command")
+ def test_find_project_not_found(self, mock_execute: MagicMock) -> None:
+ """Test find_project with no results."""
+ # Mock API response with no projects
+ mock_execute.return_value = (True, [])
+
+ server = GitLabServer()
+ result = server.find_project("nonexistent-project")
+
+ assert "error" in result
+ assert "not found" in result["error"]
+ mock_execute.assert_called_once_with(
+ ["api", "/projects?search=nonexistent-project"]
+ )
+
+ @patch.object(GitLabServer, "execute_glab_command")
+ def test_find_project_api_error(self, mock_execute: MagicMock) -> None:
+ """Test find_project with API error."""
+ # Mock API error
+ mock_execute.return_value = (False, {"error": "API error"})
+
+ server = GitLabServer()
+ result = server.find_project("test-project")
+
+ assert "error" in result
+ assert result["error"] == "API error"
+ mock_execute.assert_called_once_with(["api", "/projects?search=test-project"])