A parser that accepts one good XML report has passed only a narrow test. A useful validation suite checks what it extracts, what it rejects, how it handles supported variations and whether retries change totals. Treat expected normalized values as part of the fixture, not as an output the test is allowed to invent.
Declare the supported contract
References: RFC 9990 §3.1.1, RFC 9990 §3.2.
Record the parser, runtime, decompressor and schema versions. State which report formats and extensions are supported and how legacy inputs are handled. RFC 9990 defines the current aggregate format and extension model; support for older formats is a separate compatibility decision.
Build a minimal valid fixture first. Save its bytes, hash, expected metadata, interval, source IP, count, identities and aligned results. Do not consider successful XML parsing sufficient: verify field types and meanings. Keep missing optional values distinct from invalid required data.
Test the common semantic mistakes
References: RFC 9990 §3.1.1.9, RFC 9990 §3.1.1.11, RFC 9989 §5.3.5.
Include raw DKIM pass under an unrelated domain with an aligned DKIM failure, and raw SPF pass without alignment. Include a case where one aligned mechanism passes while the other fails. Check that the normalized result does not demand both mechanisms to pass or equate raw success with alignment.
Add reported disposition that differs from a simplistic inference from published policy. Preserve available override reasons. For the current schema, include the ActionDispositionType value pass rather than rejecting it as an unknown legacy value; RFC 9990 Appendix A defines it for passing DMARC with an enforcing policy. Keep legacy-format expectations explicitly separate. Test multiple DKIM results without dropping all but the first signature. The purpose is to preserve what the report says, not to fabricate a new receiver evaluation from incomplete data.
Exercise structural and security cases
References: RFC 9990 §3.2, RFC 9990 §8.1.
Reference: OWASP XML external-entity prevention, General Guidance.
Use invalid required values, malformed XML, supported namespaced extensions, unsupported versions, missing optional elements and excessive bounded nesting. Test compressed and expanded size limits, interrupted input and harmless external-reference sentinels in isolation. Define whether each input is accepted, quarantined or rejected and why.
A supported extension should not disappear silently if downstream analysis claims to preserve it. An unsupported construct should produce an explicit compatibility outcome rather than being reclassified as failed authentication. Keep security tests small and resource-bounded; do not generate destructive workloads merely to demonstrate a limit.
Verify duplicate and transaction behavior
References: RFC 9990 §3.5.4.
Import a fixture, replay it and restart the worker before replaying again. The counted report should remain single under the documented duplicate rule. Add a different attachment claiming the same report identity and require an explicit conflict decision. Identical IDs alone are not permission to discard different evidence without review.
Inject a controlled interruption before commit. Confirm that no partial records affect production metrics and that a permitted retry yields the expected result once. Test zero-observation outputs as unavailable rather than dividing by zero or manufacturing a perfect pass rate.
Work through an acceptance row
Synthetic fixture F1 contains 90 observations with aligned DKIM pass and ten with no aligned passing mechanism. Its expected included count is 100 and expected observed pass count is 90. Replaying F1 must leave both values unchanged. A deliberately truncated copy should be rejected without increasing either total.
Save the expected values before execution. Compare actual output, resource measurements and diagnostics with that baseline. This is a parser acceptance specification, not a claim that any commercial or tenant parser has passed it. A separate new offline arithmetic/replay model checks the 100/90 values and unchanged counts on repeated identity; it does not parse or validate an RFC 9990 XML report. If a case fails, retain the defect and exact fixture so a later revision can reproduce it.
Gate changes on recorded results
Keep a matrix containing fixture ID, hash, purpose, expected outcome, actual outcome, parser configuration and review decision. Require all mandatory cases to pass at the deployed revision, with documented exceptions for unsupported features. Rerun after dependency or normalization changes and compare historical metric effects before reprocessing stored reports. Parser validation should protect both system availability and the meaning of the numbers people use for policy decisions.