From b732f1df0db969d2a33104a17f3de6394f850b8b Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandro@alejandrocelaya.com>
Date: Mon, 12 Aug 2019 20:00:15 +0200
Subject: [PATCH] Moved IpGeolocation module to external library

---
 composer.json                                 |   3 +-
 module/IpGeolocation/LICENSE                  |  21 ---
 module/IpGeolocation/README.md                |  30 ----
 .../config/dependencies.config.php            |  46 ------
 .../IpGeolocation/config/geolite2.config.php  |  35 -----
 module/IpGeolocation/src/ConfigProvider.php   |  14 --
 .../src/Exception/ExceptionInterface.php      |  10 --
 .../src/Exception/RuntimeException.php        |  10 --
 .../src/Exception/WrongIpException.php        |  17 ---
 .../IpGeolocation/src/GeoLite2/DbUpdater.php  | 106 --------------
 .../src/GeoLite2/DbUpdaterInterface.php       |  16 ---
 .../src/GeoLite2/GeoLite2Options.php          |  46 ------
 module/IpGeolocation/src/Model/Location.php   |  80 -----------
 .../src/Resolver/ChainIpLocationResolver.php  |  37 -----
 .../src/Resolver/EmptyIpLocationResolver.php  |  18 ---
 .../src/Resolver/GeoLite2LocationResolver.php |  56 --------
 .../src/Resolver/IpApiLocationResolver.php    |  54 -------
 .../Resolver/IpLocationResolverInterface.php  |  15 --
 .../IpGeolocation/test-resources/.gitignore   |   1 -
 .../test-resources/GeoLite2-City.tar.gz       | Bin 202 -> 0 bytes
 .../IpGeolocation/test/ConfigProviderTest.php |  28 ----
 .../test/Exception/WrongIpExceptionTest.php   |  32 -----
 .../test/GeoLite2/DbUpdaterTest.php           | 132 ------------------
 .../Resolver/ChainIpLocationResolverTest.php  |  77 ----------
 .../Resolver/EmptyIpLocationResolverTest.php  |  41 ------
 .../Resolver/GeoLite2LocationResolverTest.php |  66 ---------
 .../Resolver/IpApiLocationResolverTest.php    |  56 --------
 phpunit.xml.dist                              |   3 -
 28 files changed, 1 insertion(+), 1049 deletions(-)
 delete mode 100644 module/IpGeolocation/LICENSE
 delete mode 100644 module/IpGeolocation/README.md
 delete mode 100644 module/IpGeolocation/config/dependencies.config.php
 delete mode 100644 module/IpGeolocation/config/geolite2.config.php
 delete mode 100644 module/IpGeolocation/src/ConfigProvider.php
 delete mode 100644 module/IpGeolocation/src/Exception/ExceptionInterface.php
 delete mode 100644 module/IpGeolocation/src/Exception/RuntimeException.php
 delete mode 100644 module/IpGeolocation/src/Exception/WrongIpException.php
 delete mode 100644 module/IpGeolocation/src/GeoLite2/DbUpdater.php
 delete mode 100644 module/IpGeolocation/src/GeoLite2/DbUpdaterInterface.php
 delete mode 100644 module/IpGeolocation/src/GeoLite2/GeoLite2Options.php
 delete mode 100644 module/IpGeolocation/src/Model/Location.php
 delete mode 100644 module/IpGeolocation/src/Resolver/ChainIpLocationResolver.php
 delete mode 100644 module/IpGeolocation/src/Resolver/EmptyIpLocationResolver.php
 delete mode 100644 module/IpGeolocation/src/Resolver/GeoLite2LocationResolver.php
 delete mode 100644 module/IpGeolocation/src/Resolver/IpApiLocationResolver.php
 delete mode 100644 module/IpGeolocation/src/Resolver/IpLocationResolverInterface.php
 delete mode 100644 module/IpGeolocation/test-resources/.gitignore
 delete mode 100644 module/IpGeolocation/test-resources/GeoLite2-City.tar.gz
 delete mode 100644 module/IpGeolocation/test/ConfigProviderTest.php
 delete mode 100644 module/IpGeolocation/test/Exception/WrongIpExceptionTest.php
 delete mode 100644 module/IpGeolocation/test/GeoLite2/DbUpdaterTest.php
 delete mode 100644 module/IpGeolocation/test/Resolver/ChainIpLocationResolverTest.php
 delete mode 100644 module/IpGeolocation/test/Resolver/EmptyIpLocationResolverTest.php
 delete mode 100644 module/IpGeolocation/test/Resolver/GeoLite2LocationResolverTest.php
 delete mode 100644 module/IpGeolocation/test/Resolver/IpApiLocationResolverTest.php

diff --git a/composer.json b/composer.json
index 9c110af8..c1ed913e 100644
--- a/composer.json
+++ b/composer.json
@@ -35,6 +35,7 @@
         "predis/predis": "^1.1",
         "shlinkio/shlink-common": "^1.0",
         "shlinkio/shlink-installer": "^1.2.1",
+        "shlinkio/shlink-ip-geolocation": "^1.0",
         "symfony/console": "^4.3",
         "symfony/filesystem": "^4.3",
         "symfony/lock": "^4.3",
@@ -76,7 +77,6 @@
             "Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
             "Shlinkio\\Shlink\\Core\\": "module/Core/src",
             "Shlinkio\\Shlink\\EventDispatcher\\": "module/EventDispatcher/src",
