200 lines
7.0 KiB
PHP
200 lines
7.0 KiB
PHP
<?php
|
||
|
||
namespace UbsCsvTransformer\Tests;
|
||
|
||
use PHPUnit\Framework\TestCase;
|
||
use UbsCsvTransformer\CsvReader;
|
||
|
||
class CsvReaderTest extends TestCase
|
||
{
|
||
private string $tempFile = '';
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$this->tempFile = tempnam(sys_get_temp_dir(), 'csvreader_test_');
|
||
}
|
||
|
||
protected function tearDown(): void
|
||
{
|
||
if (file_exists($this->tempFile)) {
|
||
unlink($this->tempFile);
|
||
}
|
||
}
|
||
|
||
private function write(string $content): void
|
||
{
|
||
file_put_contents($this->tempFile, $content);
|
||
}
|
||
|
||
private function reader(int $headerLine = 1, string $delimiter = ';', bool $hasBom = false): CsvReader
|
||
{
|
||
return new CsvReader(
|
||
$this->tempFile,
|
||
['inputDelimiter' => $delimiter, 'headerLine' => $headerLine, 'hasBom' => $hasBom]
|
||
);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// readCsvData – basic parsing
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testReadCsvDataSimple(): void
|
||
{
|
||
$this->write("Name;Age\nAlice;30\nBob;25\n");
|
||
$data = $this->reader()->readCsvData();
|
||
$this->assertCount(2, $data);
|
||
$this->assertSame('Alice', $data[0]['Name']);
|
||
$this->assertSame('30', $data[0]['Age']);
|
||
$this->assertSame('Bob', $data[1]['Name']);
|
||
}
|
||
|
||
public function testReadCsvDataTrimsWhitespace(): void
|
||
{
|
||
$this->write("Name ; Age\n Alice ; 30 \n");
|
||
$data = $this->reader()->readCsvData();
|
||
$this->assertSame('Alice', $data[0]['Name']);
|
||
$this->assertSame('30', $data[0]['Age']);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// headerLine offset
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testHeaderLineOffset(): void
|
||
{
|
||
// Lines 1+2 are metadata, header is line 3
|
||
$this->write("Meta1\nMeta2\nColA;ColB\nval1;val2\n");
|
||
$data = $this->reader(3)->readCsvData();
|
||
$this->assertCount(1, $data);
|
||
$this->assertSame('val1', $data[0]['ColA']);
|
||
$this->assertSame('val2', $data[0]['ColB']);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// readMetadataLines
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testReadMetadataLines(): void
|
||
{
|
||
$this->write("Line1\nLine2\nHeader;Col\nData;Row\n");
|
||
$meta = $this->reader(3)->readMetadataLines();
|
||
$this->assertCount(2, $meta);
|
||
$this->assertSame('Line1', $meta[0]);
|
||
$this->assertSame('Line2', $meta[1]);
|
||
}
|
||
|
||
public function testReadMetadataLinesHeaderOnLine1IsEmpty(): void
|
||
{
|
||
$this->write("Name;Age\nAlice;30\n");
|
||
$meta = $this->reader(1)->readMetadataLines();
|
||
$this->assertSame([], $meta);
|
||
}
|
||
|
||
public function testReadMetadataLinesHeaderOnLine2ReturnsOneLine(): void
|
||
{
|
||
$this->write("MetaInfo\nName;Age\nAlice;30\n");
|
||
$meta = $this->reader(2)->readMetadataLines();
|
||
$this->assertCount(1, $meta);
|
||
$this->assertSame('MetaInfo', $meta[0]);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// maxDataRows limit
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testMaxDataRowsLimit(): void
|
||
{
|
||
$this->write("Name;Age\nAlice;30\nBob;25\nCarol;20\n");
|
||
$data = $this->reader()->readCsvData(2);
|
||
$this->assertCount(2, $data);
|
||
$this->assertSame('Alice', $data[0]['Name']);
|
||
$this->assertSame('Bob', $data[1]['Name']);
|
||
}
|
||
|
||
public function testMaxDataRowsZeroMeansAll(): void
|
||
{
|
||
$this->write("Name;Age\nAlice;30\nBob;25\nCarol;20\n");
|
||
$data = $this->reader()->readCsvData(0);
|
||
$this->assertCount(3, $data);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Empty line skipping
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testEmptyLinesAreSkipped(): void
|
||
{
|
||
$this->write("Name;Age\nAlice;30\n\nBob;25\n\n");
|
||
$data = $this->reader()->readCsvData();
|
||
$this->assertCount(2, $data);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// BOM removal
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testBomIsRemovedFromFirstColumnName(): void
|
||
{
|
||
// UTF-8 BOM followed immediately by the header
|
||
$this->write("\xEF\xBB\xBFName;Age\nAlice;30\n");
|
||
$data = $this->reader(1, ';', true)->readCsvData();
|
||
$this->assertCount(1, $data);
|
||
// The column must be 'Name', not the BOM-prefixed version
|
||
$this->assertArrayHasKey('Name', $data[0]);
|
||
$this->assertSame('Alice', $data[0]['Name']);
|
||
}
|
||
|
||
public function testNoBomFlagLeavesHeaderIntact(): void
|
||
{
|
||
// If hasBom is false, BOM bytes stay and the key will be mangled — we only
|
||
// assert that the clean path (hasBom=true) works, tested above.
|
||
// Here we just verify normal CSV without BOM also works when hasBom=false.
|
||
$this->write("Name;Age\nAlice;30\n");
|
||
$data = $this->reader(1, ';', false)->readCsvData();
|
||
$this->assertArrayHasKey('Name', $data[0]);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// getHeaders
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testGetHeaders(): void
|
||
{
|
||
$this->write("Col1;Col2;Col3\nA;B;C\n");
|
||
$headers = $this->reader()->getHeaders();
|
||
$this->assertSame(['Col1', 'Col2', 'Col3'], $headers);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Row with fewer columns than headers
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testShortRowPaddedWithEmpty(): void
|
||
{
|
||
$this->write("A;B;C\n1;2\n");
|
||
$data = $this->reader()->readCsvData();
|
||
$this->assertCount(1, $data);
|
||
$this->assertSame('1', $data[0]['A']);
|
||
$this->assertSame('2', $data[0]['B']);
|
||
$this->assertSame('', $data[0]['C']);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Error cases
|
||
// -------------------------------------------------------------------------
|
||
|
||
public function testFileNotFoundThrowsRuntimeException(): void
|
||
{
|
||
$this->expectException(\RuntimeException::class);
|
||
$reader = new CsvReader('/nonexistent/path/file.csv', ['inputDelimiter' => ';', 'headerLine' => 1]);
|
||
$reader->readLines();
|
||
}
|
||
|
||
public function testHeaderLineBeyondFileLengthThrows(): void
|
||
{
|
||
$this->write("Name;Age\nAlice;30\n");
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->reader(99)->readCsvData();
|
||
}
|
||
}
|