130 lines
4.5 KiB
Python
130 lines
4.5 KiB
Python
"""
|
|
tests/test_regression.py
|
|
------------------------
|
|
Regression tests for the compose pipeline.
|
|
|
|
For each downloaded template in downloads/, run compose_template() and
|
|
compare the output against the snapshot in tests/fixtures/expected/.
|
|
|
|
These tests require no live API calls. They verify that changes to the
|
|
compose pipeline don't silently break existing template conversions.
|
|
|
|
To update snapshots after an intentional change:
|
|
pytest tests/test_regression.py --update-snapshots
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
from compose_docusign_template import compose_template
|
|
|
|
DOWNLOADS_DIR = os.path.join(os.path.dirname(__file__), "..", "downloads")
|
|
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures", "expected")
|
|
|
|
# Templates with real downloaded data to test against
|
|
REGRESSION_TEMPLATES = [
|
|
"David Tag Demo Form__CBJCHBCA",
|
|
"_DEMO USE ONLY_ NDA__CBJCHBCA",
|
|
"Rob Test__CBJCHBCA",
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def update_snapshots(request):
|
|
return request.config.getoption("--update-snapshots", default=False)
|
|
|
|
|
|
@pytest.mark.parametrize("template_name", REGRESSION_TEMPLATES)
|
|
def test_compose_regression(template_name, update_snapshots):
|
|
"""
|
|
Compose output for each template must match the stored snapshot.
|
|
Run with --update-snapshots to regenerate.
|
|
"""
|
|
template_dir = os.path.join(DOWNLOADS_DIR, template_name)
|
|
if not os.path.isdir(template_dir):
|
|
pytest.skip(f"Downloaded template not found: {template_name}")
|
|
|
|
snapshot_path = os.path.join(FIXTURES_DIR, f"{template_name}.json")
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".json", delete=False, mode="w") as tf:
|
|
output_path = tf.name
|
|
|
|
try:
|
|
result, warnings, _ = compose_template(template_dir, output_path)
|
|
|
|
if update_snapshots:
|
|
os.makedirs(FIXTURES_DIR, exist_ok=True)
|
|
with open(snapshot_path, "w") as f:
|
|
json.dump(result, f, indent=2)
|
|
pytest.skip(f"Snapshot updated for {template_name}")
|
|
|
|
if not os.path.exists(snapshot_path):
|
|
pytest.fail(
|
|
f"No snapshot for '{template_name}'. "
|
|
f"Run with --update-snapshots to create it."
|
|
)
|
|
|
|
with open(snapshot_path) as f:
|
|
expected = json.load(f)
|
|
|
|
# Compare key structural properties
|
|
assert result.get("name") == expected.get("name"), \
|
|
f"Template name mismatch for {template_name}"
|
|
|
|
# Recipients
|
|
result_roles = sorted([r.get("roleName", "") for r in result.get("recipients", {}).get("signers", [])])
|
|
expected_roles = sorted([r.get("roleName", "") for r in expected.get("recipients", {}).get("signers", [])])
|
|
assert result_roles == expected_roles, \
|
|
f"Recipient roles changed for {template_name}: {result_roles} != {expected_roles}"
|
|
|
|
# Tab counts per type — must not regress
|
|
result_tabs = _count_tabs(result)
|
|
expected_tabs = _count_tabs(expected)
|
|
for tab_type, count in expected_tabs.items():
|
|
actual = result_tabs.get(tab_type, 0)
|
|
assert actual == count, (
|
|
f"Tab count regression in {template_name}: "
|
|
f"{tab_type} expected {count}, got {actual}"
|
|
)
|
|
|
|
finally:
|
|
if os.path.exists(output_path):
|
|
os.unlink(output_path)
|
|
|
|
|
|
def _count_tabs(template: dict) -> dict:
|
|
"""Count total tabs of each type across all signers."""
|
|
counts = {}
|
|
for signer in template.get("recipients", {}).get("signers", []):
|
|
tabs = signer.get("tabs", {})
|
|
for tab_type, items in tabs.items():
|
|
if isinstance(items, list):
|
|
counts[tab_type] = counts.get(tab_type, 0) + len(items)
|
|
return counts
|
|
|
|
|
|
def test_no_tabs_lost_on_recompose():
|
|
"""
|
|
Sanity check: every downloaded template must produce at least one tab.
|
|
Catches complete compose failures silently returning empty output.
|
|
"""
|
|
for template_name in REGRESSION_TEMPLATES:
|
|
template_dir = os.path.join(DOWNLOADS_DIR, template_name)
|
|
if not os.path.isdir(template_dir):
|
|
continue
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tf:
|
|
output_path = tf.name
|
|
try:
|
|
result, _, _issues = compose_template(template_dir, output_path)
|
|
total_tabs = sum(_count_tabs(result).values())
|
|
assert total_tabs > 0, f"No tabs produced for {template_name}"
|
|
finally:
|
|
if os.path.exists(output_path):
|
|
os.unlink(output_path)
|