Skip to content

Commit

Permalink
Unit tests for the exponential backoff mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
tomwalder committed Nov 29, 2023
1 parent fe43027 commit a4e528c
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,10 @@ A full suite of unit tests is in the works. Assuming you've installed `php-gds`
```bash
vendor/bin/phpunit
```
Or, if you need to run containerised tests, you can use the `runphp` image (or any you choose)
```bash
docker run --rm -it -v`pwd`:/app -w /app fluentthinking/runphp:7.4.33-v0.9.0 php /app/vendor/bin/phpunit
```

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

Expand Down
134 changes: 134 additions & 0 deletions tests/BackoffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php
/**
* Copyright 2023 Tom Walder
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Tests for exponential backoff
*
* @author Tom Walder <[email protected]>
*/
class BackoffTest extends \PHPUnit\Framework\TestCase
{
public function testOnceAndReturn()
{
\GDS\Gateway::exponentialBackoff(true);
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->addMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->once())
->method('__invoke')
->willReturn(87);
$int_result = $this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
$this->assertEquals(87, $int_result);
}

public function testBackoffCount()
{
\GDS\Gateway::exponentialBackoff(true);
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->addMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->exactly(\GDS\Gateway::RETRY_MAX_ATTEMPTS))
->method('__invoke')
->willThrowException(new \RuntimeException('Test Exception', 503));
$this->expectException(\RuntimeException::class);
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
}

public function testBackoffCountDisabled()
{
\GDS\Gateway::exponentialBackoff(false);
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->addMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->once())
->method('__invoke')
->willThrowException(new \RuntimeException('Not retried', 503));
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Not retried');
$this->expectExceptionCode(503);
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
}

public function testPartialBackoff() {
\GDS\Gateway::exponentialBackoff(true);
$int_calls = 0;
$shouldBeCalled = function () use (&$int_calls) {
$int_calls++;
if ($int_calls < 4) {
throw new \RuntimeException('Always caught', 503);
}
return 42;
};
$int_result = $this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
$this->assertEquals(42, $int_result);
$this->assertEquals(4, $int_calls);
}


public function testIgnoredExceptionClass()
{
\GDS\Gateway::exponentialBackoff(true);
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->addMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->once())
->method('__invoke')
->willThrowException(new \LogicException('Ignored', 503));
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Ignored');
$this->expectExceptionCode(503);
$this->buildTestGateway()->runExecuteWithExponentialBackoff(
$shouldBeCalled,
\RuntimeException::class
);
}

public function testIgnoredExceptionCode()
{
\GDS\Gateway::exponentialBackoff(true);
$shouldBeCalled = $this->getMockBuilder(\stdClass::class)
->addMethods(['__invoke'])
->getMock();
$shouldBeCalled->expects($this->once())
->method('__invoke')
->willThrowException(new \RuntimeException('Non-retry code', 42));
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Non-retry code');
$this->expectExceptionCode(42);
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
}

public function testRetryOnce()
{
\GDS\Gateway::exponentialBackoff(true);
$int_calls = 0;
$shouldBeCalled = function () use (&$int_calls) {
$int_calls++;
throw new \RuntimeException('Once', 500);
};
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Once');
$this->expectExceptionCode(500);
$this->buildTestGateway()->runExecuteWithExponentialBackoff($shouldBeCalled);
$this->assertEquals(2, $int_calls);
}

private function buildTestGateway(): \RESTv1GatewayBackoff
{
return new RESTv1GatewayBackoff('dataset-id', 'my-app');
}
}
13 changes: 13 additions & 0 deletions tests/base/RESTv1GatewayBackoff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

class RESTv1GatewayBackoff extends \GDS\Gateway\RESTv1
{

public function runExecuteWithExponentialBackoff(
callable $fnc_main,
string $str_exception = null,
callable $fnc_resolve_exception = null
) {
return $this->executeWithExponentialBackoff($fnc_main, $str_exception, $fnc_resolve_exception);
}
}
3 changes: 2 additions & 1 deletion tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
require_once(dirname(__FILE__) . '/../vendor/autoload.php');

// Base Test Files
require_once(dirname(__FILE__) . '/base/RESTv1GatewayBackoff.php');
require_once(dirname(__FILE__) . '/base/RESTv1Test.php');
require_once(dirname(__FILE__) . '/base/Simple.php');
require_once(dirname(__FILE__) . '/base/Book.php');
require_once(dirname(__FILE__) . '/base/FakeGuzzleClient.php');
require_once(dirname(__FILE__) . '/base/FakeGuzzleClient.php');

0 comments on commit a4e528c

Please sign in to comment.