tmpDir = sys_get_temp_dir() . '/irdb-maxmind-' . bin2hex(random_bytes(6)); mkdir($this->tmpDir); } protected function tearDown(): void { // Clean up the tar file and any generated companions. if ($this->tmpDir !== '' && is_dir($this->tmpDir)) { foreach (glob($this->tmpDir . '/*') ?: [] as $f) { @unlink($f); } @rmdir($this->tmpDir); } } public function testNormalArchivePasses(): void { $phar = $this->buildTar([ 'GeoLite2-Country.mmdb' => 'pretend-mmdb-bytes', 'COPYRIGHT.txt' => 'CC BY-SA 4.0', ]); // Default caps (200 MiB / 400 MiB) — small fixtures pass cleanly. MaxMindDownloader::assertSizeBudget($phar, 'country'); $this->expectNotToPerformAssertions(); } public function testEntryOverPerEntryCapIsRejected(): void { $phar = $this->buildTar([ 'big.bin' => str_repeat('A', 4096), ]); $this->expectException(DownloaderException::class); $this->expectExceptionMessage('uncompressed size 4096 > cap 1024'); // Force the per-entry cap below the entry size. MaxMindDownloader::assertSizeBudget($phar, 'country', maxEntryBytes: 1024); } public function testTotalOverArchiveCapIsRejected(): void { // Three 1 KiB entries individually pass per-entry; the total // breaches the archive cap at the third entry. $phar = $this->buildTar([ 'a.bin' => str_repeat('a', 1024), 'b.bin' => str_repeat('b', 1024), 'c.bin' => str_repeat('c', 1024), ]); $this->expectException(DownloaderException::class); $this->expectExceptionMessage('archive uncompressed size > cap 2048'); MaxMindDownloader::assertSizeBudget( $phar, 'country', maxEntryBytes: 4096, maxTotalBytes: 2048, ); } public function testNestedEntriesAreCounted(): void { // The real MaxMind tarball nests one directory like // `GeoLite2-Country_20260427/GeoLite2-Country.mmdb`. Recursive // iteration must walk into the directory and count nested // entries against both caps. $phar = $this->buildTar([ 'GeoLite2-Country_20260427/GeoLite2-Country.mmdb' => str_repeat('m', 2048), 'GeoLite2-Country_20260427/COPYRIGHT.txt' => 'CC', ]); $this->expectException(DownloaderException::class); $this->expectExceptionMessage('GeoLite2-Country.mmdb uncompressed size 2048 > cap 1024'); MaxMindDownloader::assertSizeBudget( $phar, 'country', maxEntryBytes: 1024, ); } /** * @param array $entries map of entry path → contents */ private function buildTar(array $entries): PharData { $tarPath = $this->tmpDir . '/' . bin2hex(random_bytes(4)) . '.tar'; $writer = new PharData($tarPath); foreach ($entries as $name => $contents) { $writer->addFromString($name, $contents); } // PharData iteration on the *write* handle returns nothing until // the file is closed and re-opened. Production builds the // PharData from an already-on-disk tarball, so this matches the // production path. unset($writer); return new PharData($tarPath); } }