adobe-to-docusign-migrator/tests/test_validation_service.py

182 lines
6.5 KiB
Python

"""
Tests for Phase 9: validation service.
"""
import pytest
from src.models.normalized_template import (
NormalizedDocument,
NormalizedField,
NormalizedRole,
NormalizedTemplate,
)
from src.services.validation_service import (
ValidationResult,
compare_field_counts,
validate_template,
)
from src.reports.report_builder import (
MigrationReport,
MigrationStatus,
build_blocked_report,
build_error_report,
build_skipped_report,
build_success_report,
)
def _make_template(**kwargs) -> NormalizedTemplate:
defaults = dict(
name="Test Template",
roles=[NormalizedRole(name="Signer 1", order=1)],
fields=[
NormalizedField(
type="signature", label="sig1", page=1,
x=100, y=500, width=140, height=28,
role_name="Signer 1",
)
],
documents=[NormalizedDocument(name="test.pdf", checksum_sha256="abc", source_path="/fake.pdf")],
)
defaults.update(kwargs)
return NormalizedTemplate(**defaults)
class TestValidationService:
def test_valid_template_passes(self):
t = _make_template()
result = validate_template(t)
assert result.is_ok()
assert result.blockers == []
def test_no_recipients_is_blocker(self):
t = _make_template(roles=[])
result = validate_template(t)
assert result.has_blockers()
assert any("recipient" in b.lower() or "role" in b.lower() for b in result.blockers)
def test_no_documents_is_blocker(self):
t = _make_template(documents=[])
result = validate_template(t)
assert result.has_blockers()
assert any("document" in b.lower() for b in result.blockers)
def test_no_fields_is_warning(self):
t = _make_template(fields=[])
result = validate_template(t)
assert result.is_ok() # not a blocker
assert any("0 field" in w or "empty" in w.lower() for w in result.warnings)
def test_no_signature_field_is_warning(self):
t = _make_template(fields=[
NormalizedField(type="text", label="name", page=1, x=0, y=0, width=120, height=24, role_name="Signer 1")
])
result = validate_template(t)
assert result.is_ok()
assert any("signature" in w.lower() for w in result.warnings)
def test_field_with_unknown_role_is_warning(self):
t = _make_template(fields=[
NormalizedField(
type="signature", label="sig1", page=1, x=0, y=0,
width=140, height=28, role_name="NonExistentRole"
)
])
result = validate_template(t)
assert result.is_ok()
assert any("role" in w.lower() or "assign" in w.lower() for w in result.warnings)
def test_unsupported_features_become_warnings(self):
t = _make_template(unsupported_features=["Conditional HIDE action", "Webhook associations"])
result = validate_template(t)
assert result.is_ok()
assert len([w for w in result.warnings if "Unsupported" in w or "manual" in w.lower()]) >= 2
def test_validation_result_all_issues(self):
r = ValidationResult(blockers=["blocker1"], warnings=["warn1"])
issues = r.all_issues()
assert any("BLOCKER" in i for i in issues)
assert any("WARNING" in i for i in issues)
class TestCompareFieldCounts:
def test_matching_counts_no_warnings(self):
t = _make_template(fields=[
NormalizedField(type="signature", label="sig1", page=1, x=0, y=0, width=140, height=28, role_name="Signer 1")
])
ds = {
"recipients": {
"signers": [{"tabs": {"signHereTabs": [{"tabLabel": "sig1"}]}}]
}
}
result = compare_field_counts(t, ds)
assert result.is_ok()
def test_mismatched_counts_warns(self):
t = _make_template(fields=[
NormalizedField(type="signature", label="s1", page=1, x=0, y=0, width=140, height=28, role_name="Signer 1"),
NormalizedField(type="text", label="t1", page=1, x=0, y=50, width=120, height=24, role_name="Signer 1"),
])
ds = {"recipients": {"signers": [{"tabs": {"signHereTabs": [{}]}}]}}
result = compare_field_counts(t, ds)
assert any("mismatch" in w.lower() or "count" in w.lower() for w in result.warnings)
def test_zero_tabs_with_fields_warns(self):
t = _make_template()
ds = {"recipients": {"signers": []}}
result = compare_field_counts(t, ds)
assert result.warnings # should warn about 0 tabs
class TestReportBuilder:
def test_success_report(self):
r = build_success_report("My Template", "src_001", "ds_001", warnings=[])
assert r.status == MigrationStatus.SUCCESS
assert r.docusign_template_id == "ds_001"
def test_success_with_warnings(self):
r = build_success_report("My Template", "src_001", "ds_001", warnings=["some warning"])
assert r.status == MigrationStatus.SUCCESS_WITH_WARNINGS
def test_blocked_report(self):
r = build_blocked_report("T", "id1", blockers=["no docs"], warnings=[])
assert r.status == MigrationStatus.BLOCKED
assert r.blockers == ["no docs"]
def test_error_report(self):
r = build_error_report("T", "id1", error="Connection refused")
assert r.status == MigrationStatus.ERROR
assert "Connection" in r.error
def test_skipped_report(self):
r = build_skipped_report("T", "id1", reason="already migrated")
assert r.status == MigrationStatus.SKIPPED
def test_migration_report_summary(self):
report = MigrationReport()
report.add(build_success_report("T1", "1", "ds1", []))
report.add(build_success_report("T2", "2", "ds2", ["warn"]))
report.add(build_error_report("T3", "3", "fail"))
summary = report.summary()
assert summary["total"] == 3
assert summary.get("success", 0) == 1
assert summary.get("error", 0) == 1
def test_report_to_dict(self):
report = MigrationReport()
report.add(build_success_report("T1", "1", "ds1", []))
d = report.to_dict()
assert "summary" in d
assert "templates" in d
assert d["templates"][0]["template_name"] == "T1"
def test_report_has_errors(self):
report = MigrationReport()
report.add(build_error_report("T", "1", "err"))
assert report.has_errors()
def test_report_no_errors(self):
report = MigrationReport()
report.add(build_success_report("T", "1", "ds1", []))
assert not report.has_errors()