"""Opt in: JR_EINVOICE_INTEGRATION=1 python -m unittest discover -s tests -v.""" import os import io import json from unittest.mock import patch import logging import tempfile import unittest from pathlib import Path from email import policy from email.parser import BytesParser import xml.etree.ElementTree as ET import yaml from jr_einvoice.pipeline import build_in_folder from jr_einvoice.delivery import prepare_email from jr_einvoice.mustang import validate, MustangError from jr_einvoice.facturx import NS ROOT = Path(__file__).resolve().parent.parent JAR = Path(os.environ.get('JR_EINVOICE_MUSTANG_JAR', ROOT / 'tools/mustang/Mustang-CLI.jar')) LOGGER = logging.getLogger('integration') @unittest.skipUnless(os.environ.get('JR_EINVOICE_INTEGRATION') == '1', 'Opt in with JR_EINVOICE_INTEGRATION=1') class MustangIntegrationTest(unittest.TestCase): def test_build_email_and_reject_bad_xml_and_plain_pdf(self): with tempfile.TemporaryDirectory() as folder: root = Path(folder) data = yaml.safe_load((ROOT / 'examples/invoice.sample.yaml').read_text()) data['buyer']['email'] = 'rechnung@example.invalid' data['items'][1]['tax_rate'] = 7 (root / 'invoice.yaml').write_text(yaml.safe_dump(data)) result = build_in_folder(root, 'out', ROOT / 'templates/invoice.html.j2', LOGGER, JAR, True, True) self.assertIn('status="valid"', result.validate_report.read_text()) path = prepare_email(root, JAR, LOGGER) message = BytesParser(policy=policy.default).parsebytes(path.read_bytes()) self.assertEqual(message['From'], data['seller']['email']) self.assertEqual(message['To'], 'rechnung@example.invalid') attachments = list(message.iter_attachments()) self.assertEqual(len(attachments), 1) self.assertEqual(attachments[0].get_payload(decode=True), result.facturx_pdf.read_bytes()) self.assertTrue((root / 'out/manifest.json').is_file()) # A valid PDF/A without invoice XML must not be an accepted e-invoice. with self.assertRaises(MustangError): validate(JAR, result.invoice_pdf, root / 'plain-report.xml', LOGGER) xml = ET.parse(result.facturx_xml) xml.getroot().find('.//ram:GrandTotalAmount', NS).text = '0.01' xml.write(root / 'broken.xml', encoding='utf-8', xml_declaration=True) with self.assertRaises(MustangError): validate(JAR, root / 'broken.xml', root / 'bad-report.xml', LOGGER) self.assertTrue((root / 'bad-report.xml').is_file()) def test_one_call_new_existing_customer_and_rejected_invalid_unit(self): from jr_einvoice.quick_cli import main with tempfile.TemporaryDirectory() as folder: root = Path(folder) presets = root / 'presets' presets.mkdir() sample = yaml.safe_load((ROOT / 'examples/invoice.sample.yaml').read_text()) (presets / 'seller.default.yaml').write_text(yaml.safe_dump({ 'seller': sample['seller'], 'payment': sample['payment']})) base = ['--create', '--Customer', 'Beliebiger neuer / alter Kunde GmbH', '--Entry', 'Geleistete Stunden Oktober 2026', '--Volume', '120', '--Price', '85,00 €', '--Date', '2026-10-31', '--root', str(root / 'invoices'), '--mustang-jar', str(JAR), '--json'] with patch.dict(os.environ, {'JR_EINVOICE_PRESETS': str(presets)}): for expected, extra in [('JR-2026-0001', ['--Street', 'Testweg 1', '--PostalCode', '10115', '--City', 'Berlin']), ('JR-2026-0002', [])]: with patch('sys.stdout', new_callable=io.StringIO) as output: main(base + extra) result = json.loads(output.getvalue()) self.assertEqual(result['invoice_number'], expected) self.assertEqual(result['technical_validation'], 'valid') self.assertTrue(Path(result['pdf']).is_file()) xml = ET.parse(Path(result['invoice_dir']) / 'out/factur-x.xml') self.assertEqual(xml.getroot().findtext('.//ram:GrandTotalAmount', namespaces=NS), '12138.00') with patch('sys.stdout', new_callable=io.StringIO) as output, patch('sys.stderr', new_callable=io.StringIO): with self.assertRaises(SystemExit) as failure: main(base + ['--Unit', 'ZZZ']) self.assertEqual(failure.exception.code, 1) self.assertNotIn('technical_validation', output.getvalue()) failed = next((root / 'invoices').glob('*JR-2026-0003*')) self.assertFalse((failed / 'Rechnung_JR-2026-0003.pdf').exists()) self.assertTrue((failed / 'out/validation/validate_report.xml').exists())