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 $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')), ); } }