Skip to content

[1.8] AAA test scaffolding for e-invoice XML generators #22

Description

@nielsdrost7

Context

XML template generators depend on a full invoice data array, client data, and line items. Without shared test infrastructure, every developer writing or testing a template builds different fixture data from scratch — tests become inconsistent, hard to maintain, and cover different edge cases.

This issue delivers the foundation that all e-invoice template tests build on. All issues #16#21 depend on this scaffolding being in place.

Repository status (develop branch):

application/libraries/XMLtemplates/BaseXml.php already exists as the base class for all XML generators. All generator classes in the e-invoices repo extend it. The scaffolding and any new generators (see #18) must also extend BaseXml. Read BaseXml.php to understand the generator interface (xml() method signature, available helper methods) before building the scaffolding.


Deliverables

1. Invoice fixture builder

tests/helpers/EInvoiceTestFixture.php

class EInvoiceTestFixture
{
    // B2B invoice: two line items, configurable tax rate (default 21%)
    public static function standardInvoice(array $overrides = []): array;

    // Intra-EU supply: one line at 0% VAT, exemption reason set
    public static function zeroTaxInvoice(array $overrides = []): array;

    // Credit note: negative amounts, reference to original invoice number
    public static function creditNote(string $originalInvoiceNumber, array $overrides = []): array;

    // B2G invoice: pre-populated with three routing codes
    public static function b2gInvoice(array $routingCodes = [], array $overrides = []): array;

    // B2C invoice: individual recipient, no company name
    public static function b2cInvoice(array $overrides = []): array;

    // Invoice with line-level discount
    public static function discountedInvoice(array $overrides = []): array;
}

2. Client fixture builder

tests/helpers/EInvoiceClientFixture.php

class EInvoiceClientFixture
{
    public static function belgian(array $overrides = []): array;        // VAT BE0123456789, Peppol 9925:0123456789
    public static function german(array $overrides = []): array;         // VAT DE123456789
    public static function french(array $overrides = []): array;         // VAT FR12345678901
    public static function italian(array $overrides = []): array;        // VAT IT12345678901, delivery code XJ5ETH7
    public static function italianPEC(array $overrides = []): array;     // delivery code 0000000, PEC set
    public static function italianIndividual(array $overrides = []): array; // no VAT, fiscal code set
    public static function spanish(array $overrides = []): array;        // VAT ESB12345678
    public static function czech(array $overrides = []): array;          // VAT CZ12345678, company number 12345678
    public static function swedish(array $overrides = []): array;        // VAT SE123456789001, GLN endpoint
    public static function norwegian(array $overrides = []): array;      // VAT NO974761076, endpoint 0192:974761076
}

3. Schema validator wrappers

tests/helpers/EInvoiceAssert.php

class EInvoiceAssert
{
    // Validates against UBL 2.1 Invoice schema + OpenPeppol schematron
    public static function validPeppol(string $xml, TestCase $test): void;

    // Validates against KoSIT XRechnung 3.0 schematron
    public static function validXRechnung(string $xml, TestCase $test): void;

    // Validates against ISDOC 6.0.1 XSD
    public static function validISDOC(string $xml, TestCase $test): void;

    // Validates against FatturaPA v1.2 XSD
    public static function validFatturaPA(string $xml, TestCase $test): void;

    // Validates against Facturae XSD (3.2 or 3.2.1 — match actual config version)
    public static function validFacturae(string $xml, TestCase $test): void;

    // Validates against Factur-X / ZUGFeRD CII schema
    public static function validZUGFeRD(string $xml, TestCase $test): void;

    // Asserts that no ds:Signature element is present (Facturae output must be unsigned)
    public static function noSignatureBlock(string $xml, TestCase $test): void;
}

Schema and schematron files are stored in tests/schemas/ — one subdirectory per standard. Include a README in that directory explaining where each schema file was sourced from and its version.

4. One complete example test

tests/einvoice/PeppolBISv30Test.php — demonstrates the AAA pattern using the scaffolding:

public function test_standard_b2b_invoice_is_valid_peppol(): void
{
    // Arrange
    $client  = EInvoiceClientFixture::belgian();
    $invoice = EInvoiceTestFixture::standardInvoice();

    // Act
    $generator = new Ublv24Xml();  // the actual generator class
    $xml = $generator->xml($invoice, $client, $invoice['items']);

    // Assert
    EInvoiceAssert::validPeppol($xml, $this);
    $this->assertStringContainsString('9925:0123456789', $xml);
}

5. Documentation

tests/TESTING.md — explains:

  • How to run the e-invoice test suite
  • How to write a new AAA test for an XML template
  • Where the schema files come from and how to update them
  • The BaseXml contract: method signature, what the invoice/client/items arrays contain
  • Required coverage: every template PR must include tests for standard invoice, credit note, and at least one error/validation scenario

Acceptance criteria

  • All fixture builders implemented with the methods listed above
  • EInvoiceAssert validator wrappers implemented for all five in-scope standards plus ZUGFeRD
  • Schema files present in tests/schemas/ with a sourcing README
  • Example Peppol test passes using Ublv24Xml (the actual generator class)
  • Test suite runs with the existing test runner without additional setup steps
  • TESTING.md written and covers all five points listed above, including BaseXml contract
  • All future e-invoice template PRs ([1.8] Main app: client model and controller — save and load e-invoicing fields #57[1.8] Implement Facturae 3.2.1 XML template #20) must use this scaffolding — enforced by code review

Delivery order

This issue should be completed before any of #16#20 are merged. Template PRs without AAA tests using this scaffolding should not be accepted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions