| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- <?php
- declare(strict_types=1);
- namespace App\Tests\Unit\Reputation;
- use App\Domain\Ip\IpAddress;
- use App\Domain\Policy\EvaluationOutcome;
- use App\Domain\Policy\Policy;
- use App\Domain\Policy\PolicyEvaluator;
- use App\Domain\Reputation\CidrEvaluator;
- use DateTimeImmutable;
- use DateTimeZone;
- use PHPUnit\Framework\TestCase;
- /**
- * Unit-level coverage for the score-vs-policy evaluator. Hand-built
- * `CidrEvaluator` snapshots avoid touching the DB.
- */
- final class PolicyEvaluatorTest extends TestCase
- {
- public function testAllowlistShortCircuitsEverything(): void
- {
- $ip = IpAddress::fromString('203.0.113.5');
- $cidr = new CidrEvaluator(
- manualIpBins: [$ip->binary()],
- manualSubnets: [],
- allowlistIpBins: [$ip->binary()],
- allowlistSubnets: [],
- );
- $policy = $this->makePolicy([1 => 0.5], includeManual: true);
- $evaluator = new PolicyEvaluator($policy, $cidr);
- $result = $evaluator->evaluate($ip, [1 => 5.0]);
- self::assertSame(EvaluationOutcome::ExcludedByAllowlist, $result->outcome);
- }
- public function testIncludedByScoreWhenAnyCategoryMeetsThreshold(): void
- {
- $ip = IpAddress::fromString('203.0.113.5');
- $cidr = new CidrEvaluator([], [], [], []);
- $policy = $this->makePolicy([1 => 1.0, 2 => 5.0]);
- $evaluator = new PolicyEvaluator($policy, $cidr);
- $result = $evaluator->evaluate($ip, [1 => 1.5, 2 => 0.1]);
- self::assertSame(EvaluationOutcome::IncludedByScore, $result->outcome);
- self::assertSame([1], $result->matchedCategoryIds);
- self::assertEqualsWithDelta(1.5, $result->maxScore, 1e-6);
- }
- public function testIncludedByScoreWithMultipleMatches(): void
- {
- $ip = IpAddress::fromString('203.0.113.5');
- $cidr = new CidrEvaluator([], [], [], []);
- $policy = $this->makePolicy([1 => 1.0, 2 => 0.5]);
- $evaluator = new PolicyEvaluator($policy, $cidr);
- $result = $evaluator->evaluate($ip, [1 => 2.0, 2 => 3.0]);
- self::assertSame(EvaluationOutcome::IncludedByScore, $result->outcome);
- self::assertSame([1, 2], $result->matchedCategoryIds);
- self::assertEqualsWithDelta(3.0, $result->maxScore, 1e-6);
- }
- public function testIncludedByManualBlockOnlyWhenPolicyIncludesIt(): void
- {
- $ip = IpAddress::fromString('203.0.113.5');
- $cidr = new CidrEvaluator(
- manualIpBins: [$ip->binary()],
- manualSubnets: [],
- allowlistIpBins: [],
- allowlistSubnets: [],
- );
- $policyIncluding = $this->makePolicy([1 => 5.0], includeManual: true);
- $policyExcluding = $this->makePolicy([1 => 5.0], includeManual: false);
- $including = (new PolicyEvaluator($policyIncluding, $cidr))->evaluate($ip, [1 => 0.0]);
- $excluding = (new PolicyEvaluator($policyExcluding, $cidr))->evaluate($ip, [1 => 0.0]);
- self::assertSame(EvaluationOutcome::IncludedByManualBlock, $including->outcome);
- self::assertSame(EvaluationOutcome::Excluded, $excluding->outcome);
- }
- public function testCategoryAbsentFromPolicyIsIgnored(): void
- {
- $ip = IpAddress::fromString('203.0.113.5');
- $cidr = new CidrEvaluator([], [], [], []);
- // policy only cares about category 1; even a sky-high score on
- // category 2 must be ignored.
- $policy = $this->makePolicy([1 => 1.0]);
- $evaluator = new PolicyEvaluator($policy, $cidr);
- $result = $evaluator->evaluate($ip, [2 => 999.0]);
- self::assertSame(EvaluationOutcome::Excluded, $result->outcome);
- }
- /**
- * @param array<int, float> $thresholds
- */
- private function makePolicy(array $thresholds, bool $includeManual = true): Policy
- {
- return new Policy(
- id: 1,
- name: 'test',
- description: null,
- includeManualBlocks: $includeManual,
- thresholds: $thresholds,
- createdAt: new DateTimeImmutable('now', new DateTimeZone('UTC')),
- );
- }
- }
|