1
0

CidrEvaluatorTest.php 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Tests\Unit\Reputation;
  4. use App\Domain\Ip\Cidr;
  5. use App\Domain\Ip\IpAddress;
  6. use App\Domain\Reputation\CidrEvaluator;
  7. use PHPUnit\Framework\TestCase;
  8. /**
  9. * Unit-level coverage of the in-memory containment math: v4, v6, IPv4
  10. * mapped into v6, and the empty-evaluator no-op shape.
  11. */
  12. final class CidrEvaluatorTest extends TestCase
  13. {
  14. public function testEmptyEvaluatorMatchesNothing(): void
  15. {
  16. $evaluator = new CidrEvaluator([], [], [], []);
  17. $ip = IpAddress::fromString('203.0.113.42');
  18. self::assertFalse($evaluator->isAllowlisted($ip));
  19. self::assertFalse($evaluator->isManuallyBlocked($ip));
  20. self::assertSame([], $evaluator->manualBlockedSubnets());
  21. self::assertSame([], $evaluator->allowlistedSubnets());
  22. }
  23. public function testSingleIpManualBlockMatches(): void
  24. {
  25. $bin = IpAddress::fromString('203.0.113.42')->binary();
  26. $evaluator = new CidrEvaluator([$bin], [], [], []);
  27. self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.113.42')));
  28. self::assertFalse($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.113.43')));
  29. }
  30. public function testV4SubnetContainsHostV4(): void
  31. {
  32. $cidr = Cidr::fromString('203.0.113.0/24');
  33. $evaluator = new CidrEvaluator([], [$cidr], [], []);
  34. self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.113.42')));
  35. self::assertFalse($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.114.42')));
  36. }
  37. public function testV6SubnetContainsV6Host(): void
  38. {
  39. $cidr = Cidr::fromString('2001:db8::/32');
  40. $evaluator = new CidrEvaluator([], [$cidr], [], []);
  41. self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('2001:db8::1')));
  42. self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('2001:db8:abcd::42')));
  43. self::assertFalse($evaluator->isManuallyBlocked(IpAddress::fromString('2001:db9::1')));
  44. }
  45. public function testIpv4MappedV6RoundTripsAsV4(): void
  46. {
  47. // Per IpAddress: ::ffff:203.0.113.42 normalizes to the v4-mapped binary
  48. // of 203.0.113.42 — i.e. it should match a v4 /24 manual block.
  49. $cidr = Cidr::fromString('203.0.113.0/24');
  50. $evaluator = new CidrEvaluator([], [$cidr], [], []);
  51. $ip = IpAddress::fromString('::ffff:203.0.113.42');
  52. self::assertTrue($evaluator->isManuallyBlocked($ip));
  53. }
  54. public function testAllowlistAndBlockOverlapAreIndependent(): void
  55. {
  56. // Both lists "see" the same IP — the evaluator just reports each
  57. // independently. SPEC §5 precedence is the responsibility of the
  58. // caller (EffectiveStatusService).
  59. $bin = IpAddress::fromString('198.51.100.5')->binary();
  60. $evaluator = new CidrEvaluator([$bin], [], [$bin], []);
  61. $ip = IpAddress::fromString('198.51.100.5');
  62. self::assertTrue($evaluator->isManuallyBlocked($ip));
  63. self::assertTrue($evaluator->isAllowlisted($ip));
  64. }
  65. public function testManualBlockedSubnetsExposesSingleEntryForLargeCidr(): void
  66. {
  67. $cidr = Cidr::fromString('10.0.0.0/16');
  68. $evaluator = new CidrEvaluator([], [$cidr], [], []);
  69. self::assertCount(1, $evaluator->manualBlockedSubnets());
  70. self::assertSame('10.0.0.0/16', $evaluator->manualBlockedSubnets()[0]->text());
  71. }
  72. public function testAllowlistedSubnetsExposesEntries(): void
  73. {
  74. $a = Cidr::fromString('10.0.0.0/8');
  75. $b = Cidr::fromString('192.168.0.0/16');
  76. $evaluator = new CidrEvaluator([], [], [], [$a, $b]);
  77. self::assertCount(2, $evaluator->allowlistedSubnets());
  78. }
  79. }