The Question
“Development is creating an implementation to create and consume an
InvoicePaymentReconciliationAdviceV01and respond with anInvoicePaymentReconciliationStatusV01. I’m the QA manager. Can you help me develop a test plan? I need to verify every kind of data issue imaginable since some of the messages originate elsewhere, and the appropriate response MUST conform to the spec before being sent or they are rejected.”
Not “write me some unit tests.” Not “check the happy path.” Every kind of data issue imaginable — because messages arriving from external parties can be anything. And outbound responses that fail schema validation are silently rejected by the network. This is a real QA problem, and it needs a real answer.
Step 1 — The MCP Server Identifies Both Messages
The AI queries the Beneficial Strategies ISO 20022 MCP Server for both message definitions. The server resolves them immediately:
| Message | ID | Business Area | Role |
|---|---|---|---|
InvoicePaymentReconciliationAdviceV01 |
tsmt.053.001.01 |
TSMT | Payer → Payee: attributes payments to invoice instalments |
InvoicePaymentReconciliationStatusV01 |
tsmt.054.001.01 |
TSMT | Payee → Payer: confirms or disputes the reconciliation |
The server then retrieves the full component hierarchy for both messages, including the key ReconciliationList1 building block and every required/optional field with its type, multiplicity, and constraints.
Step 2 — Two Clarifying Questions
Before generating the test plan, the AI asks:
“1. Should test cases cover XML-level failures (malformed XML, wrong namespace, schema violations) in addition to business-level data failures?”
“2. For the outbound
InvoicePaymentReconciliationStatusresponse — should test cases verify that it correctly references the originating Advice message, or just that it is internally valid?”
Answers: Yes to both. XML-level failures absolutely matter since messages come from external systems. And response traceability — that the Status correctly references the Advice — is a core conformance requirement.
Step 3 — The Complete Test Plan Is Generated Automatically
Message Structure Reference
classDiagram
direction LR
class InvoicePaymentReconciliationAdviceV01{
ReconciliationCount Max15NumericText
ItemCount Max15NumericText
ControlSum DecimalNumber
}
InvoicePaymentReconciliationAdviceV01 *-- "1..1" BusinessLetter1 : Header
InvoicePaymentReconciliationAdviceV01 *-- "1..*" ReconciliationList1 : ReconciliationList
class ReconciliationList1{
Date ISODate
ItemCount Max15NumericText
ControlSum DecimalNumber
PaymentDate ISODate
PaymentAmount CurrencyAndAmount
}
ReconciliationList1 *-- "1..1" QualifiedPartyIdentification1 : Recipient
ReconciliationList1 *-- "1..1" QualifiedPartyIdentification1 : Advisor
ReconciliationList1 *-- "1..1" PaymentIdentification1 : PaymentReference
ReconciliationList1 *-- "1..1" PaymentMeans1 : PaymentMeans
ReconciliationList1 *-- "1..1" PaymentTerms6 : PaymentTerms
ReconciliationList1 *-- "1..*" FinancialItem1 : Item
class InvoicePaymentReconciliationStatusV01{
ReconciliationCount Max15NumericText
ItemCount Max15NumericText
ControlSum DecimalNumber
}
InvoicePaymentReconciliationStatusV01 *-- "1..1" BusinessLetter1 : Header
InvoicePaymentReconciliationStatusV01 *-- "1..*" ReconciliationList1 : ReconciliationList
Category 1 — XML Structure and Namespace Failures
These failures arrive before any business logic runs. Every inbound message must survive this gauntlet first.
| # | Test Case | Input Condition | Expected Behaviour |
|---|---|---|---|
| 1.1 | Malformed XML | Missing closing tag | Parser exception; message rejected before processing |
| 1.2 | Wrong namespace | xmlns="urn:iso:std:iso:20022:tech:xsd:tsin.008.001.01" (wrong message type) |
Schema validation failure; reject with namespace mismatch error |
| 1.3 | Missing namespace | No xmlns attribute on <Document> |
Schema validation failure |
| 1.4 | Root element wrong | <InvcAssgnmtNtfctn> instead of <InvcPmtRcncltnAdvc> |
Schema validation failure |
| 1.5 | Empty document | <Document/> only |
Required building blocks missing; reject |
| 1.6 | Encoding mismatch | UTF-16 file declared as UTF-8 | Parser exception |
| 1.7 | BOM present | Byte-order mark before XML declaration | Verify parser handles or rejects gracefully |
| 1.8 | Extra elements | Unknown element inside <Hdr> |
Schema validation failure (strict mode) |
Category 2 — Header (BusinessLetter1) Failures
The Hdr building block is required (1..1). Every field failure here must produce a clear rejection.
| # | Test Case | Input Condition | Expected Behaviour |
|---|---|---|---|
| 2.1 | Missing Header entirely | No <Hdr> element |
Schema: missing required element |
| 2.2 | Missing LetterIdentifier | No <LttrIdr> inside Header |
Schema: required field absent |
| 2.3 | Missing Date | No <Dt> in Header |
Schema: required field absent |
| 2.4 | Date invalid format | <Dt>2026-13-45</Dt> |
Schema: date pattern violation |
| 2.5 | Date as free text | <Dt>March 17 2026</Dt> |
Schema: type mismatch |
| 2.6 | Missing Originator | No <Orgtr> party |
Schema: required party absent |
| 2.7 | Missing PrimaryRecipient | No <PmryRcpt> party |
Schema: required party absent |
| 2.8 | Party with no Name or ID | <Pty></Pty> |
Schema: empty party element |
| 2.9 | BIC malformed | <AnyBIC>NOT-A-BIC</AnyBIC> |
Schema: BIC pattern violation ([A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?) |
Category 3 — ReconciliationList (ReconciliationList1) Failures
The RcncltnList building block is required (1..*) — at least one must be present.
| # | Test Case | Input Condition | Expected Behaviour |
|---|---|---|---|
| 3.1 | No ReconciliationList | Missing <RcncltnList> entirely |
Schema: required element absent |
| 3.2 | Missing Date | No <Dt> in list |
Schema: required field absent |
| 3.3 | Future date | <Dt>2099-01-01</Dt> |
Business rule: date must not be future (if applicable) |
| 3.4 | Missing Recipient | No <Rcpt> |
Schema: required party absent |
| 3.5 | Missing Advisor | No <Advsr> |
Schema: required party absent |
| 3.6 | Missing PaymentReference | No <PmtRef> |
Schema: required element absent |
| 3.7 | Missing PaymentMeans | No <PmtMeans> |
Schema: required element absent |
| 3.8 | Missing PaymentDate | No <PmtDt> |
Schema: required element absent |
| 3.9 | PaymentDate before invoice date | Payment precedes the invoice it references | Business rule: chronological violation |
| 3.10 | Missing PaymentTerms | No <PmtTerms> |
Schema: required element absent |
| 3.11 | Missing PaymentAmount | No <PmtAmt> |
Schema: required element absent |
| 3.12 | PaymentAmount zero | <PmtAmt Ccy="USD">0.00</PmtAmt> |
Business rule: zero-value payment |
| 3.13 | PaymentAmount negative | <PmtAmt Ccy="USD">-100.00</PmtAmt> |
Schema or business rule: negative amount |
| 3.14 | Missing currency attribute | <PmtAmt>4750.00</PmtAmt> |
Schema: required attribute Ccy absent |
| 3.15 | Invalid currency code | <PmtAmt Ccy="XXX">4750.00</PmtAmt> |
Business rule: non-ISO-4217 currency |
| 3.16 | No Item elements | <ItmCnt>0</ItmCnt> with no <Itm> |
Schema: 1..* minimum violation |
| 3.17 | ItemCount mismatch | <ItmCnt>3</ItmCnt> but only 1 <Itm> present |
Business rule: count integrity failure |
| 3.18 | ControlSum mismatch | <CtrlSum>999.99</CtrlSum> but items sum to different value |
Business rule: control sum integrity failure |
| 3.19 | ControlSum correct | Amounts sum exactly to ControlSum | Pass |
Category 4 — FinancialItem (FinancialItem1) Failures
Each <Itm> inside the reconciliation list represents one invoice being matched to a payment.
| # | Test Case | Input Condition | Expected Behaviour |
|---|---|---|---|
| 4.1 | Missing ItemContext | No <ItmCntxt> |
Schema: required element absent |
| 4.2 | Missing CreditDebitIndicator | No <CdtDbtInd> |
Schema: required element absent |
| 4.3 | Invalid CreditDebitIndicator | <CdtDbtInd>MAYBE</CdtDbtInd> |
Schema: enum violation (only CRDT or DBIT) |
| 4.4 | Missing TotalAmount | No <TtlAmt> block |
Schema: required element absent |
| 4.5 | TaxAmount exceeds TotalTaxableAmount | Tax > pretax amount | Business rule: nonsensical tax calculation |
| 4.6 | TotalAmount inconsistent | TtlTaxblAmt + TaxAmt ≠ TtlAmt |
Business rule: amount arithmetic failure |
| 4.7 | Currency mismatch within item | TtlTaxblAmt Ccy="USD" but TaxAmt Ccy="EUR" |
Business rule: mixed currencies within single item |
| 4.8 | Currency mismatch with payment | Item currency differs from PaymentAmount currency |
Business rule: currency mismatch across list |
| 4.9 | DueAmount exceeds TotalAmount | More owed than total invoiced | Business rule: amount logic failure |
| 4.10 | Reference to unknown invoice | FinancialDocumentReference ID not in system | Business rule: unresolvable reference |
| 4.11 | Duplicate item reference | Same invoice referenced twice in one list | Business rule: duplicate item |
Category 5 — Envelope-Level Integrity Failures
These cross-reference the building blocks against each other.
| # | Test Case | Input Condition | Expected Behaviour |
|---|---|---|---|
| 5.1 | Top-level ItemCount mismatch | <ItmCnt>5</ItmCnt> but total items across all lists ≠ 5 |
Business rule: envelope count integrity |
| 5.2 | Top-level ControlSum mismatch | <CtrlSum> doesn’t match sum of all list ControlSums |
Business rule: envelope sum integrity |
| 5.3 | ReconciliationCount mismatch | <RcncltnCnt>2</RcncltnCnt> but only 1 list present |
Business rule: count integrity |
| 5.4 | Multiple lists, one invalid | First list valid, second list missing required field | Partial failure handling: reject whole message or list-level fault? |
| 5.5 | Duplicate list identifiers | Two <RcncltnList> elements with same <Idr> |
Business rule: uniqueness violation |
Category 6 — Outbound Response Conformance (tsmt.054.001.01)
The InvoicePaymentReconciliationStatus must be schema-valid before transmission. These tests verify the implementation produces a conformant response.
| # | Test Case | Trigger Condition | Verify in Response |
|---|---|---|---|
| 6.1 | Happy path acknowledgement | Valid Advice received and accepted | Response namespace = urn:iso:std:iso:20022:tech:xsd:tsmt.054.001.01; passes XSD |
| 6.2 | Response references original | Valid Advice received | RelatedDocument in response Header references the original Advice LetterIdentifier |
| 6.3 | Disputed item | One item amount doesn’t match expected | ValidationStatusInformation present with appropriate status code on disputed item |
| 6.4 | Fully disputed | All items disputed | All items carry dispute status; not silently accepted |
| 6.5 | Response ItemCount accurate | 3 items in Advice | Response <ItmCnt> = 3 |
| 6.6 | Response ControlSum accurate | Items sum to X | Response <CtrlSum> matches |
| 6.7 | Response sent to correct party | Advice has Advisor A and Recipient B | Response <PmryRcpt> is Advisor A (the original sender) |
| 6.8 | Response within same currency | Advice used USD | Response amounts all in USD |
| 6.9 | Response with wrong namespace | Bug in serializer | Must NOT produce tsin.008 or any other namespace |
| 6.10 | Response schema validation | Any response generated | Run against tsmt.054.001.01.xsd before transmission; failure = do not send |
Category 7 — Edge Cases and Boundary Conditions
| # | Test Case | Input Condition | Expected Behaviour |
|---|---|---|---|
| 7.1 | Maximum field lengths | All Max35Text fields at exactly 35 chars |
Accept |
| 7.2 | Exceed field length | Max35Text field with 36 chars |
Schema: maxLength violation |
| 7.3 | Empty string in required text | <Id></Id> |
Schema: minLength violation (usually 1) |
| 7.4 | Whitespace-only text | <Id> </Id> |
Schema or business rule: blank value |
| 7.5 | Unicode in party name | <Nm>株式会社</Nm> |
Accept (ISO 20022 is UTF-8) |
| 7.6 | Special XML characters unescaped | <Nm>AT&T</Nm> |
Parser failure; must be & |
| 7.7 | Single item, single list | Minimum valid message | Accept; ItemCount = 1, ControlSum = item amount |
| 7.8 | Large message | 1000 items across 50 lists | Performance: no timeout, correct count/sum |
| 7.9 | Payment amount precision | <PmtAmt Ccy="JPY">4750.999</PmtAmt> |
Business rule: JPY has 0 decimal places |
| 7.10 | Zero control sum with items | <CtrlSum>0</CtrlSum> but items have amounts |
Business rule: integrity failure |
C# Test Harness Skeleton
using System.Xml;
using System.Xml.Schema;
namespace Iso20022.Tsmt.Tests
{
public class ReconciliationAdviceValidationTests
{
private const string AdviceNamespace =
"urn:iso:std:iso:20022:tech:xsd:tsmt.053.001.01";
private const string StatusNamespace =
"urn:iso:std:iso:20022:tech:xsd:tsmt.054.001.01";
// Load schema once for the test suite
private static XmlSchemaSet LoadSchema(string xsdPath)
{
var schemas = new XmlSchemaSet();
schemas.Add(null, xsdPath);
schemas.Compile();
return schemas;
}
// Validate any XML string against a compiled schema
private static List<string> ValidateXml(string xml, XmlSchemaSet schemas)
{
var errors = new List<string>();
var settings = new XmlReaderSettings { ValidationType = ValidationType.Schema };
settings.Schemas = schemas;
settings.ValidationEventHandler += (_, e) => errors.Add(e.Message);
using var reader = XmlReader.Create(new StringReader(xml), settings);
while (reader.Read()) { }
return errors;
}
// --- Category 1: XML Structure ---
[Fact]
public void MalformedXml_ShouldThrowParserException()
{
var xml = "<Document><InvcPmtRcncltnAdvc></Document>"; // unclosed element
Assert.Throws<XmlException>(() =>
XmlReader.Create(new StringReader(xml)).Read());
}
[Fact]
public void WrongNamespace_ShouldFailValidation()
{
var xml = WrapInDocument(
"urn:iso:std:iso:20022:tech:xsd:tsin.008.001.01",
"<InvcAssgnmtNtfctn/>");
var errors = ValidateXml(xml, LoadSchema("tsmt.053.001.01.xsd"));
Assert.NotEmpty(errors);
}
// --- Category 3: ReconciliationList ---
[Fact]
public void ItemCountMismatch_ShouldFailBusinessRule()
{
var msg = BuildAdvice(declaredItemCount: 3, actualItems: 1);
var result = MessageValidator.Validate(msg);
Assert.Contains(result.Errors, e => e.Code == "ITEM_COUNT_MISMATCH");
}
[Fact]
public void ControlSumMismatch_ShouldFailBusinessRule()
{
var msg = BuildAdvice(declaredControlSum: 999.99m, actualSum: 4750.00m);
var result = MessageValidator.Validate(msg);
Assert.Contains(result.Errors, e => e.Code == "CONTROL_SUM_MISMATCH");
}
[Fact]
public void ZeroPaymentAmount_ShouldFailBusinessRule()
{
var msg = BuildAdvice(paymentAmount: 0m);
var result = MessageValidator.Validate(msg);
Assert.Contains(result.Errors, e => e.Code == "ZERO_PAYMENT_AMOUNT");
}
// --- Category 4: FinancialItem ---
[Fact]
public void InvalidCreditDebitCode_ShouldFailSchema()
{
var xml = BuildAdviceXml(creditDebitCode: "MAYBE");
var errors = ValidateXml(xml, LoadSchema("tsmt.053.001.01.xsd"));
Assert.NotEmpty(errors);
}
[Fact]
public void AmountArithmeticFailure_TaxPlusPretaxNotEqualTotal()
{
var msg = BuildAdvice(taxableAmount: 4000m, taxAmount: 500m, totalAmount: 9999m);
var result = MessageValidator.Validate(msg);
Assert.Contains(result.Errors, e => e.Code == "AMOUNT_ARITHMETIC_FAILURE");
}
// --- Category 6: Outbound Response Conformance ---
[Fact]
public void GeneratedStatus_ShouldPassXsdValidation()
{
var advice = BuildValidAdvice();
var status = StatusGenerator.GenerateFrom(advice);
var xml = status.Serialize();
var errors = ValidateXml(xml, LoadSchema("tsmt.054.001.01.xsd"));
Assert.Empty(errors); // Must be empty — a non-conformant response is never sent
}
[Fact]
public void GeneratedStatus_ShouldReferenceOriginalAdvice()
{
var advice = BuildValidAdvice(letterId: "ADVICE-2026-001");
var status = StatusGenerator.GenerateFrom(advice);
Assert.Equal("ADVICE-2026-001", status.Header.RelatedDocument.Identification);
}
[Fact]
public void GeneratedStatus_RecipientShouldBeOriginalAdvisor()
{
var advice = BuildValidAdvice(advisorBic: "BSTRUSXX");
var status = StatusGenerator.GenerateFrom(advice);
Assert.Equal("BSTRUSXX", status.Header.PrimaryRecipient.Party.Id.AnyBic);
}
// --- Category 7: Edge Cases ---
[Fact]
public void MaxLengthField_ExactlyAtLimit_ShouldPass()
{
var xml = BuildAdviceXml(partyName: new string('A', 35));
var errors = ValidateXml(xml, LoadSchema("tsmt.053.001.01.xsd"));
Assert.Empty(errors);
}
[Fact]
public void MaxLengthField_OneBeyondLimit_ShouldFail()
{
var xml = BuildAdviceXml(partyName: new string('A', 36));
var errors = ValidateXml(xml, LoadSchema("tsmt.053.001.01.xsd"));
Assert.NotEmpty(errors);
}
// Helper: wrap XML in a Document element with namespace
private static string WrapInDocument(string ns, string inner) =>
$"<Document xmlns=\"{ns}\">{inner}</Document>";
}
}
What Just Happened?
The QA manager asked one question. The Beneficial Strategies ISO 20022 MCP Server retrieved both message specs live, resolved the complete field hierarchy including multiplicities, types, and constraints, and the AI turned that into:
- 7 test categories
- 59 individual test cases — all spec-grounded, not invented
- A C# test harness skeleton ready to fill in
- Clear separation between schema failures (caught by XSD), business rule failures (caught by your validator), and response conformance failures (caught before transmission)
The only decisions you made were whether to include XML-level tests and whether to verify response traceability.
No spec PDF opened. No field list manually assembled. No test cases missed because someone forgot about currency precision for JPY.
That’s what a subscription to the Beneficial Strategies MCP Server does for your QA team. Learn more about subscription tiers.