From 30bf1c2641010df120b50bb51d502b7eb5546184 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 26 Feb 2019 22:31:07 +0100 Subject: [PATCH] Added tests for new cases with non-locatable addresses --- .../Command/Visit/ProcessVisitsCommand.php | 6 +- .../Visit/ProcessVisitsCommandTest.php | 5 + module/Core/src/Entity/VisitLocation.php | 12 +++ .../IpCannotBeLocatedExceptionTest.php | 95 +++++++++++++++++++ module/Core/test/Service/VisitServiceTest.php | 19 +++- 5 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 module/Core/test/Exception/IpCannotBeLocatedExceptionTest.php diff --git a/module/CLI/src/Command/Visit/ProcessVisitsCommand.php b/module/CLI/src/Command/Visit/ProcessVisitsCommand.php index 215977d8..8de39651 100644 --- a/module/CLI/src/Command/Visit/ProcessVisitsCommand.php +++ b/module/CLI/src/Command/Visit/ProcessVisitsCommand.php @@ -65,7 +65,11 @@ class ProcessVisitsCommand extends Command $this->visitService->locateUnlocatedVisits( [$this, 'getGeolocationDataForVisit'], function (VisitLocation $location) use ($output) { - $output->writeln(sprintf(' [Address located at "%s"]', $location->getCountryName())); + if (! $location->isEmpty()) { + $output->writeln( + sprintf(' [Address located at "%s"]', $location->getCountryName()) + ); + } } ); diff --git a/module/CLI/test/Command/Visit/ProcessVisitsCommandTest.php b/module/CLI/test/Command/Visit/ProcessVisitsCommandTest.php index dbb311f2..9c008ed3 100644 --- a/module/CLI/test/Command/Visit/ProcessVisitsCommandTest.php +++ b/module/CLI/test/Command/Visit/ProcessVisitsCommandTest.php @@ -116,6 +116,11 @@ class ProcessVisitsCommandTest extends TestCase $output = $this->commandTester->getDisplay(); $this->assertStringContainsString($message, $output); + if (empty($address)) { + $this->assertStringNotContainsString('Processing IP', $output); + } else { + $this->assertStringContainsString('Processing IP', $output); + } $locateVisits->shouldHaveBeenCalledOnce(); $resolveIpLocation->shouldNotHaveBeenCalled(); } diff --git a/module/Core/src/Entity/VisitLocation.php b/module/Core/src/Entity/VisitLocation.php index b8376c01..fad9c986 100644 --- a/module/Core/src/Entity/VisitLocation.php +++ b/module/Core/src/Entity/VisitLocation.php @@ -72,4 +72,16 @@ class VisitLocation extends AbstractEntity implements VisitLocationInterface 'timezone' => $this->timezone, ]; } + + public function isEmpty(): bool + { + return + $this->countryCode === '' && + $this->countryName === '' && + $this->regionName === '' && + $this->cityName === '' && + ((float) $this->latitude) === 0.0 && + ((float) $this->longitude) === 0.0 && + $this->timezone === ''; + } } diff --git a/module/Core/test/Exception/IpCannotBeLocatedExceptionTest.php b/module/Core/test/Exception/IpCannotBeLocatedExceptionTest.php new file mode 100644 index 00000000..4a00d5b9 --- /dev/null +++ b/module/Core/test/Exception/IpCannotBeLocatedExceptionTest.php @@ -0,0 +1,95 @@ +assertTrue($e->isNonLocatableAddress()); + $this->assertEquals('Ignored visit with no IP address', $e->getMessage()); + $this->assertEquals(0, $e->getCode()); + $this->assertNull($e->getPrevious()); + } + + /** @test */ + public function forLocalhostInitializesException(): void + { + $e = IpCannotBeLocatedException::forLocalhost(); + + $this->assertTrue($e->isNonLocatableAddress()); + $this->assertEquals('Ignored localhost address', $e->getMessage()); + $this->assertEquals(0, $e->getCode()); + $this->assertNull($e->getPrevious()); + } + + /** + * @test + * @dataProvider provideErrors + */ + public function forErrorInitializesException(Throwable $prev): void + { + $e = IpCannotBeLocatedException::forError($prev); + + $this->assertFalse($e->isNonLocatableAddress()); + $this->assertEquals('An error occurred while locating IP', $e->getMessage()); + $this->assertEquals($prev->getCode(), $e->getCode()); + $this->assertSame($prev, $e->getPrevious()); + } + + public function provideErrors(): iterable + { + yield 'Simple exception with positive code' => [new Exception('Some message', 100)]; + yield 'Runtime exception with negative code' => [new RuntimeException('Something went wrong', -50)]; + yield 'Logic exception with default code' => [new LogicException('Conditions unmet')]; + } + + /** + * @test + * @dataProvider provideConstructorArgs + */ + public function constructorInitializesException(): void + { + $args = func_get_args(); + [$isNonLocatableAddress, $message] = $args; + $code = $args[2] ?? 0; + $prev = $args[3] ?? null; + + switch (count($args)) { + case 2: + $e = new IpCannotBeLocatedException($isNonLocatableAddress, $message); + break; + case 3: + $e = new IpCannotBeLocatedException($isNonLocatableAddress, $message, $code); + break; + default: + $e = new IpCannotBeLocatedException($isNonLocatableAddress, $message, $code, $prev); + } + + $this->assertEquals($isNonLocatableAddress, $e->isNonLocatableAddress()); + $this->assertEquals($message, $e->getMessage()); + $this->assertEquals($code, $e->getCode()); + $this->assertEquals($prev, $e->getPrevious()); + } + + public function provideConstructorArgs(): iterable + { + yield 'without default args' => [true, 'Message']; + yield 'without prev' => [true, 'Message', random_int(1, 100)]; + yield 'without all args' => [false, 'Foo', random_int(1, 100), new RuntimeException('Foo')]; + } +} diff --git a/module/Core/test/Service/VisitServiceTest.php b/module/Core/test/Service/VisitServiceTest.php index 46f1da37..a2821fa6 100644 --- a/module/Core/test/Service/VisitServiceTest.php +++ b/module/Core/test/Service/VisitServiceTest.php @@ -70,8 +70,11 @@ class VisitServiceTest extends TestCase $clear->shouldHaveBeenCalledTimes(floor(count($unlocatedVisits) / 200) + 1); } - /** @test */ - public function visitsWhichCannotBeLocatedAreIgnored(): void + /** + * @test + * @dataProvider provideIsNonLocatableAddress + */ + public function visitsWhichCannotBeLocatedAreIgnoredOrLocatedAsEmpty(bool $isNonLocatableAddress): void { $unlocatedVisits = [ new Visit(new ShortUrl('foo'), Visitor::emptyInstance()), @@ -88,14 +91,20 @@ class VisitServiceTest extends TestCase $clear = $this->em->clear()->will(function () { }); - $this->visitService->locateUnlocatedVisits(function () { - throw new IpCannotBeLocatedException(false, 'Cannot be located'); + $this->visitService->locateUnlocatedVisits(function () use ($isNonLocatableAddress) { + throw new IpCannotBeLocatedException($isNonLocatableAddress, 'Cannot be located'); }); $findUnlocatedVisits->shouldHaveBeenCalledOnce(); $getRepo->shouldHaveBeenCalledOnce(); - $persist->shouldNotHaveBeenCalled(); + $persist->shouldHaveBeenCalledTimes($isNonLocatableAddress ? 1 : 0); $flush->shouldHaveBeenCalledOnce(); $clear->shouldHaveBeenCalledOnce(); } + + public function provideIsNonLocatableAddress(): iterable + { + yield 'The address is locatable' => [false]; + yield 'The address is non-locatable' => [true]; + } }