|
3 | 3 | import pytest |
4 | 4 | from unittest.mock import AsyncMock, MagicMock, patch |
5 | 5 | from fastmcp import Context |
6 | | -from tools.fetch_artifacts import fetch_artifacts |
| 6 | +from tools.fetch_artifacts import fetch_artifacts, _add_line_numbers, _build_artifacts_xml |
| 7 | + |
| 8 | + |
| 9 | +class TestAddLineNumbers: |
| 10 | + """Test cases for _add_line_numbers helper.""" |
| 11 | + |
| 12 | + def test_multi_line_content(self): |
| 13 | + content = "line1\nline2\nline3" |
| 14 | + result = _add_line_numbers(content) |
| 15 | + assert result == "1 | line1\n2 | line2\n3 | line3" |
| 16 | + |
| 17 | + def test_single_line_content(self): |
| 18 | + content = "only one line" |
| 19 | + result = _add_line_numbers(content) |
| 20 | + assert result == "1 | only one line" |
| 21 | + |
| 22 | + def test_empty_content(self): |
| 23 | + assert _add_line_numbers("") == "" |
| 24 | + |
| 25 | + def test_right_aligned_padding(self): |
| 26 | + lines = "\n".join(f"line{i}" for i in range(100)) |
| 27 | + result = _add_line_numbers(lines) |
| 28 | + first_line = result.split("\n")[0] |
| 29 | + assert first_line == " 1 | line0" |
| 30 | + last_line = result.split("\n")[99] |
| 31 | + assert last_line == "100 | line99" |
| 32 | + |
| 33 | + def test_start_line_offset(self): |
| 34 | + result = _add_line_numbers("a\nb", start_line=50) |
| 35 | + assert result == "50 | a\n51 | b" |
| 36 | + |
| 37 | + def test_start_line_default(self): |
| 38 | + result = _add_line_numbers("x", start_line=1) |
| 39 | + assert result == "1 | x" |
| 40 | + |
| 41 | + def test_start_line_right_aligned_padding(self): |
| 42 | + result = _add_line_numbers("a\nb\nc", start_line=98) |
| 43 | + assert result == " 98 | a\n 99 | b\n100 | c" |
| 44 | + |
| 45 | + def test_start_line_empty_content(self): |
| 46 | + assert _add_line_numbers("", start_line=50) == "" |
| 47 | + |
| 48 | + |
| 49 | +class TestBuildArtifactsXmlStartLine: |
| 50 | + """Test _build_artifacts_xml uses startLine from API response.""" |
| 51 | + |
| 52 | + def test_artifact_with_start_line(self): |
| 53 | + data = {"artifacts": [ |
| 54 | + {"identifier": "repo::file.py::func", "content": "line1\nline2", "contentByteSize": 10, "startLine": 50} |
| 55 | + ]} |
| 56 | + result = _build_artifacts_xml(data) |
| 57 | + assert "50 | line1" in result |
| 58 | + assert "51 | line2" in result |
| 59 | + |
| 60 | + def test_artifact_without_start_line_defaults_to_1(self): |
| 61 | + data = {"artifacts": [ |
| 62 | + {"identifier": "repo::file.py::func", "content": "line1\nline2", "contentByteSize": 10} |
| 63 | + ]} |
| 64 | + result = _build_artifacts_xml(data) |
| 65 | + assert "1 | line1" in result |
| 66 | + assert "2 | line2" in result |
| 67 | + |
| 68 | + def test_artifact_with_null_start_line_defaults_to_1(self): |
| 69 | + data = {"artifacts": [ |
| 70 | + {"identifier": "repo::file.py", "content": "hello", "contentByteSize": 5, "startLine": None} |
| 71 | + ]} |
| 72 | + result = _build_artifacts_xml(data) |
| 73 | + assert "1 | hello" in result |
7 | 74 |
|
8 | 75 |
|
9 | 76 | @pytest.mark.asyncio |
@@ -52,8 +119,9 @@ async def test_fetch_artifacts_returns_xml(mock_get_api_key): |
52 | 119 | assert isinstance(result, str) |
53 | 120 | assert "<artifacts>" in result |
54 | 121 | assert "</artifacts>" in result |
55 | | - # Found artifact has content |
56 | | - assert "def login(user, pwd):" in result |
| 122 | + # Found artifact has line-numbered content |
| 123 | + assert "1 | def login(user, pwd):" in result |
| 124 | + assert "2 | return True" in result |
57 | 125 | assert 'contentByteSize="38"' in result |
58 | 126 | assert 'identifier="owner/repo::src/auth.py::login"' in result |
59 | 127 | # Not-found artifact is skipped (not in output) |
@@ -204,6 +272,8 @@ async def test_fetch_artifacts_escapes_xml(mock_get_api_key): |
204 | 272 | identifiers=["owner/repo::file.py::func"], |
205 | 273 | ) |
206 | 274 |
|
| 275 | + # Line numbers are added before escaping |
| 276 | + assert "1 | if x < 10 && y > 5:" in result |
207 | 277 | assert "<" in result |
208 | 278 | assert "&" in result |
209 | 279 | assert "<artifacts>" in result |
|
0 commit comments