isAllowlisted($ip)); self::assertFalse($evaluator->isManuallyBlocked($ip)); self::assertSame([], $evaluator->manualBlockedSubnets()); self::assertSame([], $evaluator->allowlistedSubnets()); } public function testSingleIpManualBlockMatches(): void { $bin = IpAddress::fromString('203.0.113.42')->binary(); $evaluator = new CidrEvaluator([$bin], [], [], []); self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.113.42'))); self::assertFalse($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.113.43'))); } public function testV4SubnetContainsHostV4(): void { $cidr = Cidr::fromString('203.0.113.0/24'); $evaluator = new CidrEvaluator([], [$cidr], [], []); self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.113.42'))); self::assertFalse($evaluator->isManuallyBlocked(IpAddress::fromString('203.0.114.42'))); } public function testV6SubnetContainsV6Host(): void { $cidr = Cidr::fromString('2001:db8::/32'); $evaluator = new CidrEvaluator([], [$cidr], [], []); self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('2001:db8::1'))); self::assertTrue($evaluator->isManuallyBlocked(IpAddress::fromString('2001:db8:abcd::42'))); self::assertFalse($evaluator->isManuallyBlocked(IpAddress::fromString('2001:db9::1'))); } public function testIpv4MappedV6RoundTripsAsV4(): void { // Per IpAddress: ::ffff:203.0.113.42 normalizes to the v4-mapped binary // of 203.0.113.42 — i.e. it should match a v4 /24 manual block. $cidr = Cidr::fromString('203.0.113.0/24'); $evaluator = new CidrEvaluator([], [$cidr], [], []); $ip = IpAddress::fromString('::ffff:203.0.113.42'); self::assertTrue($evaluator->isManuallyBlocked($ip)); } public function testAllowlistAndBlockOverlapAreIndependent(): void { // Both lists "see" the same IP — the evaluator just reports each // independently. SPEC §5 precedence is the responsibility of the // caller (EffectiveStatusService). $bin = IpAddress::fromString('198.51.100.5')->binary(); $evaluator = new CidrEvaluator([$bin], [], [$bin], []); $ip = IpAddress::fromString('198.51.100.5'); self::assertTrue($evaluator->isManuallyBlocked($ip)); self::assertTrue($evaluator->isAllowlisted($ip)); } public function testManualBlockedSubnetsExposesSingleEntryForLargeCidr(): void { $cidr = Cidr::fromString('10.0.0.0/16'); $evaluator = new CidrEvaluator([], [$cidr], [], []); self::assertCount(1, $evaluator->manualBlockedSubnets()); self::assertSame('10.0.0.0/16', $evaluator->manualBlockedSubnets()[0]->text()); } public function testAllowlistedSubnetsExposesEntries(): void { $a = Cidr::fromString('10.0.0.0/8'); $b = Cidr::fromString('192.168.0.0/16'); $evaluator = new CidrEvaluator([], [], [], [$a, $b]); self::assertCount(2, $evaluator->allowlistedSubnets()); } }