Skip to content

Commit bd60134

Browse files
committed
updated unit tests to better communicate outcome
1 parent 169c15a commit bd60134

8 files changed

Lines changed: 81 additions & 7 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ jobs:
1515
matrix:
1616
os:
1717
- ubuntu-latest
18-
- macos-latest
1918
steps:
2019
- name: Checkout
2120
uses: actions/checkout@v4

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ Test suite notes:
8282
- Tests live in `test/`.
8383
- External tools are mocked (Docker, Java/RMLStreamer, gzip, brotli, rdf2hdt).
8484
- GitHub Actions runs this suite on each push and pull request (`.github/workflows/tests.yml`).
85+
- A successful run prints `[PASS]` markers for each test and ends with `OK`.
86+
- See `test/README.md` for an example of successful output.
8587

8688

8789
## Manual Setup (Legacy)

‎test/README.md‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,31 @@ This repository uses `unittest` (Python standard library) to isolate orchestrati
2929
- It tests the highest-risk logic in this codebase: orchestration, branching, path wiring, and metrics consistency.
3030
- It catches regressions in command construction without requiring heavyweight dependencies.
3131
- It validates that metrics remain comparable across runs by enforcing one shared CSV schema.
32+
33+
## What a successful run looks like
34+
35+
Run:
36+
37+
```bash
38+
python -m unittest discover -s test -p "test_*_unit.py" -v
39+
```
40+
41+
Success indicators:
42+
43+
- Each test prints a clear marker:
44+
- `[TEST] <description>`
45+
- `[PASS] <description>`
46+
- `unittest` prints `ok` next to each test.
47+
- Final summary ends with:
48+
- `Ran <N> tests ...`
49+
- `OK`
50+
51+
Example (truncated):
52+
53+
```text
54+
[TEST] Wrapper runs all pipeline steps and forwards compression arguments.
55+
[PASS] Wrapper runs all pipeline steps and forwards compression arguments.
56+
...
57+
Ran 10 tests in 0.90s
58+
OK
59+
```

‎test/helpers.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import csv
22
import os
3+
import unittest
34
from pathlib import Path
45

56

@@ -82,3 +83,34 @@ def env_with_path(bin_dir: Path) -> dict[str, str]:
8283
env = os.environ.copy()
8384
env["PATH"] = f"{bin_dir}:{env['PATH']}"
8485
return env
86+
87+
88+
class VerboseTestCase(unittest.TestCase):
89+
"""Print explicit test start/end markers to make outcomes easy to scan."""
90+
91+
def run(self, result=None):
92+
label = self.shortDescription() or self.id().split(".")[-1]
93+
print(f"\n[TEST] {label}")
94+
95+
if result is None:
96+
result = self.defaultTestResult()
97+
98+
failures_before = len(result.failures)
99+
errors_before = len(result.errors)
100+
skips_before = len(result.skipped)
101+
unexpected_before = len(result.unexpectedSuccesses)
102+
103+
super().run(result)
104+
105+
failed = (len(result.failures) > failures_before) or (len(result.errors) > errors_before)
106+
failed = failed or (len(result.unexpectedSuccesses) > unexpected_before)
107+
skipped = len(result.skipped) > skips_before
108+
109+
if skipped:
110+
print(f"[SKIP] {label}")
111+
elif failed:
112+
print(f"[FAIL] {label}")
113+
else:
114+
print(f"[PASS] {label}")
115+
116+
return result

‎test/test_compression_unit.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import unittest
55
from pathlib import Path
66

7-
from test.helpers import env_with_path, make_executable, seed_conversion_metrics_row
7+
from test.helpers import VerboseTestCase, env_with_path, make_executable, seed_conversion_metrics_row
88

99

1010
REPO_ROOT = Path(__file__).resolve().parents[1]
@@ -48,8 +48,9 @@ def read_metrics_row(metrics_csv: Path, run_id: str, output_name: str):
4848
raise AssertionError(f"Metrics row not found for run_id={run_id}, output_name={output_name}")
4949

5050

51-
class CompressionUnitTests(unittest.TestCase):
51+
class CompressionUnitTests(VerboseTestCase):
5252
def test_compression_updates_existing_metrics_row_with_mocked_tools(self):
53+
"""Compression mode gzip|brotli|hdt updates existing metrics row and writes artifacts."""
5354
with tempfile.TemporaryDirectory() as td:
5455
tmp_path = Path(td)
5556
out_root = tmp_path / "out"
@@ -103,6 +104,7 @@ def test_compression_updates_existing_metrics_row_with_mocked_tools(self):
103104
self.assertGreater(int(row["combined_nq_size_bytes"]), 0)
104105

105106
def test_compression_none_updates_metrics_without_generating_outputs(self):
107+
"""Compression mode none leaves no compressed artifacts and records zero sizes."""
106108
with tempfile.TemporaryDirectory() as td:
107109
tmp_path = Path(td)
108110
out_root = tmp_path / "out"

