Files
maraphon-app/tests/Marathon.Application.Tests/Export/CsvTests.cs
T
alexei.dolgolyov 88615a95e9 feat(export): CSV export for the bet journal + forward-test ledger
Adds CSV export alongside the Excel snapshot export: two buttons on the Export hub
write the bet journal and the paper-trading ledger to UTF-8 (BOM) .csv files in the
configured export directory and toast the path — mirroring the Excel use case's
write-and-return-path contract. CSV needs no third-party library, so it lives in the
Application layer behind a pure RFC-4180 formatter.

- Csv formatter (RFC 4180 escaping) + ExportToCsvUseCase (journal + ledger, batched
  title join, returns null when there's nothing to export); registered; Export hub
  buttons + en/ru resx.
- 11 tests: formatter escaping/Document + use-case empty-state + real-file write to a temp dir.
2026-05-29 13:44:35 +03:00

42 lines
1.0 KiB
C#

using FluentAssertions;
using Marathon.Application.Export;
namespace Marathon.Application.Tests.Export;
public sealed class CsvTests
{
[Theory]
[InlineData("plain", "plain")]
[InlineData("", "")]
[InlineData("a,b", "\"a,b\"")]
[InlineData("he said \"hi\"", "\"he said \"\"hi\"\"\"")]
[InlineData("line1\nline2", "\"line1\nline2\"")]
public void Escape_QuotesOnlyWhenNeeded(string input, string expected)
{
Csv.Escape(input).Should().Be(expected);
}
[Fact]
public void Escape_Null_IsEmpty()
{
Csv.Escape(null).Should().BeEmpty();
}
[Fact]
public void Document_JoinsHeaderAndRows_WithCrlf_AndEscapes()
{
var csv = Csv.Document(
new[] { "Name", "Note" },
new[]
{
(IReadOnlyList<string>)new[] { "Kelly", "ok" },
new[] { "Flat, fixed", "say \"hi\"" },
});
csv.Should().Be(
"Name,Note\r\n" +
"Kelly,ok\r\n" +
"\"Flat, fixed\",\"say \"\"hi\"\"\"\r\n");
}
}