727 lines
25 KiB
PHP
727 lines
25 KiB
PHP
<?php
|
|
|
|
namespace UbsCsvTransformer\Tests;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use UbsCsvTransformer\ColumnTransformer;
|
|
use UbsCsvTransformer\DebugLogger;
|
|
|
|
class ColumnTransformerTest extends TestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
DebugLogger::reset();
|
|
}
|
|
|
|
/**
|
|
* Helper: build a transformer with one rule and apply it to the given row.
|
|
*/
|
|
private function applyOne(array $config, array $row, array $metadata = []): array
|
|
{
|
|
return (new ColumnTransformer([$config], $metadata))->transformRow($row);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// map
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testMapPassthrough(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Name', 'outputColumn' => 'Name', 'type' => 'map'],
|
|
['Name' => 'Alice']
|
|
);
|
|
$this->assertSame('Alice', $result['Name']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// replace
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testReplace(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'replace',
|
|
'search' => 'foo',
|
|
'replace' => 'bar',
|
|
], ['Col' => 'foo baz foo']);
|
|
$this->assertSame('bar baz bar', $result['Col']);
|
|
}
|
|
|
|
public function testReplaceEmptySearchReturnsOriginal(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'replace',
|
|
'search' => '',
|
|
'replace' => 'bar',
|
|
], ['Col' => 'hello']);
|
|
$this->assertSame('hello', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// dateformat
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testDateFormat(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Date',
|
|
'outputColumn' => 'Date',
|
|
'type' => 'dateformat',
|
|
'fromFormat' => 'd.m.Y',
|
|
'toFormat' => 'Y-m-d',
|
|
], ['Date' => '15.03.2024']);
|
|
$this->assertSame('2024-03-15', $result['Date']);
|
|
}
|
|
|
|
public function testDateFormatInvalidValueReturnsOriginal(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Date',
|
|
'outputColumn' => 'Date',
|
|
'type' => 'dateformat',
|
|
'fromFormat' => 'd.m.Y',
|
|
'toFormat' => 'Y-m-d',
|
|
], ['Date' => 'not-a-date']);
|
|
$this->assertSame('not-a-date', $result['Date']);
|
|
}
|
|
|
|
public function testDateFormatEmptyValueReturnsEmpty(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Date',
|
|
'outputColumn' => 'Date',
|
|
'type' => 'dateformat',
|
|
'fromFormat' => 'd.m.Y',
|
|
'toFormat' => 'Y-m-d',
|
|
], ['Date' => '']);
|
|
$this->assertSame('', $result['Date']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// split
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testSplitPart0(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'split',
|
|
'delimiter' => ';',
|
|
'part' => 0,
|
|
], ['Col' => 'Coop Pronto;7007 Chur']);
|
|
$this->assertSame('Coop Pronto', $result['Col']);
|
|
}
|
|
|
|
public function testSplitPart1(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'split',
|
|
'delimiter' => ';',
|
|
'part' => 1,
|
|
], ['Col' => 'Coop Pronto;7007 Chur']);
|
|
$this->assertSame('7007 Chur', $result['Col']);
|
|
}
|
|
|
|
public function testSplitPartOutOfBoundsReturnsOriginal(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'split',
|
|
'delimiter' => ';',
|
|
'part' => 5,
|
|
], ['Col' => 'A;B']);
|
|
$this->assertSame('A;B', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// regexextract
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testRegexExtract(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Zip',
|
|
'type' => 'regexextract',
|
|
'pattern' => '(\d{4})',
|
|
], ['Col' => 'Shop 7007 Chur', 'Zip' => '']);
|
|
$this->assertSame('7007', $result['Zip']);
|
|
}
|
|
|
|
public function testRegexExtractNoMatchReturnsEmpty(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Zip',
|
|
'type' => 'regexextract',
|
|
'pattern' => '(\d{4})',
|
|
], ['Col' => 'No digits here', 'Zip' => '']);
|
|
$this->assertSame('', $result['Zip']);
|
|
}
|
|
|
|
public function testRegexExtractEmptyValueReturnsEmpty(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Zip',
|
|
'type' => 'regexextract',
|
|
'pattern' => '(\d{4})',
|
|
], ['Col' => '', 'Zip' => '']);
|
|
$this->assertSame('', $result['Zip']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// trim
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testTrim(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'trim'],
|
|
['Col' => ' hello world ']
|
|
);
|
|
$this->assertSame('hello world', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// uppercase
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testUppercase(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'uppercase'],
|
|
['Col' => 'Hello World']
|
|
);
|
|
$this->assertSame('HELLO WORLD', $result['Col']);
|
|
}
|
|
|
|
public function testUppercaseUnicode(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'uppercase'],
|
|
['Col' => 'zürich']
|
|
);
|
|
$this->assertSame('ZÜRICH', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// lowercase
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testLowercase(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'lowercase'],
|
|
['Col' => 'Hello World']
|
|
);
|
|
$this->assertSame('hello world', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// ucwordsfirst
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testUcwordsFirst(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'ucwordsfirst'],
|
|
['Col' => 'COOP PRONTO CHUR']
|
|
);
|
|
$this->assertSame('Coop Pronto Chur', $result['Col']);
|
|
}
|
|
|
|
public function testUcwordsFirstHyphen(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'ucwordsfirst'],
|
|
['Col' => 'SAINT-JEAN-DE-MAURIENNE']
|
|
);
|
|
$this->assertSame('Saint-Jean-De-Maurienne', $result['Col']);
|
|
}
|
|
|
|
public function testUcwordsFirstApostrophe(): void
|
|
{
|
|
$result = $this->applyOne(
|
|
['sourceColumn' => 'Col', 'outputColumn' => 'Col', 'type' => 'ucwordsfirst'],
|
|
['Col' => "O'NEILL STORE"]
|
|
);
|
|
$this->assertSame("O'Neill Store", $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// truncate
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testTruncate(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'truncate',
|
|
'maxLength' => 5,
|
|
], ['Col' => 'Hello World']);
|
|
$this->assertSame('Hello', $result['Col']);
|
|
}
|
|
|
|
public function testTruncateShorterThanMaxIsUnchanged(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'truncate',
|
|
'maxLength' => 100,
|
|
], ['Col' => 'Short']);
|
|
$this->assertSame('Short', $result['Col']);
|
|
}
|
|
|
|
public function testTruncateUnicode(): void
|
|
{
|
|
// 'ü' counts as 1 Unicode character, so maxLength=3 gives 3 chars: Z, ü, r
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'truncate',
|
|
'maxLength' => 3,
|
|
], ['Col' => 'Zürich']);
|
|
$this->assertSame('Zür', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// constantvalue
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testConstantValue(): void
|
|
{
|
|
$transformer = new ColumnTransformer([[
|
|
'sourceColumn' => '_constant_',
|
|
'outputColumn' => 'Currency',
|
|
'type' => 'constantvalue',
|
|
'metadataKey' => 'currency_code',
|
|
]], ['currency_code' => 'CHF']);
|
|
$result = $transformer->transformRow(['Currency' => '']);
|
|
$this->assertSame('CHF', $result['Currency']);
|
|
}
|
|
|
|
public function testConstantValueMissingKeyReturnsEmpty(): void
|
|
{
|
|
$transformer = new ColumnTransformer([[
|
|
'sourceColumn' => '_constant_',
|
|
'outputColumn' => 'Currency',
|
|
'type' => 'constantvalue',
|
|
'metadataKey' => 'nonexistent',
|
|
]], []);
|
|
$result = $transformer->transformRow(['Currency' => '']);
|
|
$this->assertSame('', $result['Currency']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// pipeline
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testPipeline(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'pipeline',
|
|
'steps' => [
|
|
['type' => 'trim'],
|
|
['type' => 'lowercase'],
|
|
['type' => 'ucwordsfirst'],
|
|
],
|
|
], ['Col' => ' COOP PRONTO ']);
|
|
$this->assertSame('Coop Pronto', $result['Col']);
|
|
}
|
|
|
|
public function testPipelineEmptyStepsReturnsOriginal(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'pipeline',
|
|
'steps' => [],
|
|
], ['Col' => 'hello']);
|
|
$this->assertSame('hello', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Inline transformations[] array (flat pipeline per column entry)
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testInlineTransformationsArray(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'map',
|
|
'transformations' => [
|
|
['type' => 'trim'],
|
|
['type' => 'uppercase'],
|
|
],
|
|
], ['Col' => ' hello ']);
|
|
$this->assertSame('HELLO', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// normalizeTransformType: snake_case and kebab-case aliases
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testNormalizeTypeSnakeCase(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Date',
|
|
'outputColumn' => 'Date',
|
|
'type' => 'date_format',
|
|
'fromFormat' => 'd.m.Y',
|
|
'toFormat' => 'Y-m-d',
|
|
], ['Date' => '15.03.2024']);
|
|
$this->assertSame('2024-03-15', $result['Date']);
|
|
}
|
|
|
|
public function testNormalizeTypeKebabCase(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Col',
|
|
'outputColumn' => 'Col',
|
|
'type' => 'ucwords-first',
|
|
], ['Col' => 'HELLO WORLD']);
|
|
$this->assertSame('Hello World', $result['Col']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// outputAction
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testOutputActionOverwrite(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'overwrite',
|
|
], ['A' => 'new', 'B' => 'old']);
|
|
$this->assertSame('new', $result['B']);
|
|
}
|
|
|
|
public function testOutputActionCreate(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'NewCol',
|
|
'type' => 'map',
|
|
'outputAction' => 'create',
|
|
], ['A' => 'hello']);
|
|
$this->assertSame('hello', $result['NewCol']);
|
|
}
|
|
|
|
public function testOutputActionAppend(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append',
|
|
], ['A' => ' World', 'B' => 'Hello']);
|
|
$this->assertSame('Hello World', $result['B']);
|
|
}
|
|
|
|
public function testOutputActionAppendWithDelimiter(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append',
|
|
'appendDelimiter' => ', ',
|
|
], ['A' => 'World', 'B' => 'Hello']);
|
|
$this->assertSame('Hello, World', $result['B']);
|
|
}
|
|
|
|
public function testOutputActionAppendWithDelimiterSkippedWhenTargetEmpty(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append',
|
|
'appendDelimiter' => ', ',
|
|
], ['A' => 'Hello', 'B' => '']);
|
|
$this->assertSame('Hello', $result['B']);
|
|
}
|
|
|
|
public function testOutputActionAppendLine(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append-line',
|
|
], ['A' => 'Line2', 'B' => 'Line1']);
|
|
$this->assertSame("Line1\nLine2", $result['B']);
|
|
}
|
|
|
|
public function testOutputActionAppendLineNoLeadingNewlineWhenEmpty(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append-line',
|
|
], ['A' => 'Line1', 'B' => '']);
|
|
$this->assertSame('Line1', $result['B']);
|
|
}
|
|
|
|
public function testOutputActionOverwriteIfEmpty(): void
|
|
{
|
|
$resultEmpty = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'overwrite-if-empty',
|
|
], ['A' => 'new', 'B' => '']);
|
|
$this->assertSame('new', $resultEmpty['B']);
|
|
|
|
$resultFilled = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'overwrite-if-empty',
|
|
], ['A' => 'new', 'B' => 'existing']);
|
|
$this->assertSame('existing', $resultFilled['B']);
|
|
}
|
|
|
|
public function testOutputActionOverwriteIfNotEmpty(): void
|
|
{
|
|
$resultNotEmpty = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'overwrite-if-not-empty',
|
|
], ['A' => 'new', 'B' => 'old']);
|
|
$this->assertSame('new', $resultNotEmpty['B']);
|
|
|
|
$resultEmpty = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'overwrite-if-not-empty',
|
|
], ['A' => '', 'B' => 'old']);
|
|
$this->assertSame('old', $resultEmpty['B']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// multi-output split
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testMultiOutputSplit(): void
|
|
{
|
|
$transformer = new ColumnTransformer([[
|
|
'outputs' => ['FirstName', 'LastName'],
|
|
'sourceColumn' => 'FullName',
|
|
'type' => 'split',
|
|
'delimiter' => ' ',
|
|
]]);
|
|
$result = $transformer->transformRow(['FullName' => 'John Doe']);
|
|
$this->assertSame('John', $result['FirstName']);
|
|
$this->assertSame('Doe', $result['LastName']);
|
|
}
|
|
|
|
public function testMultiOutputSplitFewerPartsYieldsEmptyString(): void
|
|
{
|
|
$transformer = new ColumnTransformer([[
|
|
'outputs' => ['Col1', 'Col2', 'Col3'],
|
|
'sourceColumn' => 'Source',
|
|
'type' => 'split',
|
|
'delimiter' => ';',
|
|
]]);
|
|
$result = $transformer->transformRow(['Source' => 'A;B']);
|
|
$this->assertSame('A', $result['Col1']);
|
|
$this->assertSame('B', $result['Col2']);
|
|
$this->assertSame('', $result['Col3']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Error cases
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testMissingOutputColumnThrows(): void
|
|
{
|
|
$this->expectException(\RuntimeException::class);
|
|
$transformer = new ColumnTransformer([
|
|
['sourceColumn' => 'A', 'type' => 'map'],
|
|
]);
|
|
$transformer->transformRow(['A' => 'x']);
|
|
}
|
|
|
|
public function testMultiOutputNonSplitTypeThrows(): void
|
|
{
|
|
$this->expectException(\RuntimeException::class);
|
|
$transformer = new ColumnTransformer([[
|
|
'outputs' => ['Col1', 'Col2'],
|
|
'sourceColumn' => 'Source',
|
|
'type' => 'uppercase',
|
|
]]);
|
|
$transformer->transformRow(['Source' => 'hello']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// getOutputColumns
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testGetOutputColumnsCountsUniqueColumns(): void
|
|
{
|
|
$transformer = new ColumnTransformer([
|
|
['sourceColumn' => 'A', 'outputColumn' => 'X', 'type' => 'map'],
|
|
['sourceColumn' => 'B', 'outputColumn' => 'Y', 'type' => 'map'],
|
|
['sourceColumn' => 'C', 'outputColumn' => 'X', 'type' => 'map'], // duplicate output
|
|
]);
|
|
$transformer->transformRow(['A' => '1', 'B' => '2', 'C' => '3']);
|
|
$this->assertSame(2, $transformer->getOutputColumns());
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// timeperiod
|
|
// -------------------------------------------------------------------------
|
|
|
|
/** @var array<int, array<string, string>> */
|
|
private array $testPeriods = [
|
|
['from' => '04:00:00', 'to' => '08:59:59', 'label' => 'Morgen'],
|
|
['from' => '09:00:00', 'to' => '10:59:59', 'label' => 'Vormittag'],
|
|
['from' => '11:00:00', 'to' => '13:59:59', 'label' => 'Mittag'],
|
|
['from' => '14:00:00', 'to' => '17:59:59', 'label' => 'Nachmittag'],
|
|
['from' => '18:00:00', 'to' => '21:59:59', 'label' => 'Abend'],
|
|
['from' => '22:00:00', 'to' => '03:59:59', 'label' => 'Nacht'],
|
|
];
|
|
|
|
public function testTimePeriodBasicMapping(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Time',
|
|
'outputColumn' => 'Period',
|
|
'type' => 'timeperiod',
|
|
'timeFormat' => 'H:i:s',
|
|
'periods' => $this->testPeriods,
|
|
'default' => '',
|
|
], ['Time' => '09:30:00', 'Period' => '']);
|
|
$this->assertSame('Vormittag', $result['Period']);
|
|
}
|
|
|
|
public function testTimePeriodMidnightSpanning(): void
|
|
{
|
|
$result1 = $this->applyOne([
|
|
'sourceColumn' => 'Time',
|
|
'outputColumn' => 'Period',
|
|
'type' => 'timeperiod',
|
|
'timeFormat' => 'H:i:s',
|
|
'periods' => $this->testPeriods,
|
|
'default' => '',
|
|
], ['Time' => '23:00:00', 'Period' => '']);
|
|
$this->assertSame('Nacht', $result1['Period']);
|
|
|
|
$result2 = $this->applyOne([
|
|
'sourceColumn' => 'Time',
|
|
'outputColumn' => 'Period',
|
|
'type' => 'timeperiod',
|
|
'timeFormat' => 'H:i:s',
|
|
'periods' => $this->testPeriods,
|
|
'default' => '',
|
|
], ['Time' => '02:00:00', 'Period' => '']);
|
|
$this->assertSame('Nacht', $result2['Period']);
|
|
}
|
|
|
|
public function testTimePeriodNoMatch(): void
|
|
{
|
|
// 03:45 falls outside all labelled ranges except Nacht (00:00-03:59)
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Time',
|
|
'outputColumn' => 'Period',
|
|
'type' => 'timeperiod',
|
|
'timeFormat' => 'H:i:s',
|
|
'periods' => [
|
|
['from' => '09:00:00', 'to' => '17:59:59', 'label' => 'Day'],
|
|
],
|
|
'default' => 'Unknown',
|
|
], ['Time' => '03:45:00', 'Period' => '']);
|
|
$this->assertSame('Unknown', $result['Period']);
|
|
}
|
|
|
|
public function testTimePeriodInvalidInput(): void
|
|
{
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'Time',
|
|
'outputColumn' => 'Period',
|
|
'type' => 'timeperiod',
|
|
'timeFormat' => 'H:i:s',
|
|
'periods' => $this->testPeriods,
|
|
'default' => 'N/A',
|
|
], ['Time' => '', 'Period' => '']);
|
|
$this->assertSame('N/A', $result['Period']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// ucwordsfirst guard
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testUcwordsFirstSkipsLowercase(): void
|
|
{
|
|
// Input already contains lowercase letters → must be returned unchanged
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'A',
|
|
'type' => 'ucwordsfirst',
|
|
], ['A' => 'Coop pronto chur']);
|
|
$this->assertSame('Coop pronto chur', $result['A']);
|
|
}
|
|
|
|
public function testUcwordsFirstAppliesAllCaps(): void
|
|
{
|
|
// Fully uppercase input → capitalise first letter of each word
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'A',
|
|
'type' => 'ucwordsfirst',
|
|
], ['A' => 'COOP PRONTO']);
|
|
$this->assertSame('Coop Pronto', $result['A']);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// append-if-not-empty
|
|
// -------------------------------------------------------------------------
|
|
|
|
public function testAppendIfNotEmptySkipsEmpty(): void
|
|
{
|
|
// Result is empty → target column must remain unchanged
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append-if-not-empty',
|
|
'appendDelimiter' => ' ',
|
|
], ['A' => '', 'B' => 'existing']);
|
|
$this->assertSame('existing', $result['B']);
|
|
}
|
|
|
|
public function testAppendIfNotEmptyAppendsNonEmpty(): void
|
|
{
|
|
// Non-empty result → appended with delimiter
|
|
$result = $this->applyOne([
|
|
'sourceColumn' => 'A',
|
|
'outputColumn' => 'B',
|
|
'type' => 'map',
|
|
'outputAction' => 'append-if-not-empty',
|
|
'appendDelimiter' => ' ',
|
|
], ['A' => 'new', 'B' => 'existing']);
|
|
$this->assertSame('existing new', $result['B']);
|
|
}
|
|
}
|