From b24511b7b5bcef350fab21b5a5c88d10b3b8a522 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 14 Apr 2019 10:49:54 +0200 Subject: [PATCH] Created service that updated GeoLite database when it is older than 7 days --- module/CLI/src/Util/GeolocationDbUpdater.php | 60 ++++++++ .../Util/GeolocationDbUpdaterInterface.php | 14 ++ .../test/Util/GeolocationDbUpdaterTest.php | 136 ++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 module/CLI/src/Util/GeolocationDbUpdater.php create mode 100644 module/CLI/src/Util/GeolocationDbUpdaterInterface.php create mode 100644 module/CLI/test/Util/GeolocationDbUpdaterTest.php diff --git a/module/CLI/src/Util/GeolocationDbUpdater.php b/module/CLI/src/Util/GeolocationDbUpdater.php new file mode 100644 index 00000000..c4fb5e7f --- /dev/null +++ b/module/CLI/src/Util/GeolocationDbUpdater.php @@ -0,0 +1,60 @@ +dbUpdater = $dbUpdater; + $this->geoLiteDbReader = $geoLiteDbReader; + } + + /** + * @throws GeolocationDbUpdateFailedException + */ + public function checkDbUpdate(callable $handleProgress = null): void + { + try { + $meta = $this->geoLiteDbReader->metadata(); + if ($this->buildIsOlderThanOneWeek($meta->__get('buildEpoch'))) { + $this->downloadNewDb(true, $handleProgress); + } + } catch (InvalidArgumentException $e) { + // This is the exception thrown by the reader when the database file does not exist + $this->downloadNewDb(false, $handleProgress); + } + } + + private function buildIsOlderThanOneWeek(int $buildTimestamp): bool + { + $buildDate = Chronos::createFromTimestamp($buildTimestamp); + $now = Chronos::now(); + return $now->gt($buildDate->addDays(7)); + } + + /** + * @throws GeolocationDbUpdateFailedException + */ + private function downloadNewDb(bool $olderDbExists, callable $handleProgress = null): void + { + try { + $this->dbUpdater->downloadFreshCopy($handleProgress); + } catch (RuntimeException $e) { + throw GeolocationDbUpdateFailedException::create($olderDbExists, $e); + } + } +} diff --git a/module/CLI/src/Util/GeolocationDbUpdaterInterface.php b/module/CLI/src/Util/GeolocationDbUpdaterInterface.php new file mode 100644 index 00000000..9b3d70d0 --- /dev/null +++ b/module/CLI/src/Util/GeolocationDbUpdaterInterface.php @@ -0,0 +1,14 @@ +dbUpdater = $this->prophesize(DbUpdaterInterface::class); + $this->geoLiteDbReader = $this->prophesize(Reader::class); + + $this->geolocationDbUpdater = new GeolocationDbUpdater( + $this->dbUpdater->reveal(), + $this->geoLiteDbReader->reveal() + ); + } + + /** @test */ + public function exceptionIsThrownWhenOlderDbDoesNotExistAndDownloadFails(): void + { + $getMeta = $this->geoLiteDbReader->metadata()->willThrow(InvalidArgumentException::class); + $prev = new RuntimeException(''); + $download = $this->dbUpdater->downloadFreshCopy(null)->willThrow($prev); + + try { + $this->geolocationDbUpdater->checkDbUpdate(); + $this->assertTrue(false); // If this is reached, the test will fail + } catch (Throwable $e) { + /** @var GeolocationDbUpdateFailedException $e */ + $this->assertInstanceOf(GeolocationDbUpdateFailedException::class, $e); + $this->assertSame($prev, $e->getPrevious()); + $this->assertFalse($e->olderDbExists()); + } + + $getMeta->shouldHaveBeenCalledOnce(); + $download->shouldHaveBeenCalledOnce(); + } + + /** + * @test + * @dataProvider provideDaysBiggerThanSeven + */ + public function exceptionIsThrownWhenOlderDbIsTooOldAndDownloadFails(int $days): void + { + $getMeta = $this->geoLiteDbReader->metadata()->willReturn(new Metadata([ + 'binary_format_major_version' => '', + 'binary_format_minor_version' => '', + 'build_epoch' => Chronos::now()->subDays($days)->getTimestamp(), + 'database_type' => '', + 'languages' => '', + 'description' => '', + 'ip_version' => '', + 'node_count' => 1, + 'record_size' => 4, + ])); + $prev = new RuntimeException(''); + $download = $this->dbUpdater->downloadFreshCopy(null)->willThrow($prev); + + try { + $this->geolocationDbUpdater->checkDbUpdate(); + $this->assertTrue(false); // If this is reached, the test will fail + } catch (Throwable $e) { + /** @var GeolocationDbUpdateFailedException $e */ + $this->assertInstanceOf(GeolocationDbUpdateFailedException::class, $e); + $this->assertSame($prev, $e->getPrevious()); + $this->assertTrue($e->olderDbExists()); + } + + $getMeta->shouldHaveBeenCalledOnce(); + $download->shouldHaveBeenCalledOnce(); + } + + public function provideDaysBiggerThanSeven(): iterable + { + yield [8]; + yield [9]; + yield [10]; + yield [100]; + } + + /** + * @test + * @dataProvider provideDaysSmallerThanSeven + */ + public function databaseIsNotUpdatedIfItIsYoungerThanOneWeek(int $days): void + { + $getMeta = $this->geoLiteDbReader->metadata()->willReturn(new Metadata([ + 'binary_format_major_version' => '', + 'binary_format_minor_version' => '', + 'build_epoch' => Chronos::now()->subDays($days)->getTimestamp(), + 'database_type' => '', + 'languages' => '', + 'description' => '', + 'ip_version' => '', + 'node_count' => 1, + 'record_size' => 4, + ])); + $download = $this->dbUpdater->downloadFreshCopy(null)->will(function () { + }); + + $this->geolocationDbUpdater->checkDbUpdate(); + + $getMeta->shouldHaveBeenCalledOnce(); + $download->shouldNotHaveBeenCalled(); + } + + public function provideDaysSmallerThanSeven(): iterable + { + return map(range(0, 6), function (int $days) { + return [$days]; + }); + } +}