Skip to content

Commit 418f73a

Browse files
committed
test: Add test for provider and it strategy
Signed-off-by: tmakinde <[email protected]>
1 parent 3a68951 commit 418f73a

File tree

5 files changed

+778
-0
lines changed

5 files changed

+778
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenFeature\Test\unit;
6+
7+
use Exception;
8+
use Mockery;
9+
use Mockery\MockInterface;
10+
use OpenFeature\Test\TestCase;
11+
use OpenFeature\implementation\flags\EvaluationContext;
12+
use OpenFeature\implementation\multiprovider\Multiprovider;
13+
use OpenFeature\implementation\multiprovider\strategy\ComparisonStrategy;
14+
use OpenFeature\implementation\provider\ResolutionDetailsBuilder;
15+
use OpenFeature\interfaces\provider\Provider;
16+
use OpenFeature\interfaces\provider\ResolutionDetails;
17+
18+
use function count;
19+
20+
class ComparisonStrategyTest extends TestCase
21+
{
22+
/** @var Provider&MockInterface */
23+
private Provider $providerA;
24+
/** @var Provider&MockInterface */
25+
private Provider $providerB;
26+
/** @var Provider&MockInterface */
27+
private Provider $providerC;
28+
29+
protected function setUp(): void
30+
{
31+
parent::setUp();
32+
$this->providerA = Mockery::mock(Provider::class);
33+
$this->providerB = Mockery::mock(Provider::class);
34+
$this->providerC = Mockery::mock(Provider::class);
35+
36+
$this->providerA->shouldReceive('getMetadata->getName')->andReturn('ProviderA');
37+
$this->providerB->shouldReceive('getMetadata->getName')->andReturn('ProviderB');
38+
$this->providerC->shouldReceive('getMetadata->getName')->andReturn('ProviderC');
39+
}
40+
41+
private function details(bool $value): ResolutionDetails
42+
{
43+
return (new ResolutionDetailsBuilder())->withValue($value)->build();
44+
}
45+
46+
public function testAllProvidersAgreeReturnsFirstValue(): void
47+
{
48+
$strategy = new ComparisonStrategy();
49+
$this->providerA->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
50+
$this->providerB->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
51+
$this->providerC->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
52+
53+
$mp = new Multiprovider(
54+
[
55+
['name' => 'a', 'provider' => $this->providerA],
56+
['name' => 'b', 'provider' => $this->providerB],
57+
['name' => 'c', 'provider' => $this->providerC],
58+
],
59+
$strategy,
60+
);
61+
62+
$res = $mp->resolveBooleanValue('flag', false, new EvaluationContext());
63+
$this->assertTrue($res->getValue());
64+
}
65+
66+
public function testMismatchUsesFallbackProvider(): void
67+
{
68+
$strategy = new ComparisonStrategy('b');
69+
$this->providerA->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
70+
$this->providerB->shouldReceive('resolveBooleanValue')->andReturn($this->details(false));
71+
$this->providerC->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
72+
73+
$mp = new Multiprovider(
74+
[
75+
['name' => 'a', 'provider' => $this->providerA],
76+
['name' => 'b', 'provider' => $this->providerB],
77+
['name' => 'c', 'provider' => $this->providerC],
78+
],
79+
$strategy,
80+
);
81+
82+
$res = $mp->resolveBooleanValue('flag', false, new EvaluationContext());
83+
$this->assertFalse($res->getValue());
84+
}
85+
86+
public function testMismatchWithoutFallbackReturnsFirstSuccessful(): void
87+
{
88+
$strategy = new ComparisonStrategy(); // no fallback
89+
$this->providerA->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
90+
$this->providerB->shouldReceive('resolveBooleanValue')->andReturn($this->details(false));
91+
92+
$mp = new Multiprovider(
93+
[
94+
['name' => 'a', 'provider' => $this->providerA],
95+
['name' => 'b', 'provider' => $this->providerB],
96+
],
97+
$strategy,
98+
);
99+
100+
$res = $mp->resolveBooleanValue('flag', false, new EvaluationContext());
101+
$this->assertTrue($res->getValue());
102+
}
103+
104+
public function testOnMismatchCallbackInvoked(): void
105+
{
106+
$invoked = false;
107+
$capturedCount = 0;
108+
$callback = function (array $resolutions) use (&$invoked, &$capturedCount): void {
109+
$invoked = true;
110+
$capturedCount = count($resolutions);
111+
};
112+
113+
$strategy = new ComparisonStrategy(null, $callback);
114+
$this->providerA->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
115+
$this->providerB->shouldReceive('resolveBooleanValue')->andReturn($this->details(false));
116+
$this->providerC->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
117+
118+
$mp = new Multiprovider(
119+
[
120+
['name' => 'a', 'provider' => $this->providerA],
121+
['name' => 'b', 'provider' => $this->providerB],
122+
['name' => 'c', 'provider' => $this->providerC],
123+
],
124+
$strategy,
125+
);
126+
127+
$mp->resolveBooleanValue('flag', false, new EvaluationContext());
128+
$this->assertTrue($invoked);
129+
$this->assertEquals(3, $capturedCount);
130+
}
131+
132+
public function testSingleSuccessfulResult(): void
133+
{
134+
$strategy = new ComparisonStrategy();
135+
$this->providerA->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
136+
$this->providerB->shouldReceive('resolveBooleanValue')->andThrow(new Exception('err'));
137+
$this->providerC->shouldReceive('resolveBooleanValue')->andThrow(new Exception('err2'));
138+
139+
$mp = new Multiprovider(
140+
[
141+
['name' => 'a', 'provider' => $this->providerA],
142+
['name' => 'b', 'provider' => $this->providerB],
143+
['name' => 'c', 'provider' => $this->providerC],
144+
],
145+
$strategy,
146+
);
147+
148+
$res = $mp->resolveBooleanValue('flag', false, new EvaluationContext());
149+
$this->assertTrue($res->getValue());
150+
}
151+
152+
public function testNoSuccessfulResultsReturnsError(): void
153+
{
154+
$strategy = new ComparisonStrategy();
155+
$this->providerA->shouldReceive('resolveBooleanValue')->andThrow(new Exception('a'));
156+
$this->providerB->shouldReceive('resolveBooleanValue')->andThrow(new Exception('b'));
157+
158+
$mp = new Multiprovider(
159+
[
160+
['name' => 'a', 'provider' => $this->providerA],
161+
['name' => 'b', 'provider' => $this->providerB],
162+
],
163+
$strategy,
164+
);
165+
166+
$res = $mp->resolveBooleanValue('flag', false, new EvaluationContext());
167+
$this->assertNotNull($res->getError());
168+
}
169+
170+
public function testMismatchFallbackNotFoundReturnsFirst(): void
171+
{
172+
$strategy = new ComparisonStrategy('non-existent');
173+
$this->providerA->shouldReceive('resolveBooleanValue')->andReturn($this->details(false));
174+
$this->providerB->shouldReceive('resolveBooleanValue')->andReturn($this->details(true));
175+
176+
$mp = new Multiprovider(
177+
[
178+
['name' => 'a', 'provider' => $this->providerA],
179+
['name' => 'b', 'provider' => $this->providerB],
180+
],
181+
$strategy,
182+
);
183+
184+
$res = $mp->resolveBooleanValue('flag', true, new EvaluationContext());
185+
$this->assertFalse($res->getValue());
186+
}
187+
}

