diff options
Diffstat (limited to 'servers/gitlab_glab/tests/test_server.py')
| -rw-r--r-- | servers/gitlab_glab/tests/test_server.py | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/servers/gitlab_glab/tests/test_server.py b/servers/gitlab_glab/tests/test_server.py index ab93026..13f90af 100644 --- a/servers/gitlab_glab/tests/test_server.py +++ b/servers/gitlab_glab/tests/test_server.py @@ -585,3 +585,725 @@ class TestGitLabServer: # Verify the number of calls assert mock_run.call_count == len(working_dirs) + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_success_small(self, mock_execute: MagicMock) -> None: + """Test successful MR diff retrieval with small diff.""" + # Mock successful command execution with small diff + diff_content = """diff --git a/file.txt b/file.txt +index 1234567..abcdefg 100644 +--- a/file.txt ++++ b/file.txt +@@ -1,3 +1,4 @@ + line 1 + line 2 ++new line + line 3""" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + mr_id="123", + color="never", + raw=False, + repo="group/project", + ) + + assert "diff" in result + assert result["diff"] == diff_content + assert result["size_kb"] < 1 + assert result["temp_file_path"] is None + + # Verify command was called with correct arguments + mock_execute.assert_called_once_with( + ["mr", "diff", "123", "--color", "never", "-R", "group/project"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_success_current_branch(self, mock_execute: MagicMock) -> None: + """Test successful MR diff retrieval for current branch.""" + diff_content = "diff content" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff(working_directory=working_dir) + + assert "diff" in result + assert result["diff"] == diff_content + assert result["temp_file_path"] is None + + # Verify command was called without MR ID + mock_execute.assert_called_once_with( + ["mr", "diff", "--color", "never"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_with_raw_option(self, mock_execute: MagicMock) -> None: + """Test MR diff retrieval with raw option.""" + diff_content = "raw diff content" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + mr_id="branch-name", + color="auto", + raw=True, + ) + + assert "diff" in result + assert result["diff"] == diff_content + + # Verify command was called with raw option + mock_execute.assert_called_once_with( + ["mr", "diff", "branch-name", "--color", "auto", "--raw"], + working_dir, + ) + + @patch("tempfile.NamedTemporaryFile") + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_large_diff_temp_file( + self, mock_execute: MagicMock, mock_temp_file: MagicMock + ) -> None: + """Test MR diff retrieval with large diff that gets saved to temp file.""" + # Create a large diff content (over 100KB) + large_diff = "x" * (101 * 1024) # 101 KB + mock_execute.return_value = (True, large_diff) + + # Mock temporary file + mock_file = MagicMock() + mock_file.name = "/tmp/mr_diff_12345.diff" + mock_temp_file.return_value.__enter__.return_value = mock_file + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + mr_id="123", + max_size_kb=100, + ) + + assert result["diff_too_large"] is True + assert result["size_kb"] > 100 + assert result["max_size_kb"] == 100 + assert result["temp_file_path"] == "/tmp/mr_diff_12345.diff" + assert "message" in result + + # Verify temp file was created with correct parameters + mock_temp_file.assert_called_once_with( + mode='w', + suffix='.diff', + prefix='mr_diff_', + delete=False, + encoding='utf-8' + ) + mock_file.write.assert_called_once_with(large_diff) + + @patch("tempfile.NamedTemporaryFile") + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_large_diff_temp_file_error( + self, mock_execute: MagicMock, mock_temp_file: MagicMock + ) -> None: + """Test MR diff retrieval with large diff and temp file creation error.""" + # Create a large diff content + large_diff = "x" * (101 * 1024) # 101 KB + mock_execute.return_value = (True, large_diff) + + # Mock temporary file creation error + mock_temp_file.side_effect = Exception("Permission denied") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + mr_id="123", + max_size_kb=100, + ) + + assert "error" in result + assert "too large" in result["error"] + assert "Permission denied" in result["error"] + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_command_failure(self, mock_execute: MagicMock) -> None: + """Test MR diff retrieval with command failure.""" + mock_execute.return_value = (False, {"error": "MR not found"}) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff(working_directory=working_dir, mr_id="999") + + assert "error" in result + assert result["error"] == "MR not found" + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_invalid_color_option(self, mock_execute: MagicMock) -> None: + """Test MR diff with invalid color option skips color parameter.""" + diff_content = "diff content" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + mr_id="123", + color="invalid", # Invalid color option + ) + + assert "diff" in result + # Invalid color options should be filtered out, only valid ones are added + mock_execute.assert_called_once_with( + ["mr", "diff", "123"], + working_dir, + ) + + # Tests for run_ci_pipeline method + + @patch.object(GitLabServer, "execute_glab_command") + @patch("subprocess.run") + def test_run_ci_pipeline_success_with_branch( + self, mock_subprocess: MagicMock, mock_execute: MagicMock + ) -> None: + """Test successful pipeline run with specified branch.""" + # Mock successful glab command execution + mock_execute.return_value = (True, "Pipeline created: https://gitlab.example.com/project/-/pipelines/123") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + branch="main", + ) + + assert result["success"] is True + assert result["branch"] == "main" + assert result["web_mode"] is False + assert "pipeline_url" in result + assert result["pipeline_url"] == "https://gitlab.example.com/project/-/pipelines/123" + mock_execute.assert_called_once_with( + ["ci", "run", "-b", "main"], + working_dir, + ) + # subprocess.run should not be called when branch is specified + mock_subprocess.assert_not_called() + + @patch.object(GitLabServer, "execute_glab_command") + @patch("subprocess.run") + def test_run_ci_pipeline_success_current_branch( + self, mock_subprocess: MagicMock, mock_execute: MagicMock + ) -> None: + """Test successful pipeline run using current branch.""" + # Mock git branch --show-current command + mock_git_process = MagicMock() + mock_git_process.returncode = 0 + mock_git_process.stdout = "feature-branch" + mock_subprocess.return_value = mock_git_process + + # Mock successful glab command execution + mock_execute.return_value = (True, "Pipeline created: https://gitlab.example.com/project/-/pipelines/456") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + ) + + assert result["success"] is True + assert result["branch"] == "feature-branch" + assert result["web_mode"] is False + assert "pipeline_url" in result + assert result["pipeline_url"] == "https://gitlab.example.com/project/-/pipelines/456" + + mock_subprocess.assert_called_once_with( + ["git", "branch", "--show-current"], + capture_output=True, + text=True, + check=False, + cwd=working_dir, + ) + mock_execute.assert_called_once_with( + ["ci", "run", "-b", "feature-branch"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + @patch("subprocess.run") + def test_run_ci_pipeline_git_branch_failure( + self, mock_subprocess: MagicMock, mock_execute: MagicMock + ) -> None: + """Test pipeline run when git branch --show-current fails.""" + # Mock git branch --show-current command failure + mock_git_process = MagicMock() + mock_git_process.returncode = 1 + mock_git_process.stdout = "" + mock_subprocess.return_value = mock_git_process + + # Mock successful glab command execution (without branch) + mock_execute.return_value = (True, "Pipeline created: https://gitlab.example.com/project/-/pipelines/789") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + ) + + assert result["success"] is True + assert result["branch"] is None + assert result["web_mode"] is False + + mock_subprocess.assert_called_once_with( + ["git", "branch", "--show-current"], + capture_output=True, + text=True, + check=False, + cwd=working_dir, + ) + # Should run without -b flag when branch detection fails + mock_execute.assert_called_once_with( + ["ci", "run"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + def test_run_ci_pipeline_with_web_mode(self, mock_execute: MagicMock) -> None: + """Test pipeline run with web mode enabled.""" + # Mock successful glab command execution + mock_execute.return_value = (True, "Pipeline created in web mode") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + branch="main", + web_mode=True, + ) + + assert result["success"] is True + assert result["branch"] == "main" + assert result["web_mode"] is True + + mock_execute.assert_called_once_with( + ["ci", "run", "-b", "main", "--variables-env", "CI_PIPELINE_SOURCE:web"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + def test_run_ci_pipeline_with_variables(self, mock_execute: MagicMock) -> None: + """Test pipeline run with various variable types.""" + # Mock successful glab command execution + mock_execute.return_value = (True, "Pipeline created with variables") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + branch="develop", + variables=["VAR1:value1", "VAR2:value2"], + variables_env=["ENV1:envvalue1"], + variables_file=["FILE1:file1.txt"], + variables_from="/path/to/vars.json", + repo="group/project", + ) + + assert result["success"] is True + assert result["branch"] == "develop" + + expected_args = [ + "ci", "run", + "-b", "develop", + "--variables", "VAR1:value1", + "--variables", "VAR2:value2", + "--variables-env", "ENV1:envvalue1", + "--variables-file", "FILE1:file1.txt", + "-f", "/path/to/vars.json", + "-R", "group/project" + ] + mock_execute.assert_called_once_with(expected_args, working_dir) + + @patch.object(GitLabServer, "execute_glab_command") + def test_run_ci_pipeline_with_web_mode_and_variables( + self, mock_execute: MagicMock + ) -> None: + """Test pipeline run with web mode and additional variables.""" + # Mock successful glab command execution + mock_execute.return_value = (True, "Pipeline created") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + branch="main", + variables_env=["CUSTOM_VAR:value"], + web_mode=True, + ) + + assert result["success"] is True + assert result["web_mode"] is True + + expected_args = [ + "ci", "run", + "-b", "main", + "--variables-env", "CUSTOM_VAR:value", + "--variables-env", "CI_PIPELINE_SOURCE:web" + ] + mock_execute.assert_called_once_with(expected_args, working_dir) + + @patch.object(GitLabServer, "execute_glab_command") + def test_run_ci_pipeline_glab_failure(self, mock_execute: MagicMock) -> None: + """Test pipeline run when glab command fails.""" + # Mock glab command failure + mock_execute.return_value = (False, {"error": "Authentication required"}) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + branch="main", + ) + + assert result == {"error": "Authentication required"} + mock_execute.assert_called_once_with( + ["ci", "run", "-b", "main"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + def test_run_ci_pipeline_no_url_in_output(self, mock_execute: MagicMock) -> None: + """Test pipeline run when output doesn't contain pipeline URL.""" + # Mock successful glab command execution without URL + mock_execute.return_value = ( + True, + "Pipeline created successfully\nCheck status later" + ) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + branch="main", + ) + + assert result["success"] is True + assert result["branch"] == "main" + assert "pipeline_url" not in result + assert "Pipeline created successfully" in result["output"] + + @patch.object(GitLabServer, "execute_glab_command") + @patch("subprocess.run") + def test_run_ci_pipeline_git_exception( + self, mock_subprocess: MagicMock, mock_execute: MagicMock + ) -> None: + """Test pipeline run when git command raises an exception.""" + # Mock git branch --show-current command exception + mock_subprocess.side_effect = Exception("Git command failed") + + # Mock successful glab command execution (without branch) + mock_execute.return_value = (True, "Pipeline created") + + server = GitLabServer() + working_dir = "/test/directory" + result = server.run_ci_pipeline( + working_directory=working_dir, + ) + + assert result["success"] is True + assert result["branch"] is None + + # Should run without -b flag when git command fails + mock_execute.assert_called_once_with( + ["ci", "run"], + working_dir, + ) + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_with_default_filtering(self, mock_execute: MagicMock) -> None: + """Test MR diff with default filtering (.lock and .log files).""" + # Mock diff content with .lock and .log files + diff_content = """diff --git a/package-lock.json b/package-lock.json +index 1234567..abcdefg 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1,3 +1,4 @@ + { + "name": "test", ++ "version": "1.0.0", + "lockfileVersion": 1 +} +diff --git a/debug.log b/debug.log +index 1234567..abcdefg 100644 +--- a/debug.log ++++ b/debug.log +@@ -1,2 +1,3 @@ + [INFO] Starting application ++[INFO] Loading configuration + [INFO] Application ready +diff --git a/src/main.py b/src/main.py +index 1234567..abcdefg 100644 +--- a/src/main.py ++++ b/src/main.py +@@ -1,3 +1,4 @@ + def main(): + print("Hello") ++ print("World") + return 0""" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff(working_directory=working_dir) + + # Check that .lock and .log files are filtered out + filtered_diff = result["diff"] + assert "package-lock.json" not in filtered_diff + assert "debug.log" not in filtered_diff + assert "src/main.py" in filtered_diff + assert 'print("World")' in filtered_diff + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_with_custom_filtering(self, mock_execute: MagicMock) -> None: + """Test MR diff with custom filtering extensions.""" + # Mock diff content with various file types + diff_content = """diff --git a/test.txt b/test.txt +index 1234567..abcdefg 100644 +--- a/test.txt ++++ b/test.txt +@@ -1,2 +1,3 @@ + line 1 ++new line + line 2 +diff --git a/config.json b/config.json +index 1234567..abcdefg 100644 +--- a/config.json ++++ b/config.json +@@ -1,3 +1,4 @@ + { + "setting": "value" ++ "new_setting": "new_value" + } +diff --git a/temp.tmp b/temp.tmp +index 1234567..abcdefg 100644 +--- a/temp.tmp ++++ b/temp.tmp +@@ -1,2 +1,3 @@ + temp data ++more temp data + end""" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + filter_extensions=[".tmp", ".json"] + ) + + # Check that .tmp and .json files are filtered out + filtered_diff = result["diff"] + assert "temp.tmp" not in filtered_diff + assert "config.json" not in filtered_diff + assert "test.txt" in filtered_diff + assert "new line" in filtered_diff + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_with_no_filtering(self, mock_execute: MagicMock) -> None: + """Test MR diff with filtering disabled (empty list).""" + # Mock diff content with .lock file + diff_content = """diff --git a/package-lock.json b/package-lock.json +index 1234567..abcdefg 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1,3 +1,4 @@ + { + "name": "test", ++ "version": "1.0.0", + "lockfileVersion": 1 +}""" + mock_execute.return_value = (True, diff_content) + + server = GitLabServer() + working_dir = "/test/directory" + result = server.get_mr_diff( + working_directory=working_dir, + filter_extensions=[] + ) + + # Check that no filtering occurred + filtered_diff = result["diff"] + assert "package-lock.json" in filtered_diff + assert '"version": "1.0.0"' in filtered_diff + + def test_filter_diff_content_basic(self) -> None: + """Test basic diff content filtering.""" + server = GitLabServer() + + diff_content = """diff --git a/src/main.py b/src/main.py +index 1234567..abcdefg 100644 +--- a/src/main.py ++++ b/src/main.py +@@ -1,3 +1,4 @@ + def main(): + print("Hello") ++ print("World") + return 0 +diff --git a/package-lock.json b/package-lock.json +index 1234567..abcdefg 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1,3 +1,4 @@ + { + "name": "test", ++ "version": "1.0.0", + "lockfileVersion": 1 +}""" + + filtered = server.filter_diff_content(diff_content, [".lock"]) + + assert "src/main.py" in filtered + assert 'print("World")' in filtered + assert "package-lock.json" not in filtered + assert '"version": "1.0.0"' not in filtered + + def test_filter_diff_content_multiple_extensions(self) -> None: + """Test filtering with multiple extensions.""" + server = GitLabServer() + + diff_content = """diff --git a/app.py b/app.py +index 1234567..abcdefg 100644 +--- a/app.py ++++ b/app.py +@@ -1,2 +1,3 @@ + import logging ++logging.basicConfig() + app = Flask(__name__) +diff --git a/debug.log b/debug.log +index 1234567..abcdefg 100644 +--- a/debug.log ++++ b/debug.log +@@ -1,2 +1,3 @@ + [INFO] Starting ++[DEBUG] Debug message + [INFO] Ready +diff --git a/yarn.lock b/yarn.lock +index 1234567..abcdefg 100644 +--- a/yarn.lock ++++ b/yarn.lock +@@ -1,3 +1,4 @@ + # THIS IS AN AUTOGENERATED FILE ++# Dependencies + package@1.0.0: + version "1.0.0""" + + filtered = server.filter_diff_content(diff_content, [".log", ".lock"]) + + assert "app.py" in filtered + assert "logging.basicConfig()" in filtered + assert "debug.log" not in filtered + assert "yarn.lock" not in filtered + assert "[DEBUG] Debug message" not in filtered + assert "# Dependencies" not in filtered + + def test_filter_diff_content_empty_extensions(self) -> None: + """Test filtering with empty extensions list.""" + server = GitLabServer() + + diff_content = """diff --git a/package-lock.json b/package-lock.json +index 1234567..abcdefg 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1,2 +1,3 @@ + { ++ "name": "test", + "lockfileVersion": 1 +}""" + + filtered = server.filter_diff_content(diff_content, []) + + # Should return original content when no extensions to filter + assert filtered == diff_content + + def test_filter_diff_content_malformed_diff_header(self) -> None: + """Test filtering with malformed diff headers.""" + server = GitLabServer() + + diff_content = """diff --git a/file.py +index 1234567..abcdefg 100644 +--- a/file.py ++++ b/file.py +@@ -1,2 +1,3 @@ + line 1 ++new line + line 2 +diff --git a/package-lock.json b/package-lock.json +index 1234567..abcdefg 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1,2 +1,3 @@ + { ++ "version": "1.0.0", + "lockfileVersion": 1 +}""" + + filtered = server.filter_diff_content(diff_content, [".lock"]) + + # Malformed header should not be filtered + assert "diff --git a/file.py" in filtered + assert "new line" in filtered + # Properly formed header with .lock extension should be filtered + assert "package-lock.json" not in filtered + assert '"version": "1.0.0"' not in filtered + + @patch.object(GitLabServer, "execute_glab_command") + def test_get_mr_diff_filtering_with_large_file(self, mock_execute: MagicMock) -> None: + """Test that filtering works correctly even when creating temp files.""" + # Create a large diff that will still be large after filtering + large_diff_base = """diff --git a/src/main.py b/src/main.py +index 1234567..abcdefg 100644 +--- a/src/main.py ++++ b/src/main.py +@@ -1,3 +1,4 @@ + def main(): + print("Hello") ++ print("World") + return 0 +""" + # Add a lot of content to main.py to make it large + large_diff = large_diff_base + "+ # " + "Large comment line\n" * 3000 + + # Add package-lock.json content that should be filtered out + large_diff += """diff --git a/package-lock.json b/package-lock.json +index 1234567..abcdefg 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -1,500 +1,501 @@ + { + "name": "test", ++ "version": "1.0.0", +""" + " \"dependency\": \"^1.0.0\",\n" * 1000 + "}\n" + + mock_execute.return_value = (True, large_diff) + + server = GitLabServer() + working_dir = "/test/directory" + + with patch("tempfile.NamedTemporaryFile") as mock_temp: + mock_file = MagicMock() + mock_file.name = "/tmp/test_diff.diff" + mock_temp.return_value.__enter__.return_value = mock_file + + result = server.get_mr_diff( + working_directory=working_dir, + max_size_kb=1 # Force temp file creation + ) + + # Should still filter before saving to temp file + assert result["diff_too_large"] is True + # The filtered content should be written to temp file + written_content = mock_file.write.call_args[0][0] + assert "src/main.py" in written_content + assert "package-lock.json" not in written_content |
