from __future__ import annotations from datetime import UTC, datetime import hashlib from pathlib import Path import pytest from govoplan_xrechnung.backend.manifest import get_manifest from govoplan_xrechnung.backend.validation import ( InboundInvoice, KoSITValidationProfile, XRechnungValidationError, configuration_tree_sha256, create_validated_handoff, interpret_kosit_result, verify_profile, ) def _profile(tmp_path: Path, *, minimum_steps: int = 2) -> KoSITValidationProfile: java = tmp_path / "java" java.write_bytes(b"#!/bin/sh\n") java.chmod(0o700) jar = tmp_path / "validator.jar" jar.write_bytes(b"pinned validator") config = tmp_path / "config" config.mkdir() scenarios = config / "scenarios.xml" scenarios.write_text("", encoding="utf-8") resources = config / "resources" resources.mkdir() (resources / "rules.xsl").write_text("", encoding="utf-8") return KoSITValidationProfile( profile_id="xrechnung-explicit-test", xrechnung_version="explicit-test-only", validator_version="validator-test", configuration_version="configuration-test", java_executable=java.resolve(), validator_jar=jar.resolve(), validator_jar_sha256=hashlib.sha256(jar.read_bytes()).hexdigest(), configuration_root=config.resolve(), configuration_tree_sha256=configuration_tree_sha256(config), scenarios_file=scenarios.resolve(), minimum_validation_steps=minimum_steps, ) def _report(*, valid: bool, step_count: int = 2) -> bytes: assessment = "accept" if valid else "reject" flag = "true" if valid else "false" steps = "".join( f'' for index in range(step_count) ) return ( f'' f"{steps}" f"" "" ).encode() def _invoice() -> InboundInvoice: return InboundInvoice( tenant_id="tenant-a", source_reference="mail:message-1:attachment-1", document=b"", received_at=datetime(2026, 8, 23, tzinfo=UTC), ) def test_profile_verifies_exact_engine_and_complete_configuration_tree(tmp_path: Path) -> None: profile = _profile(tmp_path) first = verify_profile(profile) assert len(first) == 64 (profile.configuration_root / "resources" / "rules.xsl").write_text("changed", encoding="utf-8") with pytest.raises(XRechnungValidationError, match="tree digest"): verify_profile(profile) def test_complete_valid_report_can_create_digest_bound_handoff(tmp_path: Path) -> None: profile = _profile(tmp_path) invoice = _invoice() result = interpret_kosit_result( profile=profile, profile_sha256=verify_profile(profile), document_sha256=invoice.document_sha256, return_code=0, runner_output=b"INFO validation completed", report=_report(valid=True), ) handoff = create_validated_handoff(invoice, result) assert result.technical_outcome == "complete" assert result.conformance == "valid" assert result.assessment == "accept" assert handoff.document_sha256 == invoice.document_sha256 assert len(handoff.handoff_sha256) == 64 def test_semantically_invalid_report_is_complete_but_cannot_handoff(tmp_path: Path) -> None: profile = _profile(tmp_path) invoice = _invoice() result = interpret_kosit_result( profile=profile, profile_sha256=verify_profile(profile), document_sha256=invoice.document_sha256, return_code=0, runner_output=b"INFO validation completed", report=_report(valid=False), ) assert result.technical_outcome == "complete" assert result.conformance == "invalid" assert result.assessment == "reject" with pytest.raises(XRechnungValidationError, match="technically complete"): create_validated_handoff(invoice, result) @pytest.mark.parametrize( ("return_code", "output", "report", "expected"), [ (1, b"", _report(valid=True), "failed"), (0, b"ERROR Transformation failed", _report(valid=True), "incomplete"), (0, b"", _report(valid=True, step_count=1), "incomplete"), (0, b"", None, "incomplete"), ], ) def test_technical_failures_never_trust_a_valid_looking_report( tmp_path: Path, return_code: int, output: bytes, report: bytes | None, expected: str, ) -> None: profile = _profile(tmp_path) result = interpret_kosit_result( profile=profile, profile_sha256=verify_profile(profile), document_sha256="a" * 64, return_code=return_code, runner_output=output, report=report, ) assert result.technical_outcome == expected assert result.conformance == "unknown" assert result.assessment == "unknown" assert result.handoff_allowed is False def test_manifest_does_not_select_an_active_standard_version() -> None: manifest = get_manifest() assert manifest.version == "0.1.20" assert "none is activated by default" in manifest.architecture.known_limits[0].lower()