‎test/test_run_conversion_unit.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
import unittest
55
from pathlib import Path
66

7-
from test.helpers import METRICS_HEADER, env_with_path, make_executable
7+
from test.helpers import METRICS_HEADER, VerboseTestCase, env_with_path, make_executable
88

99

1010
REPO_ROOT = Path(__file__).resolve().parents[1]
1111
SCRIPT = REPO_ROOT / "src" / "run_conversion.sh"
1212

1313

14-
class RunConversionUnitTests(unittest.TestCase):
14+
class RunConversionUnitTests(VerboseTestCase):
1515
def test_run_conversion_writes_nq_and_metrics_without_real_java(self):
16+
"""Conversion script writes .nq output and unified metrics using mocked Java."""
1617
with tempfile.TemporaryDirectory() as td:
1718
tmp_path = Path(td)
1819
fake_bin = tmp_path / "bin"

‎test/test_vcf_as_tsv_unit.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import unittest
55
from pathlib import Path
66

7+
from test.helpers import VerboseTestCase
8+
79

810
REPO_ROOT = Path(__file__).resolve().parents[1]
911
SCRIPT = REPO_ROOT / "src" / "vcf_as_tsv.sh"
1012

1113

12-
class VcfAsTsvUnitTests(unittest.TestCase):
14+
class VcfAsTsvUnitTests(VerboseTestCase):
1315
def test_vcf_as_tsv_directory_mode(self):
16+
"""Directory input: converts VCF and normalizes #CHROM header to CHROM."""
1417
with tempfile.TemporaryDirectory() as td:
1518
tmp_path = Path(td)
1619
input_dir = tmp_path / "in"
@@ -34,6 +37,7 @@ def test_vcf_as_tsv_directory_mode(self):
3437
self.assertEqual(lines[1], "1\t10\trs1")
3538

3639
def test_vcf_as_tsv_single_gz_file_mode(self):
40+
"""Single .vcf.gz input: decompresses and writes expected TSV output."""
3741
with tempfile.TemporaryDirectory() as td:
3842
tmp_path = Path(td)
3943
input_file = tmp_path / "sample.vcf.gz"
@@ -53,6 +57,7 @@ def test_vcf_as_tsv_single_gz_file_mode(self):
5357
self.assertIn("CHROM\tPOS", out_file.read_text())
5458

5559
def test_vcf_as_tsv_errors_when_no_vcf_files_found(self):
60+
"""Empty directory input: exits non-zero with a clear no-files message."""
5661
with tempfile.TemporaryDirectory() as td:
5762
tmp_path = Path(td)
5863
input_dir = tmp_path / "empty"

‎test/test_vcf_rdfizer_unit.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from unittest import mock
77

88
import vcf_rdfizer
9+
from test.helpers import VerboseTestCase
910

1011

1112
def invoke_main(argv):
@@ -22,8 +23,9 @@ def prepare_inputs(base: Path):
2223
return input_dir, rules_path
2324

2425

25-
class WrapperUnitTests(unittest.TestCase):
26+
class WrapperUnitTests(VerboseTestCase):
2627
def test_main_happy_path_runs_pipeline_and_passes_compression(self):
28+
"""Wrapper runs all pipeline steps and forwards compression arguments."""
2729
with tempfile.TemporaryDirectory() as td:
2830
tmp_path = Path(td)
2931
input_dir, rules_path = prepare_inputs(tmp_path)
@@ -67,6 +69,7 @@ def fake_run(cmd, cwd=None, env=None):
6769
self.assertEqual(commands[2][-2:], ["-m", "none"])
6870

6971
def test_main_errors_when_versioned_image_does_not_exist(self):
72+
"""Wrapper returns a user error when a requested image version cannot be pulled."""
7073
with tempfile.TemporaryDirectory() as td:
7174
tmp_path = Path(td)
7275
input_dir, rules_path = prepare_inputs(tmp_path)
@@ -99,6 +102,7 @@ def test_main_errors_when_versioned_image_does_not_exist(self):
99102
self.assertEqual(rc, 2)
100103

101104
def test_main_no_build_fails_if_local_image_missing(self):
105+
"""Wrapper fails fast with --no-build when no local image is available."""
102106
with tempfile.TemporaryDirectory() as td:
103107
tmp_path = Path(td)
104108
input_dir, rules_path = prepare_inputs(tmp_path)
@@ -126,6 +130,7 @@ def test_main_no_build_fails_if_local_image_missing(self):
126130
self.assertEqual(rc, 2)
127131

128132
def test_resolve_image_ref_accepts_repo_plus_version(self):
133+
"""Image repository and explicit version resolve to a tagged image reference."""
129134
ref, requested = vcf_rdfizer.resolve_image_ref("vcf-rdfizer", "1.2.3")
130135
self.assertEqual(ref, "vcf-rdfizer:1.2.3")
131136
self.assertTrue(requested)

0 commit comments

Comments
 (0)