makeService(self::COUNTRY_DB, self::ASN_DB); $result = $service->enrich(IpAddress::fromString('81.2.69.142')); self::assertSame('GB', $result->countryCode); // The fixture's ASN db doesn't carry every test IP — country alone is the load-bearing assertion. } public function testIpv6Lookup(): void { $service = $this->makeService(self::COUNTRY_DB, self::ASN_DB); // The MaxMind fixture covers v4-mapped v6 of 81.2.69.142. $result = $service->enrich(IpAddress::fromString('::ffff:81.2.69.142')); self::assertSame('GB', $result->countryCode); } public function testUnknownIpReturnsEmpty(): void { $service = $this->makeService(self::COUNTRY_DB, self::ASN_DB); // A reserved-but-not-in-fixture IP. $result = $service->enrich(IpAddress::fromString('192.0.2.99')); self::assertNull($result->countryCode); self::assertNull($result->asn); self::assertTrue($result->isEmpty()); } public function testMissingDbWarnsOnceAndReturnsEmpty(): void { $logger = new Logger('t'); $handler = new TestHandler(); $logger->pushHandler($handler); $service = new MmdbEnrichmentService( countryDbPath: '/nonexistent/country.mmdb', asnDbPath: '/nonexistent/asn.mmdb', adapter: new MaxMindRecordAdapter(), clock: new SystemClock(), logger: $logger, ); // Two lookups should still produce only one warning. $a = $service->enrich(IpAddress::fromString('1.2.3.4')); $b = $service->enrich(IpAddress::fromString('5.6.7.8')); self::assertTrue($a->isEmpty()); self::assertTrue($b->isEmpty()); self::assertFalse($service->isReady()); $messages = array_map(static fn ($r): string => $r->message, $handler->getRecords()); $matches = array_filter($messages, static fn (string $m): bool => $m === 'geoip_db_unavailable'); self::assertCount(1, $matches); } public function testReloadReadersClearsState(): void { $service = $this->makeService(self::COUNTRY_DB, self::ASN_DB); $service->enrich(IpAddress::fromString('81.2.69.142')); $service->reloadReaders(); // Should still work after reload. $result = $service->enrich(IpAddress::fromString('81.2.69.142')); self::assertSame('GB', $result->countryCode); } private function makeService(string $country, string $asn, ?Clock $clock = null): MmdbEnrichmentService { $logger = new Logger('t'); $logger->pushHandler(new TestHandler()); return new MmdbEnrichmentService( countryDbPath: $country, asnDbPath: $asn, adapter: new MaxMindRecordAdapter(), clock: $clock ?? new SystemClock(), logger: $logger, ); } }