-            "Shlinkio\\Shlink\\IpGeolocation\\": "module/IpGeolocation/src/",
             "Shlinkio\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/src/"
         },
         "files": [
@@ -93,7 +93,6 @@
                 "module/Core/test-db"
             ],
             "ShlinkioTest\\Shlink\\EventDispatcher\\": "module/EventDispatcher/test",
-            "ShlinkioTest\\Shlink\\IpGeolocation\\": "module/IpGeolocation/test",
             "ShlinkioTest\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/test"
         }
     },
diff --git a/module/IpGeolocation/LICENSE b/module/IpGeolocation/LICENSE
deleted file mode 100644
index 31778387..00000000
--- a/module/IpGeolocation/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2019 Alejandro Celaya
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/module/IpGeolocation/README.md b/module/IpGeolocation/README.md
deleted file mode 100644
index 03922172..00000000
--- a/module/IpGeolocation/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Shlink IP Address Geolocation module
-
-Shlink module with tools to geolocate an IP address using different strategies.
-
-Most of the elements it provides require a [PSR-11] container, and it's easy to integrate on [expressive] applications thanks to the `ConfigProvider` it includes.
-
-## Install
-
-Install this library using composer:
-
-    composer require shlinkio/shlink-ip-geolocation
-
-> This library is also an expressive module which provides its own `ConfigProvider`. Add it to your configuration to get everything automatically set up.
-
-## *TODO*
-
-```php
-<?php
-declare(strict_types=1);
-
-return [
-
-    'geolite2' => [
-        'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
-        'temp_dir' => sys_get_temp_dir(),
-        // 'download_from' => 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz',
-    ],
-
-];
-```
diff --git a/module/IpGeolocation/config/dependencies.config.php b/module/IpGeolocation/config/dependencies.config.php
deleted file mode 100644
index 51474dd4..00000000
--- a/module/IpGeolocation/config/dependencies.config.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation;
-
-use GeoIp2\Database\Reader;
-use GuzzleHttp\Client as GuzzleClient;
-use Symfony\Component\Filesystem\Filesystem;
-use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
-use Zend\ServiceManager\Factory\InvokableFactory;
-
-return [
-
-    'dependencies' => [
-        'factories' => [
-            Resolver\IpApiLocationResolver::class => ConfigAbstractFactory::class,
-            Resolver\GeoLite2LocationResolver::class => ConfigAbstractFactory::class,
-            Resolver\EmptyIpLocationResolver::class => InvokableFactory::class,
-            Resolver\ChainIpLocationResolver::class => ConfigAbstractFactory::class,
-
-            GeoLite2\GeoLite2Options::class => ConfigAbstractFactory::class,
-            GeoLite2\DbUpdater::class => ConfigAbstractFactory::class,
-        ],
-        'aliases' => [
-            Resolver\IpLocationResolverInterface::class => Resolver\ChainIpLocationResolver::class,
-        ],
-    ],
-
-    ConfigAbstractFactory::class => [
-        Resolver\IpApiLocationResolver::class => [GuzzleClient::class],
-        Resolver\GeoLite2LocationResolver::class => [Reader::class],
-        Resolver\ChainIpLocationResolver::class => [
-            Resolver\GeoLite2LocationResolver::class,
-            Resolver\IpApiLocationResolver::class,
-            Resolver\EmptyIpLocationResolver::class,
-        ],
-
-        GeoLite2\GeoLite2Options::class => ['config.geolite2'],
-        GeoLite2\DbUpdater::class => [
-            GuzzleClient::class,
-            Filesystem::class,
-            GeoLite2\GeoLite2Options::class,
-        ],
-    ],
-
-];
diff --git a/module/IpGeolocation/config/geolite2.config.php b/module/IpGeolocation/config/geolite2.config.php
deleted file mode 100644
index aa910a50..00000000
--- a/module/IpGeolocation/config/geolite2.config.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation;
-
-use GeoIp2\Database\Reader;
-use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
-use Zend\ServiceManager\Proxy\LazyServiceFactory;
-
-return [
-
-    'dependencies' => [
-        'factories' => [
-            Reader::class => ConfigAbstractFactory::class,
-        ],
-        'delegators' => [
-            // The GeoLite2 db reader has to be lazy so that it does not try to load the DB file at app bootstrapping.
-            // By doing so, it would fail the first time shlink tries to download it.
-            Reader::class => [
-                LazyServiceFactory::class,
-            ],
-        ],
-
-        'lazy_services' => [
-            'class_map' => [
-                Reader::class => Reader::class,
-            ],
-        ],
-    ],
-
-    ConfigAbstractFactory::class => [
-        Reader::class => ['config.geolite2.db_location'],
-    ],
-
-];
diff --git a/module/IpGeolocation/src/ConfigProvider.php b/module/IpGeolocation/src/ConfigProvider.php
deleted file mode 100644
index d4bf15c6..00000000
--- a/module/IpGeolocation/src/ConfigProvider.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation;
-
-use function Shlinkio\Shlink\Common\loadConfigFromGlob;
-
-class ConfigProvider
-{
-    public function __invoke(): array
-    {
-        return loadConfigFromGlob(__DIR__ . '/../config/{,*.}config.php');
-    }
-}
diff --git a/module/IpGeolocation/src/Exception/ExceptionInterface.php b/module/IpGeolocation/src/Exception/ExceptionInterface.php
deleted file mode 100644
index 12b2ccdc..00000000
--- a/module/IpGeolocation/src/Exception/ExceptionInterface.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Exception;
-
-use Throwable;
-
-interface ExceptionInterface extends Throwable
-{
-}
diff --git a/module/IpGeolocation/src/Exception/RuntimeException.php b/module/IpGeolocation/src/Exception/RuntimeException.php
deleted file mode 100644
index d55a5af9..00000000
--- a/module/IpGeolocation/src/Exception/RuntimeException.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Exception;
-
-use RuntimeException as SplRuntimeException;
-
-class RuntimeException extends SplRuntimeException implements ExceptionInterface
-{
-}
diff --git a/module/IpGeolocation/src/Exception/WrongIpException.php b/module/IpGeolocation/src/Exception/WrongIpException.php
deleted file mode 100644
index 8314e662..00000000
--- a/module/IpGeolocation/src/Exception/WrongIpException.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Exception;
-
-use Shlinkio\Shlink\IpGeolocation\Exception\RuntimeException;
-use Throwable;
-
-use function sprintf;
-
-class WrongIpException extends RuntimeException implements ExceptionInterface
-{
-    public static function fromIpAddress($ipAddress, ?Throwable $prev = null): self
-    {
-        return new self(sprintf('Provided IP "%s" is invalid', $ipAddress), 0, $prev);
-    }
-}
diff --git a/module/IpGeolocation/src/GeoLite2/DbUpdater.php b/module/IpGeolocation/src/GeoLite2/DbUpdater.php
deleted file mode 100644
index 8f8af889..00000000
--- a/module/IpGeolocation/src/GeoLite2/DbUpdater.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\GeoLite2;
-
-use Fig\Http\Message\RequestMethodInterface as RequestMethod;
-use GuzzleHttp\ClientInterface;
-use GuzzleHttp\Exception\GuzzleException;
-use GuzzleHttp\RequestOptions;
-use PharData;
-use Shlinkio\Shlink\IpGeolocation\Exception\RuntimeException;
-use Symfony\Component\Filesystem\Exception as FilesystemException;
-use Symfony\Component\Filesystem\Filesystem;
-use Throwable;
-
-use function sprintf;
-
-class DbUpdater implements DbUpdaterInterface
-{
-    private const DB_COMPRESSED_FILE = 'GeoLite2-City.tar.gz';
-    private const DB_DECOMPRESSED_FILE = 'GeoLite2-City.mmdb';
-
-    /** @var ClientInterface */
-    private $httpClient;
-    /** @var Filesystem */
-    private $filesystem;
-    /** @var GeoLite2Options */
-    private $options;
-
-    public function __construct(ClientInterface $httpClient, Filesystem $filesystem, GeoLite2Options $options)
-    {
-        $this->httpClient = $httpClient;
-        $this->filesystem = $filesystem;
-        $this->options = $options;
-    }
-
-    /**
-     * @throws RuntimeException
-     */
-    public function downloadFreshCopy(?callable $handleProgress = null): void
-    {
-        $tempDir = $this->options->getTempDir();
-        $compressedFile = sprintf('%s/%s', $tempDir, self::DB_COMPRESSED_FILE);
-
-        $this->downloadDbFile($compressedFile, $handleProgress);
-        $tempFullPath = $this->extractDbFile($compressedFile, $tempDir);
-        $this->copyNewDbFile($tempFullPath);
-        $this->deleteTempFiles([$compressedFile, $tempFullPath]);
-    }
-
-    private function downloadDbFile(string $dest, ?callable $handleProgress = null): void
-    {
-        try {
-            $this->httpClient->request(RequestMethod::METHOD_GET, $this->options->getDownloadFrom(), [
-                RequestOptions::SINK => $dest,
-                RequestOptions::PROGRESS => $handleProgress,
-            ]);
-        } catch (Throwable | GuzzleException $e) {
-            throw new RuntimeException(
-                'An error occurred while trying to download a fresh copy of the GeoLite2 database',
-                0,
-                $e
-            );
-        }
-    }
-
-    private function extractDbFile(string $compressedFile, string $tempDir): string
-    {
-        try {
-            $phar = new PharData($compressedFile);
-            $internalPathToDb = sprintf('%s/%s', $phar->getBasename(), self::DB_DECOMPRESSED_FILE);
-            $phar->extractTo($tempDir, $internalPathToDb, true);
-
-            return sprintf('%s/%s', $tempDir, $internalPathToDb);
-        } catch (Throwable $e) {
-            throw new RuntimeException(
-                sprintf('An error occurred while trying to extract the GeoLite2 database from %s', $compressedFile),
-                0,
-                $e
-            );
-        }
-    }
-
-    private function copyNewDbFile(string $from): void
-    {
-        try {
-            $this->filesystem->copy($from, $this->options->getDbLocation(), true);
-        } catch (FilesystemException\FileNotFoundException | FilesystemException\IOException $e) {
-            throw new RuntimeException('An error occurred while trying to copy GeoLite2 db file to destination', 0, $e);
-        }
-    }
-
-    private function deleteTempFiles(array $files): void
-    {
-        try {
-            $this->filesystem->remove($files);
-        } catch (FilesystemException\IOException $e) {
-            // Ignore any error produced when trying to delete temp files
-        }
-    }
-
-    public function databaseFileExists(): bool
-    {
-        return $this->filesystem->exists($this->options->getDbLocation());
-    }
-}
diff --git a/module/IpGeolocation/src/GeoLite2/DbUpdaterInterface.php b/module/IpGeolocation/src/GeoLite2/DbUpdaterInterface.php
deleted file mode 100644
index f787dca3..00000000
--- a/module/IpGeolocation/src/GeoLite2/DbUpdaterInterface.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\GeoLite2;
-
-use Shlinkio\Shlink\IpGeolocation\Exception\RuntimeException;
-
-interface DbUpdaterInterface
-{
-    public function databaseFileExists(): bool;
-
-    /**
-     * @throws RuntimeException
-     */
-    public function downloadFreshCopy(?callable $handleProgress = null): void;
-}
diff --git a/module/IpGeolocation/src/GeoLite2/GeoLite2Options.php b/module/IpGeolocation/src/GeoLite2/GeoLite2Options.php
deleted file mode 100644
index 63a7b5b7..00000000
--- a/module/IpGeolocation/src/GeoLite2/GeoLite2Options.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\GeoLite2;
-
-use Zend\Stdlib\AbstractOptions;
-
-class GeoLite2Options extends AbstractOptions
-{
-    private $dbLocation = '';
-    private $tempDir = '';
-    private $downloadFrom = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz';
-
-    public function getDbLocation(): string
-    {
-        return $this->dbLocation;
-    }
-
-    protected function setDbLocation(string $dbLocation): self
-    {
-        $this->dbLocation = $dbLocation;
-        return $this;
-    }
-
-    public function getTempDir(): string
-    {
-        return $this->tempDir;
-    }
-
-    protected function setTempDir(string $tempDir): self
-    {
-        $this->tempDir = $tempDir;
-        return $this;
-    }
-
-    public function getDownloadFrom(): string
-    {
-        return $this->downloadFrom;
-    }
-
-    protected function setDownloadFrom(string $downloadFrom): self
-    {
-        $this->downloadFrom = $downloadFrom;
-        return $this;
-    }
-}
diff --git a/module/IpGeolocation/src/Model/Location.php b/module/IpGeolocation/src/Model/Location.php
deleted file mode 100644
index a41e0cf4..00000000
--- a/module/IpGeolocation/src/Model/Location.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Model;
-
-final class Location
-{
-    /** @var string */
-    private $countryCode;
-    /** @var string */
-    private $countryName;
-    /** @var string */
-    private $regionName;
-    /** @var string */
-    private $city;
-    /** @var float */
-    private $latitude;
-    /** @var float */
-    private $longitude;
-    /** @var string */
-    private $timeZone;
-
-    public function __construct(
-        string $countryCode,
-        string $countryName,
-        string $regionName,
-        string $city,
-        float $latitude,
-        float $longitude,
-        string $timeZone
-    ) {
-        $this->countryCode = $countryCode;
-        $this->countryName = $countryName;
-        $this->regionName = $regionName;
-        $this->city = $city;
-        $this->latitude = $latitude;
-        $this->longitude = $longitude;
-        $this->timeZone = $timeZone;
-    }
-
-    public static function emptyInstance(): self
-    {
-        return new self('', '', '', '', 0.0, 0.0, '');
-    }
-
-    public function countryCode(): string
-    {
-        return $this->countryCode;
-    }
-
-    public function countryName(): string
-    {
-        return $this->countryName;
-    }
-
-    public function regionName(): string
-    {
-        return $this->regionName;
-    }
-
-    public function city(): string
-    {
-        return $this->city;
-    }
-
-    public function latitude(): float
-    {
-        return $this->latitude;
-    }
-
-    public function longitude(): float
-    {
-        return $this->longitude;
-    }
-
-    public function timeZone(): string
-    {
-        return $this->timeZone;
-    }
-}
diff --git a/module/IpGeolocation/src/Resolver/ChainIpLocationResolver.php b/module/IpGeolocation/src/Resolver/ChainIpLocationResolver.php
deleted file mode 100644
index 5efb729c..00000000
--- a/module/IpGeolocation/src/Resolver/ChainIpLocationResolver.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Resolver;
-
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model;
-
-class ChainIpLocationResolver implements IpLocationResolverInterface
-{
-    /** @var IpLocationResolverInterface[] */
-    private $resolvers;
-
-    public function __construct(IpLocationResolverInterface ...$resolvers)
-    {
-        $this->resolvers = $resolvers;
-    }
-
-    /**
-     * @throws WrongIpException
-     */
-    public function resolveIpLocation(string $ipAddress): Model\Location
-    {
-        $error = null;
-
-        foreach ($this->resolvers as $resolver) {
-            try {
-                return $resolver->resolveIpLocation($ipAddress);
-            } catch (WrongIpException $e) {
-                $error = $e;
-            }
-        }
-
-        // If this instruction is reached, it means no resolver was capable of resolving the address
-        throw WrongIpException::fromIpAddress($ipAddress, $error);
-    }
-}
diff --git a/module/IpGeolocation/src/Resolver/EmptyIpLocationResolver.php b/module/IpGeolocation/src/Resolver/EmptyIpLocationResolver.php
deleted file mode 100644
index b0242f68..00000000
--- a/module/IpGeolocation/src/Resolver/EmptyIpLocationResolver.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Resolver;
-
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model;
-
-class EmptyIpLocationResolver implements IpLocationResolverInterface
-{
-    /**
-     * @throws WrongIpException
-     */
-    public function resolveIpLocation(string $ipAddress): Model\Location
-    {
-        return Model\Location::emptyInstance();
-    }
-}
diff --git a/module/IpGeolocation/src/Resolver/GeoLite2LocationResolver.php b/module/IpGeolocation/src/Resolver/GeoLite2LocationResolver.php
deleted file mode 100644
index 725a733a..00000000
--- a/module/IpGeolocation/src/Resolver/GeoLite2LocationResolver.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Resolver;
-
-use GeoIp2\Database\Reader;
-use GeoIp2\Exception\AddressNotFoundException;
-use GeoIp2\Model\City;
-use GeoIp2\Record\Subdivision;
-use MaxMind\Db\Reader\InvalidDatabaseException;
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model;
-
-use function Functional\first;
-
-class GeoLite2LocationResolver implements IpLocationResolverInterface
-{
-    /** @var Reader */
-    private $geoLiteDbReader;
-
-    public function __construct(Reader $geoLiteDbReader)
-    {
-        $this->geoLiteDbReader = $geoLiteDbReader;
-    }
-
-    /**
-     * @throws WrongIpException
-     */
-    public function resolveIpLocation(string $ipAddress): Model\Location
-    {
-        try {
-            $city = $this->geoLiteDbReader->city($ipAddress);
-            return $this->mapFields($city);
-        } catch (AddressNotFoundException $e) {
-            throw WrongIpException::fromIpAddress($ipAddress, $e);
-        } catch (InvalidDatabaseException $e) {
-            throw new WrongIpException('Provided GeoLite2 db file is invalid', 0, $e);
-        }
-    }
-
-    private function mapFields(City $city): Model\Location
-    {
-        /** @var Subdivision $region */
-        $region = first($city->subdivisions);
-
-        return new Model\Location(
-            $city->country->isoCode ?? '',
-            $city->country->name ?? '',
-            $region->name ?? '',
-            $city->city->name ?? '',
-            (float) ($city->location->latitude ?? ''),
-            (float) ($city->location->longitude ?? ''),
-            $city->location->timeZone ?? ''
-        );
-    }
-}
diff --git a/module/IpGeolocation/src/Resolver/IpApiLocationResolver.php b/module/IpGeolocation/src/Resolver/IpApiLocationResolver.php
deleted file mode 100644
index 7478a81f..00000000
--- a/module/IpGeolocation/src/Resolver/IpApiLocationResolver.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Resolver;
-
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
-use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model;
-
-use function Shlinkio\Shlink\Common\json_decode;
-use function sprintf;
-
-class IpApiLocationResolver implements IpLocationResolverInterface
-{
-    private const SERVICE_PATTERN = 'http://ip-api.com/json/%s';
-
-    /** @var Client */
-    private $httpClient;
-
-    public function __construct(Client $httpClient)
-    {
-        $this->httpClient = $httpClient;
-    }
-
-    /**
-     * @throws WrongIpException
-     */
-    public function resolveIpLocation(string $ipAddress): Model\Location
-    {
-        try {
-            $response = $this->httpClient->get(sprintf(self::SERVICE_PATTERN, $ipAddress));
-            return $this->mapFields(json_decode((string) $response->getBody()));
-        } catch (GuzzleException $e) {
-            throw WrongIpException::fromIpAddress($ipAddress, $e);
-        } catch (InvalidArgumentException $e) {
-            throw new WrongIpException('IP-API returned invalid body while locating IP address', 0, $e);
-        }
-    }
-
-    private function mapFields(array $entry): Model\Location
-    {
-        return new Model\Location(
-            (string) ($entry['countryCode'] ?? ''),
-            (string) ($entry['country'] ?? ''),
-            (string) ($entry['regionName'] ?? ''),
-            (string) ($entry['city'] ?? ''),
-            (float) ($entry['lat'] ?? 0.0),
-            (float) ($entry['lon'] ?? 0.0),
-            (string) ($entry['timezone'] ?? '')
-        );
-    }
-}
diff --git a/module/IpGeolocation/src/Resolver/IpLocationResolverInterface.php b/module/IpGeolocation/src/Resolver/IpLocationResolverInterface.php
deleted file mode 100644
index 36938b98..00000000
--- a/module/IpGeolocation/src/Resolver/IpLocationResolverInterface.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Shlinkio\Shlink\IpGeolocation\Resolver;
-
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model;
-
-interface IpLocationResolverInterface
-{
-    /**
-     * @throws WrongIpException
-     */
-    public function resolveIpLocation(string $ipAddress): Model\Location;
-}
diff --git a/module/IpGeolocation/test-resources/.gitignore b/module/IpGeolocation/test-resources/.gitignore
deleted file mode 100644
index 3ffd27ba..00000000
--- a/module/IpGeolocation/test-resources/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-geolite2-testing-db
diff --git a/module/IpGeolocation/test-resources/GeoLite2-City.tar.gz b/module/IpGeolocation/test-resources/GeoLite2-City.tar.gz
deleted file mode 100644
index c945d2954ff2b596fd58f14cfe460588e2d100f8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 202
zcmb2|=3vn2dJ)aQ{Pxm$u0sk84Ta}zTlE80&i=_DyQ}0ir`YDywY<}0tA20OcyYl#
zL*U}~-5gpgYQ*;GIe$r$_-og;O5)k+jW1?qS*Gs}v|gQeTzlc<Ba&;^7)5F13a7Sg
zsf~X!@3383?-V8NziO*LEvwt`{_3xOt#@DM|E#K;H@mGu=55)}Pn(T*9hhuVduM-c
z-OKr}mi$$BTY8Oi<*&*|#r5~<H|jZWof=!alWVDb&2J|LWN@#HY4*~05eym(3;@!s
BU*-S+

