Files
JobcardSystem/tests/CsvExporterTest.php

45 lines
1.7 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../app/Domain/Reporting/CsvExporter.php';
function csv_exporter_assert_same(mixed $expected, mixed $actual, string $message): void
{
if ($expected !== $actual) {
throw new RuntimeException($message . "\nExpected: " . var_export($expected, true) . "\nActual: " . var_export($actual, true));
}
}
$exporter = new CsvExporter();
csv_exporter_assert_same(
"Name,Notes,Amount\r\nAlice,\"Comma, quote \"\"inside\"\"\",12.5\r\n\"Bob\r\nJones\",plain,\r\n",
$exporter->export(
['Name', 'Notes', 'Amount'],
[
['Alice', 'Comma, quote "inside"', 12.5],
["Bob\r\nJones", 'plain', null],
],
),
'CSV fields must follow RFC 4180 quoting and deterministic CRLF records.'
);
$withoutBom = $exporter->export(['Name'], [['Alice']]);
$withBom = $exporter->export(['Name'], [['Alice']], true);
csv_exporter_assert_same("Name\r\nAlice\r\n", $withoutBom, 'CSV must not include a BOM by default.');
csv_exporter_assert_same("\xEF\xBB\xBFName\r\nAlice\r\n", $withBom, 'CSV must include a UTF-8 BOM only when requested.');
csv_exporter_assert_same("Name\r\n'=SUM(A1:A2)\r\n", $exporter->export(['Name'], [['=SUM(A1:A2)']]), 'CSV must neutralize spreadsheet formulas.');
$mismatchRaised = false;
try {
$exporter->export(['Name', 'Amount'], [['Alice']]);
} catch (InvalidArgumentException $exception) {
$mismatchRaised = str_contains($exception->getMessage(), 'row 1')
&& str_contains($exception->getMessage(), '2 headers')
&& str_contains($exception->getMessage(), '1 fields');
}
if (!$mismatchRaised) {
throw new RuntimeException('Mismatched row lengths must be rejected with a useful error.');
}
printf("CSV exporter tests: 4 passed\n");