fix(commons): OverrideCsvParser — preserve literal mid-field quotes, error on unterminated quoted field (T16 CSV)
This commit is contained in:
@@ -169,4 +169,80 @@ public class OverrideCsvParserTests
|
||||
Assert.Equal("42", result.Rows[0].Value);
|
||||
Assert.Equal(" spaced ", result.Rows[1].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_MidFieldUnquotedQuote_IsPreservedAsLiteral()
|
||||
{
|
||||
// A '"' that does NOT open a field (it follows non-whitespace content in an
|
||||
// unquoted field) is a literal character — 'va"lue' must survive intact.
|
||||
const string csv = "AttributeName,Value,ElementType\nName,va\"lue,Type\n";
|
||||
|
||||
var result = OverrideCsvParser.Parse(csv);
|
||||
|
||||
Assert.Empty(result.Errors);
|
||||
var row = Assert.Single(result.Rows);
|
||||
Assert.Equal("Name", row.AttributeName);
|
||||
Assert.Equal("va\"lue", row.Value);
|
||||
Assert.Equal("Type", row.ElementType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_UnterminatedQuotedField_ProducesLineNumberedErrorAndExcludesRow()
|
||||
{
|
||||
// A quote opens the field but is never closed before end-of-line → malformed.
|
||||
const string csv = "AttributeName,Value\nName,\"unclosed\n";
|
||||
|
||||
var result = OverrideCsvParser.Parse(csv);
|
||||
|
||||
Assert.Empty(result.Rows);
|
||||
var error = Assert.Single(result.Errors);
|
||||
Assert.Contains("2", error);
|
||||
Assert.Contains("Unterminated", error, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_WellFormedQuotedFieldWithComma_StillParses()
|
||||
{
|
||||
// Regression guard alongside the unterminated-quote fix: a properly closed
|
||||
// quoted field embedding a comma must still round-trip.
|
||||
const string csv = "AttributeName,Value,ElementType\nName,\"a,b\",Type\n";
|
||||
|
||||
var result = OverrideCsvParser.Parse(csv);
|
||||
|
||||
Assert.Empty(result.Errors);
|
||||
var row = Assert.Single(result.Rows);
|
||||
Assert.Equal("Name", row.AttributeName);
|
||||
Assert.Equal("a,b", row.Value);
|
||||
Assert.Equal("Type", row.ElementType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_EmptyQuotedField_YieldsNullValue()
|
||||
{
|
||||
// A bare "" is an empty quoted field; empty → null per the empty-field rule.
|
||||
const string csv = "AttributeName,Value,ElementType\nName,\"\",Type\n";
|
||||
|
||||
var result = OverrideCsvParser.Parse(csv);
|
||||
|
||||
Assert.Empty(result.Errors);
|
||||
var row = Assert.Single(result.Rows);
|
||||
Assert.Equal("Name", row.AttributeName);
|
||||
Assert.Null(row.Value);
|
||||
Assert.Equal("Type", row.ElementType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_QuotedFieldWithTrailingWhitespaceAfterClose_IsAccepted()
|
||||
{
|
||||
// The closing quote may be followed by ignorable trailing whitespace before
|
||||
// the delimiter; the field value itself is preserved verbatim.
|
||||
const string csv = "AttributeName,Value,ElementType\nName,\"a,b\" ,Type\n";
|
||||
|
||||
var result = OverrideCsvParser.Parse(csv);
|
||||
|
||||
Assert.Empty(result.Errors);
|
||||
var row = Assert.Single(result.Rows);
|
||||
Assert.Equal("a,b", row.Value);
|
||||
Assert.Equal("Type", row.ElementType);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user