|
8 | 8 | use Itools\SmartArray\SmartArray; |
9 | 9 | use Itools\SmartArray\SmartArrayBase; |
10 | 10 | use Itools\SmartArray\SmartArrayHtml; |
| 11 | +use Itools\SmartArray\SmartNull; |
11 | 12 | use Itools\SmartArray\Tests\Support\SmartArrayTestCase; |
12 | 13 | use PHPUnit\Framework\Attributes\DataProvider; |
13 | 14 | use ReflectionClass; |
@@ -497,6 +498,60 @@ public function testNestedReadChainSignalsOncePerLevel(): void |
497 | 498 | ], $this->normalizeCaller($deprecations)); |
498 | 499 | } |
499 | 500 |
|
| 501 | + //endregion |
| 502 | + //region SmartNull (missing keys and empty results) |
| 503 | + |
| 504 | + #[DataProvider('modeProvider')] |
| 505 | + public function testSmartNullBracketReadDispatchesLikeARealRow(string $class): void |
| 506 | + { |
| 507 | + // Missing-data paths signal too, so a migration sweep also finds the |
| 508 | + // bracket call sites that only run when a result is empty |
| 509 | + $smartNull = $class::new([])->first(); |
| 510 | + |
| 511 | + [[$result, $output], $deprecations] = $this->withOffsetAccess('notify', fn() => $this->captureDeprecations( |
| 512 | + fn() => $this->captureOutput(fn() => $smartNull['name']) |
| 513 | + )); |
| 514 | + |
| 515 | + $this->assertInstanceOf(SmartNull::class, $result, 'the read still chains'); |
| 516 | + $this->assertSame($this->expectedEcho(["Replace ['name'] with ->name"]), $this->normalizeCaller($output)); |
| 517 | + $this->assertSame($this->expectedMessages(["Replace ['name'] with ->name"]), $this->normalizeCaller($deprecations)); |
| 518 | + } |
| 519 | + |
| 520 | + #[DataProvider('modeProvider')] |
| 521 | + public function testSmartNullBracketAccessThrowsInThrowMode(string $class): void |
| 522 | + { |
| 523 | + $smartNull = $class::new([])->first(); |
| 524 | + |
| 525 | + $read = $this->withOffsetAccess('throw', fn() => $this->catchThrowable(fn() => $smartNull['name'])); |
| 526 | + $this->assertInstanceOf(RuntimeException::class, $read); |
| 527 | + $this->assertSame("Replace ['name'] with ->name in FILE:LINE.", $this->normalizeCaller($read->getMessage())); |
| 528 | + |
| 529 | + $write = $this->withOffsetAccess('throw', fn() => $this->catchThrowable(fn() => $smartNull['name'] = 'x')); |
| 530 | + $this->assertInstanceOf(RuntimeException::class, $write); |
| 531 | + $this->assertSame("Replace ['name'] with ->name = \$value in FILE:LINE.", $this->normalizeCaller($write->getMessage())); |
| 532 | + |
| 533 | + $unset = $this->withOffsetAccess('throw', fn() => $this->catchThrowable(function () use ($smartNull) { |
| 534 | + unset($smartNull['name']); |
| 535 | + })); |
| 536 | + $this->assertInstanceOf(RuntimeException::class, $unset); |
| 537 | + } |
| 538 | + |
| 539 | + #[DataProvider('modeProvider')] |
| 540 | + public function testSmartNullExistenceChecksStaySilent(string $class): void |
| 541 | + { |
| 542 | + // offsetExists stays silent like SmartArrayBase's, so isset() and ?? |
| 543 | + // don't signal on missing-data paths either - even in throw mode |
| 544 | + $smartNull = $class::new([])->first(); |
| 545 | + |
| 546 | + [[$results, $output], $deprecations] = $this->withOffsetAccess('throw', fn() => $this->captureDeprecations( |
| 547 | + fn() => $this->captureOutput(fn() => [isset($smartNull['name']), $smartNull['name'] ?? 'default']) |
| 548 | + )); |
| 549 | + |
| 550 | + $this->assertSame([false, 'default'], $results); |
| 551 | + $this->assertSame('', $output); |
| 552 | + $this->assertSame([], $deprecations); |
| 553 | + } |
| 554 | + |
500 | 555 | //endregion |
501 | 556 | //region Helpers |
502 | 557 |
|
|
0 commit comments