Enhance banking transaction data structure and validation
Playwright Tests / test (push) Has been cancelled

- Increased the length of the NameOfPayer field from NVARCHAR(60) to NVARCHAR(140) in the banking transactions function, table, temporary table, and user-defined type to accommodate longer names.
- Expanded the SepaRemittanceInformation field from VARCHAR(150) to VARCHAR(200) in the banking transactions function for more detailed remittance information.
- Modified the stored procedure fds__setBankingtransaction_done to return additional transaction data (ValueDate and Amount) along with the success flag for improved notification messaging.
- Added validation in MFRClientConfig constructor to ensure the provided URL is not null or empty, enhancing error handling for configuration settings.
This commit is contained in:
Stefan
2026-07-04 16:37:23 +02:00
parent aaf062fd77
commit c98be7b23f
24 changed files with 287 additions and 74 deletions
+47
View File
@@ -129,4 +129,51 @@ public class BankingParseToDatatableTests
var ex = Record.Exception(() => Svc.ParseToDatatable(stream));
Assert.Null(ex);
}
[Theory]
[InlineData("AccountIdentification", 50)]
[InlineData("NameOfPayer", 140)]
[InlineData("SepaRemittanceInformation", 200)]
[InlineData("DebitCreditMark", 2)]
[InlineData("UnstructuredData", 390)]
public void ParseToDatatable_DefaultSchema_EnforcesKnownColumnWidth(string column, int expectedMaxLength)
{
using var stream = ToStream(MinimalMT940);
var table = Svc.ParseToDatatable(stream);
Assert.Equal(expectedMaxLength, table.Columns[column]!.MaxLength);
}
[Fact]
public void ParseToDatatable_SchemaWithoutMaxLength_StillEnforcesKnownWidths()
{
// Mirrors the real bug: SELECT TOP(0) * FROM @tmp (a table-type variable) comes back
// from ADO.NET with MaxLength = -1 for every string column, so a schema built purely
// from that fetch cannot truncate on its own. ApplyKnownColumnWidths must patch it up
// regardless of what the caller-supplied schema carries.
var schema = new DataTable();
schema.Columns.Add("AccountIdentification", typeof(string));
schema.Columns.Add("Amount", typeof(decimal));
schema.Columns.Add("DebitCreditMark", typeof(string));
Assert.Equal(-1, schema.Columns["AccountIdentification"]!.MaxLength);
using var stream = ToStream(MinimalMT940);
var table = Svc.ParseToDatatable(stream, schemaDatatable: schema);
Assert.Equal(50, table.Columns["AccountIdentification"]!.MaxLength);
Assert.Equal(2, table.Columns["DebitCreditMark"]!.MaxLength);
}
[Fact]
public void ParseToDatatable_OverlongAccountIdentification_TruncatesInsteadOfThrowing()
{
string longAccount = "DE" + new string('9', 60); // 62 chars, over the 50-char column width
string mt940 = MinimalMT940.Replace("DE12345678901234567890", longAccount);
using var stream = ToStream(mt940);
var table = Svc.ParseToDatatable(stream);
Assert.Equal(1, table.Rows.Count);
Assert.Equal(50, ((string)table.Rows[0]["AccountIdentification"]).Length);
Assert.Equal(longAccount[..50], table.Rows[0]["AccountIdentification"]);
}
}
+6 -4
View File
@@ -391,7 +391,7 @@ public class BankingDualFormatTests
t.Columns.Add("Amount", typeof(decimal));
t.Columns.Add("ValueDate", typeof(DateTime));
t.Columns.Add("FundsCode", typeof(string)).MaxLength = 1; // VARCHAR(1) — currency "EUR" must not land here
t.Columns.Add("NameOfPayer", typeof(string)).MaxLength = 60;
t.Columns.Add("NameOfPayer", typeof(string)).MaxLength = 140;
t.Columns.Add("PostingText", typeof(string)).MaxLength = 30;
t.Columns.Add("TransactionTypeIdCode", typeof(string)).MaxLength = 3;
t.Columns.Add("SepaRemittanceInformation", typeof(string)).MaxLength = 200;
@@ -418,7 +418,9 @@ public class BankingDualFormatTests
[Fact]
public void ParseToDatatable_Camt_OverlongFields_AreTruncatedNotDropped()
{
const string longName = "Ein sehr langer Zahlungspflichtiger Name der weit ueber sechzig Zeichen hinausgeht GmbH Co KG";
// Some banks put the full postal address into <Nm>, not just a name — real-world case
// that exceeds even the ISO 20022 Max140Text width this column is now sized to.
const string longName = "Ein sehr langer Zahlungspflichtiger Name der weit ueber hundertvierzig Zeichen hinausgeht GmbH Co KG, Musterstrasse 123, 12345 Musterstadt, Deutschland";
string camt = """
<?xml version="1.0"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
@@ -440,7 +442,7 @@ public class BankingDualFormatTests
var t = Svc.ParseToDatatable(s, schemaDatatable: DbLikeSchema());
Assert.Equal(1, t.Rows.Count);
Assert.Equal(60, ((string)t.Rows[0]["NameOfPayer"]).Length); // truncated to column width, row kept
Assert.Equal(longName[..60], t.Rows[0]["NameOfPayer"]);
Assert.Equal(140, ((string)t.Rows[0]["NameOfPayer"]).Length); // truncated to column width, row kept
Assert.Equal(longName[..140], t.Rows[0]["NameOfPayer"]);
}
}
+12
View File
@@ -55,6 +55,18 @@ public class MFRClientConfigTests
Assert.Equal("", config.LogoutAddress);
Assert.Equal("", config.TokenCookieName);
}
// Regression: an empty/missing host used to be silently turned into "https:///odata/"
// (empty authority), which only failed much later inside RestSharp with a cryptic
// "Invalid URI: The hostname could not be parsed." Fail fast at construction instead.
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Constructor_WithMissingHost_ThrowsArgumentException(string? url)
{
Assert.Throws<ArgumentException>(() => new MFRClientConfig(url!));
}
}
public class MFRClientCredentialsTests