tests/unit/FinalResultTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenFeature\Test\unit;
6+
7+
use DateTime;
8+
use Exception;
9+
use OpenFeature\Test\TestCase;
10+
use OpenFeature\implementation\multiprovider\FinalResult;
11+
use OpenFeature\implementation\provider\ResolutionDetailsBuilder;
12+
use OpenFeature\interfaces\provider\ResolutionDetails;
13+
14+
class FinalResultTest extends TestCase
15+
{
16+
/**
17+
* @param bool|string|int|float|DateTime|array<mixed>|null $value
18+
*/
19+
private function details(bool | string | int | float | DateTime | array | null $value): ResolutionDetails
20+
{
21+
return (new ResolutionDetailsBuilder())->withValue($value)->build();
22+
}
23+
24+
public function testSuccessfulResult(): void
25+
{
26+
$details = $this->details(true);
27+
$final = new FinalResult($details, 'ProviderA', null);
28+
29+
$this->assertTrue($final->isSuccessful());
30+
$this->assertFalse($final->hasErrors());
31+
$this->assertSame($details, $final->getDetails());
32+
$this->assertEquals('ProviderA', $final->getProviderName());
33+
$this->assertNull($final->getErrors());
34+
}
35+
36+
public function testResultWithErrors(): void
37+
{
38+
$errors = [
39+
['providerName' => 'ProviderA', 'error' => new Exception('fail A')],
40+
['providerName' => 'ProviderB', 'error' => new Exception('fail B')],
41+
];
42+
$final = new FinalResult(null, null, $errors);
43+
44+
$this->assertFalse($final->isSuccessful());
45+
$this->assertTrue($final->hasErrors());
46+
$this->assertNull($final->getDetails());
47+
$this->assertNull($final->getProviderName());
48+
$errors = $final->getErrors();
49+
$this->assertNotNull($errors);
50+
$this->assertIsArray($errors);
51+
$this->assertCount(2, $errors);
52+
}
53+
54+
public function testEmptyErrorsArrayTreatedAsNoErrors(): void
55+
{
56+
$final = new FinalResult(null, null, []);
57+
$this->assertFalse($final->isSuccessful());
58+
$this->assertFalse($final->hasErrors());
59+
$this->assertSame([], $final->getErrors());
60+
}
61+
}

0 commit comments

Comments
 (0)