|
8 | 8 | use LaunchDarkly\LDUser; |
9 | 9 | use LaunchDarkly\LDUserBuilder; |
10 | 10 | use LaunchDarkly\Segment; |
| 11 | +use LaunchDarkly\VariationOrRollout; |
11 | 12 |
|
12 | 13 | const RULE_ID = 'ruleid'; |
13 | 14 |
|
@@ -610,6 +611,80 @@ public function testFlagReturnsErrorIfRuleHasRolloutWithNoVariations() |
610 | 611 | self::assertEquals(array(), $result->getPrerequisiteEvents()); |
611 | 612 | } |
612 | 613 |
|
| 614 | + public function testRolloutSelectsBucket() |
| 615 | + { |
| 616 | + $ub = new LDUserBuilder('userkey'); |
| 617 | + $user = $ub->build(); |
| 618 | + $flagKey = 'flagkey'; |
| 619 | + $salt = 'salt'; |
| 620 | + |
| 621 | + // First verify that with our test inputs, the bucket value will be greater than zero and less than 100000, |
| 622 | + // so we can construct a rollout whose second bucket just barely contains that value |
| 623 | + $bucketValue = floor(VariationOrRollout::bucketUser($user, $flagKey, "key", $salt) * 100000); |
| 624 | + self::assertGreaterThan(0, $bucketValue); |
| 625 | + self::assertLessThan(100000, $bucketValue); |
| 626 | + |
| 627 | + $badVariationA = 0; |
| 628 | + $matchedVariation = 1; |
| 629 | + $badVariationB = 2; |
| 630 | + $rollout = array( |
| 631 | + 'variations' => array( |
| 632 | + array('variation' => $badVariationA, 'weight' => $bucketValue), // end of bucket range is not inclusive, so it will *not* match the target value |
| 633 | + array('variation' => $matchedVariation, 'weight' => 1), // size of this bucket is 1, so it only matches that specific value |
| 634 | + array('variation' => $badVariationB, 'weight' => 100000 - ($bucketValue + 1)) |
| 635 | + ) |
| 636 | + ); |
| 637 | + $flag = FeatureFlag::decode(array( |
| 638 | + 'key' => $flagKey, |
| 639 | + 'version' => 1, |
| 640 | + 'deleted' => false, |
| 641 | + 'on' => true, |
| 642 | + 'offVariation' => null, |
| 643 | + 'targets' => array(), |
| 644 | + 'prerequisites' => array(), |
| 645 | + 'rules' => array(), |
| 646 | + 'fallthrough' => array('rollout' => $rollout), |
| 647 | + 'variations' => array('', '', ''), |
| 648 | + 'salt' => $salt |
| 649 | + )); |
| 650 | + |
| 651 | + $result = $flag->evaluate($user, null, static::$eventFactory); |
| 652 | + self::assertSame($matchedVariation, $result->getDetail()->getVariationIndex()); |
| 653 | + } |
| 654 | + |
| 655 | + public function testRolloutSelectsLastBucketIfBucketValueEqualsTotalWeight() |
| 656 | + { |
| 657 | + $ub = new LDUserBuilder('userkey'); |
| 658 | + $user = $ub->build(); |
| 659 | + $flagKey = 'flagkey'; |
| 660 | + $salt = 'salt'; |
| 661 | + |
| 662 | + $bucketValue = floor(VariationOrRollout::bucketUser($user, $flagKey, "key", $salt) * 100000); |
| 663 | + |
| 664 | + // We'll construct a list of variations that stops right at the target bucket value |
| 665 | + $rollout = array( |
| 666 | + 'variations' => array( |
| 667 | + array('variation' => 0, 'weight' => $bucketValue) |
| 668 | + ) |
| 669 | + ); |
| 670 | + $flag = FeatureFlag::decode(array( |
| 671 | + 'key' => $flagKey, |
| 672 | + 'version' => 1, |
| 673 | + 'deleted' => false, |
| 674 | + 'on' => true, |
| 675 | + 'offVariation' => null, |
| 676 | + 'targets' => array(), |
| 677 | + 'prerequisites' => array(), |
| 678 | + 'rules' => array(), |
| 679 | + 'fallthrough' => array('rollout' => $rollout), |
| 680 | + 'variations' => array(''), |
| 681 | + 'salt' => $salt |
| 682 | + )); |
| 683 | + |
| 684 | + $result = $flag->evaluate($user, null, static::$eventFactory); |
| 685 | + self::assertSame(0, $result->getDetail()->getVariationIndex()); |
| 686 | + } |
| 687 | + |
613 | 688 | public function testRolloutCalculationBucketsByUserKeyByDefault() |
614 | 689 | { |
615 | 690 | $ub = new LDUserBuilder('userkey'); |
|
0 commit comments