Skip to content

Commit a4e528c

Browse files
committed
Unit tests for the exponential backoff mechanism
1 parent fe43027 commit a4e528c

File tree

4 files changed

+153
-1
lines changed

4 files changed

+153
-1
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,10 @@ A full suite of unit tests is in the works. Assuming you've installed `php-gds`
535535
```bash
536536
vendor/bin/phpunit
537537
```
538+
Or, if you need to run containerised tests, you can use the `runphp` image (or any you choose)
539+
```bash
540+
docker run --rm -it -v`pwd`:/app -w /app fluentthinking/runphp:7.4.33-v0.9.0 php /app/vendor/bin/phpunit
541+
```
538542

539543
[Click here for more details](tests/).
540544

tests/BackoffTest.php

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Tom Walder
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* Tests for exponential backoff
20+
*
21+
* @author Tom Walder <[email protected]>
22+
*/
23+
class BackoffTest extends \PHPUnit\Framework\TestCase
24+
{
25+
public function testOnceAndReturn()
26+
{
27+
\GDS\Gateway::exponentialBackoff(true);
28+
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
29+
->addMethods(['__invoke'])
30+
->getMock();
31+
$shouldBeCalled->expects($this->once())
32+
->method('__invoke')
33+
->willReturn(87);
34+
$int_result = $this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
35+
$this->assertEquals(87, $int_result);
36+
}
37+
38+
public function testBackoffCount()
39+
{
40+
\GDS\Gateway::exponentialBackoff(true);
41+
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
42+
->addMethods(['__invoke'])
43+
->getMock();
44+
$shouldBeCalled->expects($this->exactly(\GDS\Gateway::RETRY_MAX_ATTEMPTS))
45+
->method('__invoke')
46+
->willThrowException(new \RuntimeException('Test Exception', 503));
47+
$this->expectException(\RuntimeException::class);
48+
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
49+
}
50+
51+
public function testBackoffCountDisabled()
52+
{
53+
\GDS\Gateway::exponentialBackoff(false);
54+
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
55+
->addMethods(['__invoke'])
56+
->getMock();
57+
$shouldBeCalled->expects($this->once())
58+
->method('__invoke')
59+
->willThrowException(new \RuntimeException('Not retried', 503));
60+
$this->expectException(\RuntimeException::class);
61+
$this->expectExceptionMessage('Not retried');
62+
$this->expectExceptionCode(503);
63+
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
64+
}
65+
66+
public function testPartialBackoff() {
67+
\GDS\Gateway::exponentialBackoff(true);
68+
$int_calls = 0;
69+
$shouldBeCalled = function () use (&$int_calls) {
70+
$int_calls++;
71+
if ($int_calls < 4) {
72+
throw new \RuntimeException('Always caught', 503);
73+
}
74+
return 42;
75+
};
76+
$int_result = $this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
77+
$this->assertEquals(42, $int_result);
78+
$this->assertEquals(4, $int_calls);
79+
}
80+
81+
82+
public function testIgnoredExceptionClass()
83+
{
84+
\GDS\Gateway::exponentialBackoff(true);
85+
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
86+
->addMethods(['__invoke'])
87+
->getMock();
88+
$shouldBeCalled->expects($this->once())
89+
->method('__invoke')
90+
->willThrowException(new \LogicException('Ignored', 503));
91+
$this->expectException(\LogicException::class);
92+
$this->expectExceptionMessage('Ignored');
93+
$this->expectExceptionCode(503);
94+
$this->buildTestGateway()->runExecuteWithExponentialBackoff(
95+
$shouldBeCalled,
96+
\RuntimeException::class
97+
);
98+
}
99+
100+
public function testIgnoredExceptionCode()
101+
{
102+
\GDS\Gateway::exponentialBackoff(true);
103+
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
104+
->addMethods(['__invoke'])
105+
->getMock();
106+
$shouldBeCalled->expects($this->once())
107+
->method('__invoke')
108+
->willThrowException(new \RuntimeException('Non-retry code', 42));
109+
$this->expectException(\RuntimeException::class);
110+
$this->expectExceptionMessage('Non-retry code');
111+
$this->expectExceptionCode(42);
112+
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
113+
}
114+
115+
public function testRetryOnce()
116+
{
117+
\GDS\Gateway::exponentialBackoff(true);
118+
$int_calls = 0;
119+
$shouldBeCalled = function () use (&$int_calls) {
120+
$int_calls++;
121+
throw new \RuntimeException('Once', 500);
122+
};
123+
$this->expectException(\RuntimeException::class);
124+
$this->expectExceptionMessage('Once');
125+
$this->expectExceptionCode(500);
126+
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
127+
$this->assertEquals(2, $int_calls);
128+
}
129+
130+
private function buildTestGateway(): \RESTv1GatewayBackoff
131+
{
132+
return new RESTv1GatewayBackoff('dataset-id', 'my-app');
133+
}
134+
}

tests/base/RESTv1GatewayBackoff.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
class RESTv1GatewayBackoff extends \GDS\Gateway\RESTv1
4+
{
5+
6+
public function runExecuteWithExponentialBackoff(
7+
callable $fnc_main,
8+
string $str_exception = null,
9+
callable $fnc_resolve_exception = null
10+
) {
11+
return $this->executeWithExponentialBackoff($fnc_main, $str_exception, $fnc_resolve_exception);
12+
}
13+
}

tests/bootstrap.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
require_once(dirname(__FILE__) . '/../vendor/autoload.php');
1313

1414
// Base Test Files
15+
require_once(dirname(__FILE__) . '/base/RESTv1GatewayBackoff.php');
1516
require_once(dirname(__FILE__) . '/base/RESTv1Test.php');
1617
require_once(dirname(__FILE__) . '/base/Simple.php');
1718
require_once(dirname(__FILE__) . '/base/Book.php');
18-
require_once(dirname(__FILE__) . '/base/FakeGuzzleClient.php');
19+
require_once(dirname(__FILE__) . '/base/FakeGuzzleClient.php');

0 commit comments

Comments
 (0)