| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- <?php
- declare(strict_types=1);
- namespace App\Tests\Integration\Enrichment;
- use App\Application\Jobs\EnrichPendingJob;
- use App\Domain\Ip\IpAddress;
- use App\Domain\Jobs\JobContext;
- use App\Domain\Time\SystemClock;
- use App\Infrastructure\Enrichment\MaxMindRecordAdapter;
- use App\Infrastructure\Enrichment\MmdbEnrichmentService;
- use App\Infrastructure\Reputation\IpEnrichmentRepository;
- use App\Tests\Integration\Support\AppTestCase;
- use Monolog\Handler\TestHandler;
- use Monolog\Logger;
- /**
- * Drives the full enrich-pending flow: seed reports -> run job ->
- * assert that the touched IPs land in `ip_enrichment`.
- */
- final class EnrichPendingJobTest extends AppTestCase
- {
- private const COUNTRY_DB = __DIR__ . '/../../Fixtures/geoip/country.mmdb';
- private const ASN_DB = __DIR__ . '/../../Fixtures/geoip/asn.mmdb';
- public function testEnrichesPendingIps(): void
- {
- // Pre-condition: a single reporter and report row for 81.2.69.142 (in the fixture as GB).
- $reporterId = $this->createReporter('test-rep');
- $categoryId = (int) $this->db->fetchOne("SELECT id FROM categories WHERE slug = 'brute_force'");
- $ip = IpAddress::fromString('81.2.69.142');
- $this->db->insert('reports', [
- 'ip_bin' => $ip->binary(),
- 'ip_text' => $ip->text(),
- 'category_id' => $categoryId,
- 'reporter_id' => $reporterId,
- 'weight_at_report' => '1.0000',
- 'received_at' => (new \DateTimeImmutable('-1 minute', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s'),
- 'metadata_json' => null,
- ], ['ip_bin' => \Doctrine\DBAL\ParameterType::LARGE_OBJECT]);
- // Replace the container's EnrichmentService with one pointed at the fixtures.
- $service = new MmdbEnrichmentService(
- countryDbPath: self::COUNTRY_DB,
- asnDbPath: self::ASN_DB,
- adapter: new MaxMindRecordAdapter(),
- clock: new SystemClock(),
- logger: new Logger('t', [new TestHandler()]),
- );
- /** @var IpEnrichmentRepository $repo */
- $repo = $this->container->get(IpEnrichmentRepository::class);
- $job = new EnrichPendingJob($service, $repo);
- $context = new JobContext(new SystemClock(), new Logger('t', [new TestHandler()]), []);
- $result = $job->run($context);
- self::assertSame(1, $result->itemsProcessed);
- $row = $repo->findByIpBin($ip->binary());
- self::assertNotNull($row);
- self::assertSame('GB', $row['country_code']);
- }
- public function testNoOpWhenDbsAreMissing(): void
- {
- // Use bogus paths.
- $service = new MmdbEnrichmentService(
- countryDbPath: '/no/where/country.mmdb',
- asnDbPath: '/no/where/asn.mmdb',
- adapter: new MaxMindRecordAdapter(),
- clock: new SystemClock(),
- logger: new Logger('t', [new TestHandler()]),
- );
- // Seed something so findPending would otherwise return rows.
- $reporterId = $this->createReporter('test-rep');
- $categoryId = (int) $this->db->fetchOne("SELECT id FROM categories WHERE slug = 'brute_force'");
- $ip = IpAddress::fromString('203.0.113.7');
- $this->db->insert('reports', [
- 'ip_bin' => $ip->binary(),
- 'ip_text' => $ip->text(),
- 'category_id' => $categoryId,
- 'reporter_id' => $reporterId,
- 'weight_at_report' => '1.0000',
- 'received_at' => (new \DateTimeImmutable('-1 minute', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s'),
- 'metadata_json' => null,
- ], ['ip_bin' => \Doctrine\DBAL\ParameterType::LARGE_OBJECT]);
- /** @var IpEnrichmentRepository $repo */
- $repo = $this->container->get(IpEnrichmentRepository::class);
- $job = new EnrichPendingJob($service, $repo);
- $result = $job->run(new JobContext(new SystemClock(), new Logger('t', [new TestHandler()]), []));
- self::assertSame(0, $result->itemsProcessed);
- self::assertNull($repo->findByIpBin($ip->binary()));
- }
- public function testFindPendingExcludesAlreadyEnriched(): void
- {
- $service = new MmdbEnrichmentService(
- countryDbPath: self::COUNTRY_DB,
- asnDbPath: self::ASN_DB,
- adapter: new MaxMindRecordAdapter(),
- clock: new SystemClock(),
- logger: new Logger('t', [new TestHandler()]),
- );
- $reporterId = $this->createReporter('test-rep');
- $categoryId = (int) $this->db->fetchOne("SELECT id FROM categories WHERE slug = 'brute_force'");
- $ip = IpAddress::fromString('81.2.69.142');
- $this->db->insert('reports', [
- 'ip_bin' => $ip->binary(),
- 'ip_text' => $ip->text(),
- 'category_id' => $categoryId,
- 'reporter_id' => $reporterId,
- 'weight_at_report' => '1.0000',
- 'received_at' => (new \DateTimeImmutable('-1 minute', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s'),
- 'metadata_json' => null,
- ], ['ip_bin' => \Doctrine\DBAL\ParameterType::LARGE_OBJECT]);
- /** @var IpEnrichmentRepository $repo */
- $repo = $this->container->get(IpEnrichmentRepository::class);
- $job = new EnrichPendingJob($service, $repo);
- $first = $job->run(new JobContext(new SystemClock(), new Logger('t', [new TestHandler()]), []));
- self::assertSame(1, $first->itemsProcessed);
- // Second run: nothing left to do.
- $second = $job->run(new JobContext(new SystemClock(), new Logger('t', [new TestHandler()]), []));
- self::assertSame(0, $second->itemsProcessed);
- }
- public function testReenrichClearAllowsReprocessing(): void
- {
- $service = new MmdbEnrichmentService(
- countryDbPath: self::COUNTRY_DB,
- asnDbPath: self::ASN_DB,
- adapter: new MaxMindRecordAdapter(),
- clock: new SystemClock(),
- logger: new Logger('t', [new TestHandler()]),
- );
- $reporterId = $this->createReporter('test-rep');
- $categoryId = (int) $this->db->fetchOne("SELECT id FROM categories WHERE slug = 'brute_force'");
- $ip = IpAddress::fromString('81.2.69.142');
- $this->db->insert('reports', [
- 'ip_bin' => $ip->binary(),
- 'ip_text' => $ip->text(),
- 'category_id' => $categoryId,
- 'reporter_id' => $reporterId,
- 'weight_at_report' => '1.0000',
- 'received_at' => (new \DateTimeImmutable('-1 minute', new \DateTimeZone('UTC')))->format('Y-m-d H:i:s'),
- 'metadata_json' => null,
- ], ['ip_bin' => \Doctrine\DBAL\ParameterType::LARGE_OBJECT]);
- /** @var IpEnrichmentRepository $repo */
- $repo = $this->container->get(IpEnrichmentRepository::class);
- $job = new EnrichPendingJob($service, $repo);
- $job->run(new JobContext(new SystemClock(), new Logger('t', [new TestHandler()]), []));
- $cleared = $repo->clearAllEnrichedAt();
- self::assertSame(1, $cleared);
- $reRun = $job->run(new JobContext(new SystemClock(), new Logger('t', [new TestHandler()]), []));
- self::assertSame(1, $reRun->itemsProcessed);
- }
- }
|