|
| 1 | +"""Tests for zip file support.""" |
| 2 | +import pytest |
| 3 | +import sys |
| 4 | +import zipfile |
| 5 | +import tempfile |
| 6 | +from pathlib import Path |
| 7 | + |
| 8 | +sys.path.insert(0, str(Path(__file__).parent.parent)) |
| 9 | +from compare_runs import _resolve_path, compare_reports, _temp_dirs |
| 10 | + |
| 11 | + |
| 12 | +class TestResolvePath: |
| 13 | + """Tests for _resolve_path function.""" |
| 14 | + |
| 15 | + def test_regular_directory_unchanged(self, temp_test_dir): |
| 16 | + """Regular directories should be returned unchanged.""" |
| 17 | + result = _resolve_path(temp_test_dir) |
| 18 | + assert result == temp_test_dir |
| 19 | + |
| 20 | + def test_regular_file_unchanged(self, temp_test_dir): |
| 21 | + """Regular files should be returned unchanged.""" |
| 22 | + csv_path = temp_test_dir / "report.csv" |
| 23 | + result = _resolve_path(csv_path) |
| 24 | + assert result == csv_path |
| 25 | + |
| 26 | + def test_zip_file_extraction(self, sample_csv_content): |
| 27 | + """Zip files should be extracted to a temp directory.""" |
| 28 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 29 | + # Create a zip file with report.csv |
| 30 | + zip_path = Path(tmpdir) / "report.zip" |
| 31 | + with zipfile.ZipFile(zip_path, "w") as zf: |
| 32 | + zf.writestr("report.csv", sample_csv_content) |
| 33 | + |
| 34 | + result = _resolve_path(zip_path) |
| 35 | + |
| 36 | + # Result should be a different path (extracted) |
| 37 | + assert result != zip_path |
| 38 | + assert result.is_dir() |
| 39 | + # Should contain the extracted file |
| 40 | + assert (result / "report.csv").exists() |
| 41 | + |
| 42 | + def test_zip_with_single_directory(self, sample_csv_content): |
| 43 | + """Zip containing a single directory should return that directory.""" |
| 44 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 45 | + zip_path = Path(tmpdir) / "report.zip" |
| 46 | + with zipfile.ZipFile(zip_path, "w") as zf: |
| 47 | + # Put files inside a subdirectory |
| 48 | + zf.writestr("HTML-Report-123/report.csv", sample_csv_content) |
| 49 | + |
| 50 | + result = _resolve_path(zip_path) |
| 51 | + |
| 52 | + # Should return the inner directory |
| 53 | + assert result.name == "HTML-Report-123" |
| 54 | + assert (result / "report.csv").exists() |
| 55 | + |
| 56 | + def test_zip_with_multiple_files(self, sample_csv_content, sample_html_template_args): |
| 57 | + """Zip with multiple files at root should return the extraction root.""" |
| 58 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 59 | + zip_path = Path(tmpdir) / "report.zip" |
| 60 | + with zipfile.ZipFile(zip_path, "w") as zf: |
| 61 | + zf.writestr("report.csv", sample_csv_content) |
| 62 | + zf.writestr("feature.html", sample_html_template_args) |
| 63 | + |
| 64 | + result = _resolve_path(zip_path) |
| 65 | + |
| 66 | + # Should return the temp directory root |
| 67 | + assert (result / "report.csv").exists() |
| 68 | + assert (result / "feature.html").exists() |
| 69 | + |
| 70 | + def test_invalid_zip_raises_error(self): |
| 71 | + """Files with .zip extension that aren't valid zips should raise.""" |
| 72 | + with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as f: |
| 73 | + f.write(b"not a zip file") |
| 74 | + f.flush() |
| 75 | + |
| 76 | + with pytest.raises(ValueError, match="not a valid zip file"): |
| 77 | + _resolve_path(Path(f.name)) |
| 78 | + |
| 79 | + def test_nonexistent_path_unchanged(self): |
| 80 | + """Non-existent paths should be returned unchanged (let load_report handle error).""" |
| 81 | + path = Path("/nonexistent/path") |
| 82 | + result = _resolve_path(path) |
| 83 | + assert result == path |
| 84 | + |
| 85 | + def test_temp_dirs_tracked_for_cleanup(self, sample_csv_content): |
| 86 | + """Extracted temp directories should be tracked for cleanup.""" |
| 87 | + initial_count = len(_temp_dirs) |
| 88 | + |
| 89 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 90 | + zip_path = Path(tmpdir) / "report.zip" |
| 91 | + with zipfile.ZipFile(zip_path, "w") as zf: |
| 92 | + zf.writestr("report.csv", sample_csv_content) |
| 93 | + |
| 94 | + _resolve_path(zip_path) |
| 95 | + |
| 96 | + # Should have added a temp dir |
| 97 | + assert len(_temp_dirs) > initial_count |
| 98 | + |
| 99 | + |
| 100 | +class TestCompareReportsWithZip: |
| 101 | + """Tests for compare_reports with zip file input.""" |
| 102 | + |
| 103 | + def test_compare_zip_to_zip(self, sample_csv_content, sample_csv_content_v2, capsys): |
| 104 | + """Should be able to compare two zip files.""" |
| 105 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 106 | + base_zip = Path(tmpdir) / "base.zip" |
| 107 | + curr_zip = Path(tmpdir) / "current.zip" |
| 108 | + |
| 109 | + with zipfile.ZipFile(base_zip, "w") as zf: |
| 110 | + zf.writestr("report.csv", sample_csv_content) |
| 111 | + |
| 112 | + with zipfile.ZipFile(curr_zip, "w") as zf: |
| 113 | + zf.writestr("report.csv", sample_csv_content_v2) |
| 114 | + |
| 115 | + result = compare_reports(base_zip, curr_zip, as_json=True) |
| 116 | + assert result == 0 |
| 117 | + |
| 118 | + captured = capsys.readouterr() |
| 119 | + assert "/api/users" in captured.out |
| 120 | + |
| 121 | + def test_compare_zip_to_directory(self, sample_csv_content, temp_test_dir_v2, capsys): |
| 122 | + """Should be able to compare a zip file to a directory.""" |
| 123 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 124 | + base_zip = Path(tmpdir) / "base.zip" |
| 125 | + |
| 126 | + with zipfile.ZipFile(base_zip, "w") as zf: |
| 127 | + zf.writestr("report.csv", sample_csv_content) |
| 128 | + |
| 129 | + result = compare_reports(base_zip, temp_test_dir_v2, as_json=True) |
| 130 | + assert result == 0 |
| 131 | + |
| 132 | + def test_compare_directory_to_zip(self, temp_test_dir, sample_csv_content_v2, capsys): |
| 133 | + """Should be able to compare a directory to a zip file.""" |
| 134 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 135 | + curr_zip = Path(tmpdir) / "current.zip" |
| 136 | + |
| 137 | + with zipfile.ZipFile(curr_zip, "w") as zf: |
| 138 | + zf.writestr("report.csv", sample_csv_content_v2) |
| 139 | + |
| 140 | + result = compare_reports(temp_test_dir, curr_zip, as_json=True) |
| 141 | + assert result == 0 |
| 142 | + |
| 143 | + def test_zip_with_nested_directory_structure(self, sample_csv_content, sample_csv_content_v2, capsys): |
| 144 | + """Zip with report inside a subdirectory should work.""" |
| 145 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 146 | + base_zip = Path(tmpdir) / "base.zip" |
| 147 | + curr_zip = Path(tmpdir) / "current.zip" |
| 148 | + |
| 149 | + with zipfile.ZipFile(base_zip, "w") as zf: |
| 150 | + zf.writestr("HTML-Report-100/report.csv", sample_csv_content) |
| 151 | + |
| 152 | + with zipfile.ZipFile(curr_zip, "w") as zf: |
| 153 | + zf.writestr("HTML-Report-200/report.csv", sample_csv_content_v2) |
| 154 | + |
| 155 | + result = compare_reports(base_zip, curr_zip, as_json=True) |
| 156 | + assert result == 0 |
| 157 | + |
| 158 | + def test_zip_with_html_files(self, sample_csv_content, sample_html_template_args, capsys): |
| 159 | + """Zip containing HTML feature files should be parsed.""" |
| 160 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 161 | + base_zip = Path(tmpdir) / "base.zip" |
| 162 | + curr_zip = Path(tmpdir) / "current.zip" |
| 163 | + |
| 164 | + with zipfile.ZipFile(base_zip, "w") as zf: |
| 165 | + zf.writestr("report.csv", sample_csv_content) |
| 166 | + zf.writestr("feature_test.html", sample_html_template_args) |
| 167 | + |
| 168 | + with zipfile.ZipFile(curr_zip, "w") as zf: |
| 169 | + zf.writestr("report.csv", sample_csv_content) |
| 170 | + zf.writestr("feature_test.html", sample_html_template_args) |
| 171 | + |
| 172 | + result = compare_reports(base_zip, curr_zip, as_json=True) |
| 173 | + assert result == 0 |
| 174 | + |
| 175 | + captured = capsys.readouterr() |
| 176 | + # Should include HTML features |
| 177 | + assert "HTML:feature_test" in captured.out |
0 commit comments