diff --git a/module/IpGeolocation/test/ConfigProviderTest.php b/module/IpGeolocation/test/ConfigProviderTest.php
deleted file mode 100644
index a6dbdcbd..00000000
--- a/module/IpGeolocation/test/ConfigProviderTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation;
-
-use PHPUnit\Framework\TestCase;
-use Shlinkio\Shlink\IpGeolocation\ConfigProvider;
-use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
-
-class ConfigProviderTest extends TestCase
-{
-    /** @var ConfigProvider */
-    private $configProvider;
-
-    public function setUp(): void
-    {
-        $this->configProvider = new ConfigProvider();
-    }
-
-    /** @test */
-    public function configIsReturned(): void
-    {
-        $config = $this->configProvider->__invoke();
-
-        $this->assertArrayHasKey('dependencies', $config);
-        $this->assertArrayHasKey(ConfigAbstractFactory::class, $config);
-    }
-}
diff --git a/module/IpGeolocation/test/Exception/WrongIpExceptionTest.php b/module/IpGeolocation/test/Exception/WrongIpExceptionTest.php
deleted file mode 100644
index 0833763d..00000000
--- a/module/IpGeolocation/test/Exception/WrongIpExceptionTest.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation\Exception;
-
-use Exception;
-use PHPUnit\Framework\TestCase;
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-
-class WrongIpExceptionTest extends TestCase
-{
-    /** @test */
-    public function fromIpAddressProperlyCreatesExceptionWithoutPrev(): void
-    {
-        $e = WrongIpException::fromIpAddress('1.2.3.4');
-
-        $this->assertEquals('Provided IP "1.2.3.4" is invalid', $e->getMessage());
-        $this->assertEquals(0, $e->getCode());
-        $this->assertNull($e->getPrevious());
-    }
-
-    /** @test */
-    public function fromIpAddressProperlyCreatesExceptionWithPrev(): void
-    {
-        $prev = new Exception('Previous error');
-        $e = WrongIpException::fromIpAddress('1.2.3.4', $prev);
-
-        $this->assertEquals('Provided IP "1.2.3.4" is invalid', $e->getMessage());
-        $this->assertEquals(0, $e->getCode());
-        $this->assertSame($prev, $e->getPrevious());
-    }
-}
diff --git a/module/IpGeolocation/test/GeoLite2/DbUpdaterTest.php b/module/IpGeolocation/test/GeoLite2/DbUpdaterTest.php
deleted file mode 100644
index 63777440..00000000
--- a/module/IpGeolocation/test/GeoLite2/DbUpdaterTest.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation\GeoLite2;
-
-use GuzzleHttp\ClientInterface;
-use GuzzleHttp\Exception\ClientException;
-use PHPUnit\Framework\TestCase;
-use Prophecy\Argument;
-use Prophecy\Prophecy\ObjectProphecy;
-use Shlinkio\Shlink\IpGeolocation\Exception\RuntimeException;
-use Shlinkio\Shlink\IpGeolocation\GeoLite2\DbUpdater;
-use Shlinkio\Shlink\IpGeolocation\GeoLite2\GeoLite2Options;
-use Symfony\Component\Filesystem\Exception as FilesystemException;
-use Symfony\Component\Filesystem\Filesystem;
-use Zend\Diactoros\Response;
-
-class DbUpdaterTest extends TestCase
-{
-    /** @var DbUpdater */
-    private $dbUpdater;
-    /** @var ObjectProphecy */
-    private $httpClient;
-    /** @var ObjectProphecy */
-    private $filesystem;
-    /** @var GeoLite2Options */
-    private $options;
-
-    public function setUp(): void
-    {
-        $this->httpClient = $this->prophesize(ClientInterface::class);
-        $this->filesystem = $this->prophesize(Filesystem::class);
-        $this->options = new GeoLite2Options([
-            'temp_dir' => __DIR__ . '/../../test-resources',
-            'db_location' => 'db_location',
-            'download_from' => '',
-        ]);
-
-        $this->dbUpdater = new DbUpdater($this->httpClient->reveal(), $this->filesystem->reveal(), $this->options);
-    }
-
-    /** @test */
-    public function anExceptionIsThrownIfFreshDbCannotBeDownloaded(): void
-    {
-        $request = $this->httpClient->request(Argument::cetera())->willThrow(ClientException::class);
-
-        $this->expectException(RuntimeException::class);
-        $this->expectExceptionCode(0);
-        $this->expectExceptionMessage(
-            'An error occurred while trying to download a fresh copy of the GeoLite2 database'
-        );
-        $request->shouldBeCalledOnce();
-
-        $this->dbUpdater->downloadFreshCopy();
-    }
-
-    /** @test */
-    public function anExceptionIsThrownIfFreshDbCannotBeExtracted(): void
-    {
-        $this->options->tempDir = '__invalid__';
-
-        $request = $this->httpClient->request(Argument::cetera())->willReturn(new Response());
-
-        $this->expectException(RuntimeException::class);
-        $this->expectExceptionCode(0);
-        $this->expectExceptionMessage(
-            'An error occurred while trying to extract the GeoLite2 database from __invalid__/GeoLite2-City.tar.gz'
-        );
-        $request->shouldBeCalledOnce();
-
-        $this->dbUpdater->downloadFreshCopy();
-    }
-
-    /**
-     * @test
-     * @dataProvider provideFilesystemExceptions
-     */
-    public function anExceptionIsThrownIfFreshDbCannotBeCopiedToDestination(string $e): void
-    {
-        $request = $this->httpClient->request(Argument::cetera())->willReturn(new Response());
-        $copy = $this->filesystem->copy(Argument::cetera())->willThrow($e);
-
-        $this->expectException(RuntimeException::class);
-        $this->expectExceptionCode(0);
-        $this->expectExceptionMessage('An error occurred while trying to copy GeoLite2 db file to destination');
-        $request->shouldBeCalledOnce();
-        $copy->shouldBeCalledOnce();
-
-        $this->dbUpdater->downloadFreshCopy();
-    }
-
-    public function provideFilesystemExceptions(): iterable
-    {
-        yield 'file not found' => [FilesystemException\FileNotFoundException::class];
-        yield 'IO error' => [FilesystemException\IOException::class];
-    }
-
-    /** @test */
-    public function noExceptionsAreThrownIfEverythingWorksFine(): void
-    {
-        $request = $this->httpClient->request(Argument::cetera())->willReturn(new Response());
-        $copy = $this->filesystem->copy(Argument::cetera())->will(function () {
-        });
-        $remove = $this->filesystem->remove(Argument::cetera())->will(function () {
-        });
-
-        $this->dbUpdater->downloadFreshCopy();
-
-        $request->shouldHaveBeenCalledOnce();
-        $copy->shouldHaveBeenCalledOnce();
-        $remove->shouldHaveBeenCalledOnce();
-    }
-
-    /**
-     * @test
-     * @dataProvider provideExists
-     */
-    public function databaseFileExistsChecksIfTheFilesExistsInTheFilesystem(bool $expected): void
-    {
-        $exists = $this->filesystem->exists('db_location')->willReturn($expected);
-
-        $result = $this->dbUpdater->databaseFileExists();
-
-        $this->assertEquals($expected, $result);
-        $exists->shouldHaveBeenCalledOnce();
-    }
-
-    public function provideExists(): iterable
-    {
-        return [[true], [false]];
-    }
-}
diff --git a/module/IpGeolocation/test/Resolver/ChainIpLocationResolverTest.php b/module/IpGeolocation/test/Resolver/ChainIpLocationResolverTest.php
deleted file mode 100644
index a95b60c0..00000000
--- a/module/IpGeolocation/test/Resolver/ChainIpLocationResolverTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation\Resolver;
-
-use PHPUnit\Framework\TestCase;
-use Prophecy\Prophecy\ObjectProphecy;
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model\Location;
-use Shlinkio\Shlink\IpGeolocation\Resolver\ChainIpLocationResolver;
-use Shlinkio\Shlink\IpGeolocation\Resolver\IpLocationResolverInterface;
-
-class ChainIpLocationResolverTest extends TestCase
-{
-    /** @var ChainIpLocationResolver */
-    private $resolver;
-    /** @var ObjectProphecy */
-    private $firstInnerResolver;
-    /** @var ObjectProphecy */
-    private $secondInnerResolver;
-
-    public function setUp(): void
-    {
-        $this->firstInnerResolver = $this->prophesize(IpLocationResolverInterface::class);
-        $this->secondInnerResolver = $this->prophesize(IpLocationResolverInterface::class);
-
-        $this->resolver = new ChainIpLocationResolver(
-            $this->firstInnerResolver->reveal(),
-            $this->secondInnerResolver->reveal()
-        );
-    }
-
-    /** @test */
-    public function throwsExceptionWhenNoInnerResolverCanHandleTheResolution()
-    {
-        $ipAddress = '1.2.3.4';
-
-        $firstResolve = $this->firstInnerResolver->resolveIpLocation($ipAddress)->willThrow(WrongIpException::class);
-        $secondResolve = $this->secondInnerResolver->resolveIpLocation($ipAddress)->willThrow(WrongIpException::class);
-
-        $this->expectException(WrongIpException::class);
-        $firstResolve->shouldBeCalledOnce();
-        $secondResolve->shouldBeCalledOnce();
-
-        $this->resolver->resolveIpLocation($ipAddress);
-    }
-
-    /** @test */
-    public function returnsResultOfFirstInnerResolver(): void
-    {
-        $ipAddress = '1.2.3.4';
-
-        $firstResolve = $this->firstInnerResolver->resolveIpLocation($ipAddress)->willReturn(Location::emptyInstance());
-        $secondResolve = $this->secondInnerResolver->resolveIpLocation($ipAddress)->willThrow(WrongIpException::class);
-
-        $this->resolver->resolveIpLocation($ipAddress);
-
-        $firstResolve->shouldHaveBeenCalledOnce();
-        $secondResolve->shouldNotHaveBeenCalled();
-    }
-
-    /** @test */
-    public function returnsResultOfSecondInnerResolver(): void
-    {
-        $ipAddress = '1.2.3.4';
-
-        $firstResolve = $this->firstInnerResolver->resolveIpLocation($ipAddress)->willThrow(WrongIpException::class);
-        $secondResolve = $this->secondInnerResolver->resolveIpLocation($ipAddress)->willReturn(
-            Location::emptyInstance()
-        );
-
-        $this->resolver->resolveIpLocation($ipAddress);
-
-        $firstResolve->shouldHaveBeenCalledOnce();
-        $secondResolve->shouldHaveBeenCalledOnce();
-    }
-}
diff --git a/module/IpGeolocation/test/Resolver/EmptyIpLocationResolverTest.php b/module/IpGeolocation/test/Resolver/EmptyIpLocationResolverTest.php
deleted file mode 100644
index 6dda0388..00000000
--- a/module/IpGeolocation/test/Resolver/EmptyIpLocationResolverTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation\Resolver;
-
-use PHPUnit\Framework\TestCase;
-use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
-use Shlinkio\Shlink\IpGeolocation\Model\Location;
-use Shlinkio\Shlink\IpGeolocation\Resolver\EmptyIpLocationResolver;
-
-use function Functional\map;
-use function range;
-
-class EmptyIpLocationResolverTest extends TestCase
-{
-    use StringUtilsTrait;
-
-    /** @var EmptyIpLocationResolver */
-    private $resolver;
-
-    public function setUp(): void
-    {
-        $this->resolver = new EmptyIpLocationResolver();
-    }
-
-    /**
-     * @test
-     * @dataProvider provideEmptyResponses
-     */
-    public function alwaysReturnsAnEmptyLocation(string $ipAddress): void
-    {
-        $this->assertEquals(Location::emptyInstance(), $this->resolver->resolveIpLocation($ipAddress));
-    }
-
-    public function provideEmptyResponses(): array
-    {
-        return map(range(0, 5), function () {
-            return [$this->generateRandomString(15)];
-        });
-    }
-}
diff --git a/module/IpGeolocation/test/Resolver/GeoLite2LocationResolverTest.php b/module/IpGeolocation/test/Resolver/GeoLite2LocationResolverTest.php
deleted file mode 100644
index 806f2759..00000000
--- a/module/IpGeolocation/test/Resolver/GeoLite2LocationResolverTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation\Resolver;
-
-use GeoIp2\Database\Reader;
-use GeoIp2\Exception\AddressNotFoundException;
-use GeoIp2\Model\City;
-use MaxMind\Db\Reader\InvalidDatabaseException;
-use PHPUnit\Framework\TestCase;
-use Prophecy\Prophecy\ObjectProphecy;
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model\Location;
-use Shlinkio\Shlink\IpGeolocation\Resolver\GeoLite2LocationResolver;
-
-class GeoLite2LocationResolverTest extends TestCase
-{
-    /** @var GeoLite2LocationResolver */
-    private $resolver;
-    /** @var ObjectProphecy */
-    private $reader;
-
-    public function setUp(): void
-    {
-        $this->reader = $this->prophesize(Reader::class);
-        $this->resolver = new GeoLite2LocationResolver($this->reader->reveal());
-    }
-
-    /**
-     * @test
-     * @dataProvider provideReaderExceptions
-     */
-    public function exceptionIsThrownIfReaderThrowsException(string $e, string $message): void
-    {
-        $ipAddress = '1.2.3.4';
-
-        $cityMethod = $this->reader->city($ipAddress)->willThrow($e);
-
-        $this->expectException(WrongIpException::class);
-        $this->expectExceptionMessage($message);
-        $this->expectExceptionCode(0);
-        $cityMethod->shouldBeCalledOnce();
-
-        $this->resolver->resolveIpLocation($ipAddress);
-    }
-
-    public function provideReaderExceptions(): iterable
-    {
-        yield 'invalid IP address' => [AddressNotFoundException::class, 'Provided IP "1.2.3.4" is invalid'];
-        yield 'invalid geolite DB' => [InvalidDatabaseException::class, 'Provided GeoLite2 db file is invalid'];
-    }
-
-    /** @test */
-    public function resolvedCityIsProperlyMapped(): void
-    {
-        $ipAddress = '1.2.3.4';
-        $city = new City([]);
-
-        $cityMethod = $this->reader->city($ipAddress)->willReturn($city);
-
-        $result = $this->resolver->resolveIpLocation($ipAddress);
-
-        $this->assertEquals(Location::emptyInstance(), $result);
-        $cityMethod->shouldHaveBeenCalledOnce();
-    }
-}
diff --git a/module/IpGeolocation/test/Resolver/IpApiLocationResolverTest.php b/module/IpGeolocation/test/Resolver/IpApiLocationResolverTest.php
deleted file mode 100644
index a87edf98..00000000
--- a/module/IpGeolocation/test/Resolver/IpApiLocationResolverTest.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace ShlinkioTest\Shlink\IpGeolocation\Resolver;
-
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\TransferException;
-use GuzzleHttp\Psr7\Response;
-use PHPUnit\Framework\TestCase;
-use Prophecy\Prophecy\ObjectProphecy;
-use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
-use Shlinkio\Shlink\IpGeolocation\Model\Location;
-use Shlinkio\Shlink\IpGeolocation\Resolver\IpApiLocationResolver;
-
-use function json_encode;
-
-class IpApiLocationResolverTest extends TestCase
-{
-    /** @var IpApiLocationResolver */
-    private $ipResolver;
-    /** @var ObjectProphecy */
-    private $client;
-
-    public function setUp(): void
-    {
-        $this->client = $this->prophesize(Client::class);
-        $this->ipResolver = new IpApiLocationResolver($this->client->reveal());
-    }
-
-    /** @test */
-    public function correctIpReturnsDecodedInfo(): void
-    {
-        $actual = [
-            'countryCode' => 'bar',
-            'lat' => 5,
-            'lon' => 10,
-        ];
-        $expected = new Location('bar', '', '', '', 5, 10, '');
-        $response = new Response();
-        $response->getBody()->write(json_encode($actual));
-        $response->getBody()->rewind();
-
-        $this->client->get('http://ip-api.com/json/1.2.3.4')->willReturn($response)
-                                                            ->shouldBeCalledOnce();
-        $this->assertEquals($expected, $this->ipResolver->resolveIpLocation('1.2.3.4'));
-    }
-
-    /** @test */
-    public function guzzleExceptionThrowsShlinkException(): void
-    {
-        $this->client->get('http://ip-api.com/json/1.2.3.4')->willThrow(new TransferException())
-                                                            ->shouldBeCalledOnce();
-        $this->expectException(WrongIpException::class);
-        $this->ipResolver->resolveIpLocation('1.2.3.4');
-    }
-}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 516a48fc..bb78e85b 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -18,9 +18,6 @@
         <testsuite name="EventDispatcher">
             <directory>./module/EventDispatcher/test</directory>
         </testsuite>
-        <testsuite name="IpGeolocation">
-            <directory>./module/IpGeolocation/test</directory>
-        </testsuite>
         <testsuite name="PreviewGenerator">
             <directory>./module/PreviewGenerator/test</directory>
         </testsuite>