functional-php/tests/PipeSyntaxTest.php
2026-10-27 22:38:37 +01:00

90 lines
2.7 KiB
PHP

<?php
/**
* Copyright 2026 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
/*
* The only test written with the pipe operator, which needs PHP 8.5 to parse. phpunit.xml loads this file only on
* PHP 8.5 and newer, so the rest of the suite still runs on PHP 8.2-8.4.
*/
use Ds\Map;
use Kadet\Functional\Collections as c;
use Kadet\Functional\Collections\Lazy as lazy;
use Kadet\Functional\Exception\EmptyCollectionException;
use Kadet\Functional\Predicates as p;
use Kadet\Functional\Transforms as t;
use PHPUnit\Framework\Attributes\RequiresPhp;
final class PipeDeparture
{
public function __construct(
public readonly string $id,
public readonly string $line,
public readonly string $destination,
public readonly int $delay,
) {
}
}
#[RequiresPhp('>= 8.5')]
class PipeSyntaxTest extends \PHPUnit\Framework\TestCase
{
/** @return list<PipeDeparture> */
private static function departures(): array
{
return [
new PipeDeparture('d1', '8', 'Stogi', 0),
new PipeDeparture('d2', '8', 'Stogi', 120),
new PipeDeparture('d3', '12', 'Oliwa', 30),
new PipeDeparture('d4', '12', 'Oliwa', 300),
new PipeDeparture('d5', '6', 'Łostowice', 0),
];
}
public function testRealisticPipeline()
{
$late = self::departures()
|> lazy\filter(p\property('delay', p\gt(60)))
|> c\keyBy(t\property('id'))
|> c\map(fn (PipeDeparture $departure) => "{$departure->line} → {$departure->destination}");
$this->assertInstanceOf(Map::class, $late);
$this->assertSame(['d2' => '8 → Stogi', 'd4' => '12 → Oliwa'], $late->toArray());
}
public function testFirstRequired()
{
$mostDelayed = self::departures()
|> c\sortBy(t\property('delay'), descending: true)
|> c\first(required: true);
$this->assertSame('d4', $mostDelayed->id);
}
public function testFirstRequiredThrowsWhenNothingMatches()
{
$this->expectException(EmptyCollectionException::class);
$this->expectExceptionMessage('first(): no element matched');
self::departures() |> c\first(p\property('line', p\same('2')), required: true);
}
public function testLazyChainReadsOnlyWhatIsNeeded()
{
$read = [];
$line = self::departures()
|> lazy\tap(function (PipeDeparture $departure) use (&$read) { $read[] = $departure->id; })
|> lazy\map(t\property('line'))
|> c\find(fn (string $line) => $line === '12');
$this->assertSame('12', $line);
$this->assertSame(['d1', 'd2', 'd3'], $read);
}
}