182 lines
6.5 KiB
Python
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()
|