Skip to content

Commit dd766e9

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

File tree

2 files changed

+459
-0
lines changed

2 files changed

+459
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenFeature\Test\unit;
6+
7+
use DateTime;
8+
use Exception;
9+
use Mockery;
10+
use OpenFeature\Test\TestCase;
11+
use OpenFeature\implementation\flags\EvaluationContext;
12+
use OpenFeature\implementation\multiprovider\ProviderResolutionResult;
13+
use OpenFeature\implementation\multiprovider\strategy\FirstMatchStrategy;
14+
use OpenFeature\implementation\multiprovider\strategy\FirstSuccessfulStrategy;
15+
use OpenFeature\implementation\multiprovider\strategy\StrategyEvaluationContext;
16+
use OpenFeature\implementation\multiprovider\strategy\StrategyPerProviderContext;
17+
use OpenFeature\implementation\provider\ResolutionDetailsBuilder;
18+
use OpenFeature\implementation\provider\ResolutionError;
19+
use OpenFeature\interfaces\provider\ErrorCode;
20+
use OpenFeature\interfaces\provider\Provider;
21+
use OpenFeature\interfaces\provider\ResolutionDetails;
22+
use OpenFeature\interfaces\provider\ThrowableWithResolutionError;
23+
24+
class MultiProviderStrategyTest extends TestCase
25+
{
26+
private Provider $mockProvider1;
27+
private Provider $mockProvider2;
28+
private Provider $mockProvider3;
29+
private StrategyEvaluationContext $baseContext;
30+
31+
protected function setUp(): void
32+
{
33+
parent::setUp();
34+
$this->mockProvider1 = Mockery::mock(Provider::class);
35+
$this->mockProvider2 = Mockery::mock(Provider::class);
36+
$this->mockProvider3 = Mockery::mock(Provider::class);
37+
38+
// Setup basic metadata for providers
39+
$this->mockProvider1->shouldReceive('getMetadata->getName')->andReturn('Provider1');
40+
$this->mockProvider2->shouldReceive('getMetadata->getName')->andReturn('Provider2');
41+
$this->mockProvider3->shouldReceive('getMetadata->getName')->andReturn('Provider3');
42+
43+
// Create base evaluation context for tests
44+
$this->baseContext = new StrategyEvaluationContext(
45+
'test-flag',
46+
'boolean',
47+
false,
48+
new EvaluationContext(),
49+
);
50+
}
51+
52+
public function testFirstMatchStrategyRunMode(): void
53+
{
54+
$strategy = new FirstMatchStrategy();
55+
$this->assertEquals('sequential', $strategy->runMode);
56+
}
57+
58+
public function testFirstSuccessfulStrategyRunMode(): void
59+
{
60+
$strategy = new FirstSuccessfulStrategy();
61+
$this->assertEquals('sequential', $strategy->runMode);
62+
}
63+
64+
public function testFirstMatchStrategyShouldEvaluateThisProvider(): void
65+
{
66+
$strategy = new FirstMatchStrategy();
67+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
68+
69+
$this->assertTrue($strategy->shouldEvaluateThisProvider($context));
70+
}
71+
72+
public function testFirstSuccessfulStrategyShouldEvaluateThisProvider(): void
73+
{
74+
$strategy = new FirstSuccessfulStrategy();
75+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
76+
77+
$this->assertTrue($strategy->shouldEvaluateThisProvider($context));
78+
}
79+
80+
public function testFirstMatchStrategyWithSuccessfulResult(): void
81+
{
82+
$strategy = new FirstMatchStrategy();
83+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
84+
85+
$details = $this->createResolutionDetails(true);
86+
$result = new ProviderResolutionResult('test1', $this->mockProvider1, $details, null);
87+
88+
$this->assertFalse($strategy->shouldEvaluateNextProvider($context, $result));
89+
}
90+
91+
public function testFirstMatchStrategyWithFlagNotFoundError(): void
92+
{
93+
$strategy = new FirstMatchStrategy();
94+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
95+
96+
$error = new class extends Exception implements ThrowableWithResolutionError {
97+
public function getResolutionError(): \OpenFeature\interfaces\provider\ResolutionError
98+
{
99+
return new ResolutionError(ErrorCode::FLAG_NOT_FOUND(), 'Flag not found');
100+
}
101+
};
102+
103+
$result = new ProviderResolutionResult('test1', $this->mockProvider1, null, $error);
104+
105+
$this->assertTrue($strategy->shouldEvaluateNextProvider($context, $result));
106+
}
107+
108+
public function testFirstMatchStrategyWithGeneralError(): void
109+
{
110+
$strategy = new FirstMatchStrategy();
111+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
112+
113+
$error = new class extends Exception implements ThrowableWithResolutionError {
114+
public function getResolutionError(): \OpenFeature\interfaces\provider\ResolutionError
115+
{
116+
return new ResolutionError(ErrorCode::GENERAL(), 'General error');
117+
}
118+
};
119+
120+
$result = new ProviderResolutionResult('test1', $this->mockProvider1, null, $error);
121+
122+
$this->assertFalse($strategy->shouldEvaluateNextProvider($context, $result));
123+
}
124+
125+
public function testFirstSuccessfulStrategyWithSuccessfulResult(): void
126+
{
127+
$strategy = new FirstSuccessfulStrategy();
128+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
129+
130+
$details = $this->createResolutionDetails(true);
131+
$result = new ProviderResolutionResult('test1', $this->mockProvider1, $details, null);
132+
133+
$this->assertFalse($strategy->shouldEvaluateNextProvider($context, $result));
134+
}
135+
136+
public function testFirstSuccessfulStrategyWithError(): void
137+
{
138+
$strategy = new FirstSuccessfulStrategy();
139+
$context = new StrategyPerProviderContext($this->baseContext, 'test1', $this->mockProvider1);
140+
141+
$error = new Exception('Test error');
142+
$result = new ProviderResolutionResult('test1', $this->mockProvider1, null, $error);
143+
144+
$this->assertTrue($strategy->shouldEvaluateNextProvider($context, $result));
145+
}
146+
147+
public function testFirstMatchStrategyDetermineFinalResultSuccess(): void
148+
{
149+
$strategy = new FirstMatchStrategy();
150+
151+
$details1 = $this->createResolutionDetails(true);
152+
$result1 = new ProviderResolutionResult('test1', $this->mockProvider1, $details1, null);
153+
154+
$finalResult = $strategy->determineFinalResult($this->baseContext, [$result1]);
155+
156+
$this->assertTrue($finalResult->isSuccessful());
157+
$this->assertEquals('test1', $finalResult->getProviderName());
158+
$this->assertSame($details1, $finalResult->getDetails());
159+
}
160+
161+
public function testFirstMatchStrategyDetermineFinalResultAllFlagNotFound(): void
162+
{
163+
$strategy = new FirstMatchStrategy();
164+
165+
$error = new class extends Exception implements ThrowableWithResolutionError {
166+
public function getResolutionError(): \OpenFeature\interfaces\provider\ResolutionError
167+
{
168+
return new ResolutionError(ErrorCode::FLAG_NOT_FOUND(), 'Flag not found');
169+
}
170+
};
171+
172+
$result1 = new ProviderResolutionResult('test1', $this->mockProvider1, null, $error);
173+
$result2 = new ProviderResolutionResult('test2', $this->mockProvider2, null, $error);
174+
175+
$finalResult = $strategy->determineFinalResult($this->baseContext, [$result1, $result2]);
176+
177+
$this->assertFalse($finalResult->isSuccessful());
178+
$this->assertNotNull($finalResult->getErrors());
179+
}
180+
181+
public function testFirstSuccessfulStrategyDetermineFinalResultSuccess(): void
182+
{
183+
$strategy = new FirstSuccessfulStrategy();
184+
185+
$error = new Exception('Test error');
186+
$result1 = new ProviderResolutionResult('test1', $this->mockProvider1, null, $error);
187+
188+
$details2 = $this->createResolutionDetails(true);
189+
$result2 = new ProviderResolutionResult('test2', $this->mockProvider2, $details2, null);
190+
191+
$finalResult = $strategy->determineFinalResult($this->baseContext, [$result1, $result2]);
192+
193+
$this->assertTrue($finalResult->isSuccessful());
194+
$this->assertEquals('test2', $finalResult->getProviderName());
195+
$this->assertSame($details2, $finalResult->getDetails());
196+
}
197+
198+
public function testFirstSuccessfulStrategyDetermineFinalResultAllErrors(): void
199+
{
200+
$strategy = new FirstSuccessfulStrategy();
201+
202+
$error1 = new Exception('Error 1');
203+
$error2 = new Exception('Error 2');
204+
205+
$result1 = new ProviderResolutionResult('test1', $this->mockProvider1, null, $error1);
206+
$result2 = new ProviderResolutionResult('test2', $this->mockProvider2, null, $error2);
207+
208+
$finalResult = $strategy->determineFinalResult($this->baseContext, [$result1, $result2]);
209+
/** @var ThrowableWithResolutionError[] $error */
210+
$error = $finalResult->getErrors();
211+
$this->assertFalse($finalResult->isSuccessful());
212+
$this->assertCount(2, $error);
213+
}
214+
215+
/**
216+
* @param bool|string|int|float|DateTime|array<mixed>|null $value
217+
*/
218+
private function createResolutionDetails(bool | string | int | float | DateTime | array | null $value): ResolutionDetails
219+
{
220+
return (new ResolutionDetailsBuilder())
221+
->withValue($value)
222+
->build();
223+
}
224+
}

0 commit comments

Comments
 (0)