first commit

This commit is contained in:
2024-11-11 18:46:54 +01:00
commit a630d17338
25634 changed files with 4923715 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace Tests\PrestaShop\CircuitBreaker;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\AdvancedCircuitBreaker;
use PrestaShop\CircuitBreaker\AdvancedCircuitBreakerFactory;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Contract\FactorySettingsInterface;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
use PrestaShop\CircuitBreaker\FactorySettings;
use PrestaShop\CircuitBreaker\State;
use PrestaShop\CircuitBreaker\Transition;
class AdvancedCircuitBreakerFactoryTest extends TestCase
{
/**
* @dataProvider getSettings
*
* @param FactorySettingsInterface $settings the Circuit Breaker settings
*
* @return void
*/
public function testCircuitBreakerCreation(FactorySettingsInterface $settings)
{
$factory = new AdvancedCircuitBreakerFactory();
$circuitBreaker = $factory->create($settings);
$this->assertInstanceOf(AdvancedCircuitBreaker::class, $circuitBreaker);
}
public function testCircuitBreakerWithDispatcher()
{
$dispatcher = $this->getMockBuilder(TransitionDispatcherInterface::class)
->disableOriginalConstructor()
->getMock()
;
$localeService = 'file://' . __FILE__;
$expectedParameters = ['toto' => 'titi', 42 => 51];
$dispatcher
->expects($this->at(0))
->method('dispatchTransition')
->with(
$this->equalTo(Transition::INITIATING_TRANSITION),
$this->equalTo($localeService),
$this->equalTo([])
)
;
$dispatcher
->expects($this->at(1))
->method('dispatchTransition')
->with(
$this->equalTo(Transition::TRIAL_TRANSITION),
$this->equalTo($localeService),
$this->equalTo($expectedParameters)
)
;
$factory = new AdvancedCircuitBreakerFactory();
$settings = new FactorySettings(2, 0.1, 10);
$settings
->setStrippedTimeout(0.2)
->setDispatcher($dispatcher)
;
$circuitBreaker = $factory->create($settings);
$this->assertInstanceOf(AdvancedCircuitBreaker::class, $circuitBreaker);
$circuitBreaker->call($localeService, $expectedParameters, function () {
});
}
public function testCircuitBreakerWithStorage()
{
$storage = $this->getMockBuilder(StorageInterface::class)
->disableOriginalConstructor()
->getMock()
;
$factory = new AdvancedCircuitBreakerFactory();
$settings = new FactorySettings(2, 0.1, 10);
$settings
->setStrippedTimeout(0.2)
->setStorage($storage)
;
$circuitBreaker = $factory->create($settings);
$this->assertInstanceOf(AdvancedCircuitBreaker::class, $circuitBreaker);
}
public function testCircuitBreakerWithDefaultFallback()
{
$factory = new AdvancedCircuitBreakerFactory();
$settings = new FactorySettings(2, 0.1, 10);
$settings->setDefaultFallback(function () {
return 'default_fallback';
});
$circuitBreaker = $factory->create($settings);
$this->assertInstanceOf(AdvancedCircuitBreaker::class, $circuitBreaker);
$response = $circuitBreaker->call('unknown_service');
$this->assertEquals(State::OPEN_STATE, $circuitBreaker->getState());
$this->assertEquals('default_fallback', $response);
}
/**
* @return array
*/
public function getSettings()
{
return [
[
(new FactorySettings(2, 0.1, 10))
->setStrippedTimeout(0.2)
->setClientOptions(['proxy' => '192.168.16.1:10']),
],
[
(new FactorySettings(2, 0.1, 10))
->setStrippedTimeout(0.2)
->setClient(new GuzzleClient(['proxy' => '192.168.16.1:10'])),
],
];
}
}

View File

@@ -0,0 +1,289 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace Tests\PrestaShop\CircuitBreaker;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Message\Request;
use PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream;
use GuzzleHttp\Subscriber\Mock;
use PHPUnit_Framework_MockObject_MockObject;
use PrestaShop\CircuitBreaker\AdvancedCircuitBreaker;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Contract\TransitionDispatcherInterface;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use PrestaShop\CircuitBreaker\State;
use PrestaShop\CircuitBreaker\Storage\SymfonyCache;
use PrestaShop\CircuitBreaker\System\MainSystem;
use PrestaShop\CircuitBreaker\Transition\NullDispatcher;
use Symfony\Component\Cache\Simple\ArrayCache;
class AdvancedCircuitBreakerTest extends CircuitBreakerTestCase
{
/**
* Used to track the dispatched events.
*
* @var PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
*/
private $spy;
/**
* We should see the circuit breaker initialized,
* a call being done and then the circuit breaker closed.
*/
public function testCircuitBreakerEventsOnFirstFailedCall()
{
$circuitBreaker = $this->createCircuitBreaker();
$circuitBreaker->call(
'https://httpbin.org/get/foo',
['toto' => 'titi'],
function () {
return '{}';
}
);
/**
* The circuit breaker is initiated
* the 2 failed trials are done
* then the conditions are met to open the circuit breaker
*/
$invocations = $this->spy->getInvocations();
$this->assertCount(4, $invocations);
$this->assertSame('INITIATING', $invocations[0]->parameters[0]);
$this->assertSame('TRIAL', $invocations[1]->parameters[0]);
$this->assertSame('TRIAL', $invocations[2]->parameters[0]);
$this->assertSame('OPENING', $invocations[3]->parameters[0]);
}
public function testSimpleCall()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$mock = new Mock([
new Response(200, [], Stream::factory('{"hello": "world"}')),
]);
$client = new GuzzleClient(['mock' => $mock]);
$circuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$symfonyCache,
new NullDispatcher()
);
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
$this->assertEquals(0, $mock->count());
$this->assertEquals('{"hello": "world"}', $response);
}
public function testOpenStateAfterTooManyFailures()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$mock = new Mock([
new RequestException('Service unavailable', new Request('GET', 'test')),
new RequestException('Service unavailable', new Request('GET', 'test')),
]);
$client = new GuzzleClient(['mock' => $mock]);
$circuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$symfonyCache,
new NullDispatcher()
);
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(0, $mock->count());
$this->assertEquals(false, $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
}
public function testNoFallback()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$mock = new Mock([
new RequestException('Service unavailable', new Request('GET', 'test')),
new RequestException('Service unavailable', new Request('GET', 'test')),
]);
$client = new GuzzleClient(['mock' => $mock]);
$circuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$symfonyCache,
new NullDispatcher()
);
$response = $circuitBreaker->call('anything');
$this->assertEquals(0, $mock->count());
$this->assertEquals('', $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
}
public function testBackToClosedStateAfterSuccess()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$mock = new Mock([
new RequestException('Service unavailable', new Request('GET', 'test')),
new RequestException('Service unavailable', new Request('GET', 'test')),
new Response(200, [], Stream::factory('{"hello": "world"}')),
]);
$client = new GuzzleClient(['mock' => $mock]);
$circuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$symfonyCache,
new NullDispatcher()
);
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(1, $mock->count());
$this->assertEquals(false, $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
//Stay in OPEN state
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(1, $mock->count());
$this->assertEquals(false, $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
sleep(2);
//Switch to CLOSED state on success
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(0, $mock->count());
$this->assertEquals('{"hello": "world"}', $response);
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
}
public function testStayInOpenStateAfterFailure()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$mock = new Mock([
new RequestException('Service unavailable', new Request('GET', 'test')),
new RequestException('Service unavailable', new Request('GET', 'test')),
new RequestException('Service unavailable', new Request('GET', 'test')),
]);
$client = new GuzzleClient(['mock' => $mock]);
$circuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$symfonyCache,
new NullDispatcher()
);
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(1, $mock->count());
$this->assertEquals(false, $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
//Stay in OPEN state
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(1, $mock->count());
$this->assertEquals(false, $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
sleep(2);
//Switch to OPEN state on failure
$response = $circuitBreaker->call('anything', [], function () {
return false;
});
$this->assertEquals(0, $mock->count());
$this->assertEquals(false, $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
}
/**
* @return AdvancedCircuitBreaker the circuit breaker for testing purposes
*/
private function createCircuitBreaker()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
/** @var PHPUnit_Framework_MockObject_MockObject|TransitionDispatcherInterface $dispatcher */
$dispatcher = $this->createMock(TransitionDispatcherInterface::class);
$dispatcher->expects($this->spy = $this->any())
->method('dispatchTransition')
;
return new AdvancedCircuitBreaker(
$system,
$this->getTestClient(),
$symfonyCache,
$dispatcher
);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker;
use GuzzleHttp\Message\Request;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream;
use GuzzleHttp\Subscriber\Mock;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use GuzzleHttp\Exception\RequestException;
use PHPUnit\Framework\TestCase;
/**
* Helper to get a fake Guzzle client.
*/
abstract class CircuitBreakerTestCase extends TestCase
{
/**
* Returns an instance of Client able to emulate
* available and not available services.
*
* @return GuzzleClient
*/
protected function getTestClient()
{
$mock = new Mock([
new RequestException('Service unavailable', new Request('GET', 'test')),
new RequestException('Service unavailable', new Request('GET', 'test')),
new Response(200, [], Stream::factory('{"hello": "world"}')),
]);
return new GuzzleClient(['mock' => $mock]);
}
}

View File

@@ -0,0 +1,263 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker;
use PrestaShop\CircuitBreaker\AdvancedCircuitBreaker;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Contract\CircuitBreakerInterface;
use PrestaShop\CircuitBreaker\Exception\UnavailableServiceException;
use PrestaShop\CircuitBreaker\State;
use PrestaShop\CircuitBreaker\Storage\SimpleArray;
use PrestaShop\CircuitBreaker\Transition\NullDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcher;
use PrestaShop\CircuitBreaker\Storage\SymfonyCache;
use PrestaShop\CircuitBreaker\SymfonyCircuitBreaker;
use PrestaShop\CircuitBreaker\SimpleCircuitBreaker;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\System\MainSystem;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use Symfony\Component\Cache\Simple\ArrayCache;
class CircuitBreakerWorkflowTest extends CircuitBreakerTestCase
{
const OPEN_THRESHOLD = 1;
/**
* {@inheritdoc}
*/
protected function setUp()
{
parent::setUp();
//For SimpleCircuitBreaker tests we need to clear the storage cache because it is stored in a static variable
$storage = new SimpleArray();
$storage->clear();
}
/**
* When we use the circuit breaker on unreachable service
* the fallback response is used.
*
* @dataProvider getCircuitBreakers
*
* @param CircuitBreakerInterface $circuitBreaker
*/
public function testCircuitBreakerIsInClosedStateAtStart($circuitBreaker)
{
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
}
/**
* Once the number of failures is reached, the circuit breaker
* is open. This time no calls to the services are done.
*
* @dataProvider getCircuitBreakers
*
* @param CircuitBreakerInterface $circuitBreaker
*/
public function testCircuitBreakerWillBeOpenInCaseOfFailures($circuitBreaker)
{
// CLOSED
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
$response = $circuitBreaker->call('https://httpbin.org/get/foo', [], $this->createFallbackResponse());
$this->assertSame('{}', $response);
//After two failed calls switch to OPEN state
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
$this->assertSame(
'{}',
$circuitBreaker->call(
'https://httpbin.org/get/foo',
[],
$this->createFallbackResponse()
)
);
}
/**
* Once the number of failures is reached, the circuit breaker
* is open. This time no calls to the services are done.
*
* @dataProvider getCircuitBreakers
*
* @param CircuitBreakerInterface $circuitBreaker
*/
public function testCircuitBreakerWillBeOpenWithoutFallback($circuitBreaker)
{
// CLOSED
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
$response = $circuitBreaker->call('https://httpbin.org/get/foo');
$this->assertSame('', $response);
//After two failed calls switch to OPEN state
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
$this->assertSame(
'{}',
$circuitBreaker->call(
'https://httpbin.org/get/foo',
[],
$this->createFallbackResponse()
)
);
}
/**
* In HalfOpen state, if the service is back we can
* close the CircuitBreaker.
*
* @dataProvider getCircuitBreakers
*
* @param CircuitBreakerInterface $circuitBreaker
*/
public function testOnceInHalfOpenModeServiceIsFinallyReachable($circuitBreaker)
{
// CLOSED - first call fails (twice)
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
$response = $circuitBreaker->call('https://httpbin.org/get/foo', [], $this->createFallbackResponse());
$this->assertSame('{}', $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
// OPEN - no call to client
$response = $circuitBreaker->call('https://httpbin.org/get/foo', [], $this->createFallbackResponse());
$this->assertSame('{}', $response);
$this->assertSame(State::OPEN_STATE, $circuitBreaker->getState());
sleep(2 * self::OPEN_THRESHOLD);
// SWITCH TO HALF OPEN - retry to call the service
$this->assertSame(
'{"hello": "world"}',
$circuitBreaker->call(
'https://httpbin.org/get/foo',
[],
$this->createFallbackResponse()
)
);
$this->assertSame(State::CLOSED_STATE, $circuitBreaker->getState());
$this->assertTrue($circuitBreaker->isClosed());
}
/**
* This is not useful for SimpleCircuitBreaker since it has a SimpleArray storage
*/
public function testRememberLastTransactionState()
{
$system = new MainSystem(
new ClosedPlace(1, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$storage = new SymfonyCache(new ArrayCache());
$client = $this->createMock(GuzzleClient::class);
$client
->expects($this->once())
->method('request')
->willThrowException(new UnavailableServiceException())
;
$firstCircuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$storage,
new NullDispatcher()
);
$this->assertEquals(State::CLOSED_STATE, $firstCircuitBreaker->getState());
$firstCircuitBreaker->call('fake_service', [], function () {
return false;
});
$this->assertEquals(State::OPEN_STATE, $firstCircuitBreaker->getState());
$this->assertTrue($storage->hasTransaction('fake_service'));
$secondCircuitBreaker = new AdvancedCircuitBreaker(
$system,
$client,
$storage,
new NullDispatcher()
);
$this->assertEquals(State::CLOSED_STATE, $secondCircuitBreaker->getState());
$secondCircuitBreaker->call('fake_service', [], function () {
return false;
});
$this->assertEquals(State::OPEN_STATE, $secondCircuitBreaker->getState());
}
/**
* Return the list of supported circuit breakers
*
* @return array
*/
public function getCircuitBreakers()
{
return [
'simple' => [$this->createSimpleCircuitBreaker()],
'symfony' => [$this->createSymfonyCircuitBreaker()],
'advanced' => [$this->createAdvancedCircuitBreaker()],
];
}
/**
* @return SimpleCircuitBreaker the circuit breaker for testing purposes
*/
private function createSimpleCircuitBreaker()
{
return new SimpleCircuitBreaker(
new OpenPlace(0, 0, self::OPEN_THRESHOLD), // threshold 1s
new HalfOpenPlace(0, 0.2, 0), // timeout 0.2s to test the service
new ClosedPlace(2, 0.2, 0), // 2 failures allowed, 0.2s timeout
$this->getTestClient()
);
}
/**
* @return AdvancedCircuitBreaker the circuit breaker for testing purposes
*/
private function createAdvancedCircuitBreaker()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, self::OPEN_THRESHOLD)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
return new AdvancedCircuitBreaker(
$system,
$this->getTestClient(),
$symfonyCache,
new NullDispatcher()
);
}
/**
* @return SymfonyCircuitBreaker the circuit breaker for testing purposes
*/
private function createSymfonyCircuitBreaker()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, self::OPEN_THRESHOLD)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$eventDispatcherS = $this->createMock(EventDispatcher::class);
return new SymfonyCircuitBreaker(
$system,
$this->getTestClient(),
$symfonyCache,
$eventDispatcherS
);
}
/**
* @return callable the fallback callable
*/
private function createFallbackResponse()
{
return function () {
return '{}';
};
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Client;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Client\GuzzleClient;
use PrestaShop\CircuitBreaker\Exception\UnavailableServiceException;
class GuzzleClientTest extends TestCase
{
public function testRequestWorksAsExpected()
{
$client = new GuzzleClient();
$this->assertNotNull($client->request('https://www.google.com', [
'method' => 'GET',
]));
}
public function testWrongRequestThrowsAnException()
{
$this->expectException(UnavailableServiceException::class);
$client = new GuzzleClient();
$client->request('http://not-even-a-valid-domain.xxx', []);
}
public function testTheClientAcceptsHttpMethodOverride()
{
$client = new GuzzleClient([
'method' => 'HEAD',
]);
$this->assertEmpty($client->request('https://www.google.fr', []));
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Event;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Event\TransitionEvent;
class TransitionEventTest extends TestCase
{
public function testCreation()
{
$event = new TransitionEvent('foo', 'bar', []);
$this->assertInstanceOf(TransitionEvent::class, $event);
}
/**
* @depends testCreation
*/
public function testGetService()
{
$event = new TransitionEvent('eventName', 'service', []);
$this->assertSame('service', $event->getService());
}
/**
* @depends testCreation
*/
public function testGetEvent()
{
$event = new TransitionEvent('eventName', 'service', []);
$this->assertSame('eventName', $event->getEvent());
}
/**
* @depends testCreation
*/
public function testGetParameters()
{
$parameters = [
'foo' => 'myFoo',
'bar' => true,
];
$event = new TransitionEvent('eventName', 'service', $parameters);
$this->assertSame($parameters, $event->getParameters());
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Exception;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Exception\InvalidPlaceException;
class InvalidPlaceTest extends TestCase
{
public function testCreation()
{
$invalidPlace = new InvalidPlaceException();
$this->assertInstanceOf(InvalidPlaceException::class, $invalidPlace);
}
/**
* @dataProvider getSettings
*
* @param array $settings
* @param string $expectedExceptionMessage
*/
public function testInvalidSettings($settings, $expectedExceptionMessage)
{
$invalidPlace = InvalidPlaceException::invalidSettings(
$settings[0], // failures
$settings[1], // timeout
$settings[2] // threshold
);
$this->assertSame($invalidPlace->getMessage(), $expectedExceptionMessage);
}
/**
* @return array
*/
public function getSettings()
{
return [
'all_invalid_settings' => [
['0', '1', null],
'Invalid settings for Place' . PHP_EOL .
'Excepted failures to be a positive integer, got string (0)' . PHP_EOL .
'Excepted timeout to be a float, got string (1)' . PHP_EOL .
'Excepted threshold to be a positive integer, got NULL' . PHP_EOL,
],
'2_invalid_settings' => [
[0, '1', null],
'Invalid settings for Place' . PHP_EOL .
'Excepted timeout to be a float, got string (1)' . PHP_EOL .
'Excepted threshold to be a positive integer, got NULL' . PHP_EOL,
],
'1_invalid_settings' => [
[0, '1', 2],
'Invalid settings for Place' . PHP_EOL .
'Excepted timeout to be a float, got string (1)' . PHP_EOL,
],
'all_valid_settings' => [
[0, 1.1, 2],
'Invalid settings for Place' . PHP_EOL,
],
];
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Exception;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Exception\InvalidTransactionException;
class InvalidTransactionTest extends TestCase
{
public function testCreation()
{
$invalidPlace = new InvalidTransactionException();
$this->assertInstanceOf(InvalidTransactionException::class, $invalidPlace);
}
/**
* @dataProvider getParameters
*
* @param array $parameters
* @param string $expectedExceptionMessage
*/
public function testInvalidParameters($parameters, $expectedExceptionMessage)
{
$invalidPlace = InvalidTransactionException::invalidParameters(
$parameters[0], // service
$parameters[1], // failures
$parameters[2], // state
$parameters[3] // threshold
);
$this->assertSame($invalidPlace->getMessage(), $expectedExceptionMessage);
}
/**
* @return array
*/
public function getParameters()
{
return [
'all_invalid_parameters' => [
[100, '0', null, 'toto'],
'Invalid parameters for Transaction' . PHP_EOL .
'Excepted service to be an URI, got integer (100)' . PHP_EOL .
'Excepted failures to be a positive integer, got string (0)' . PHP_EOL .
'Excepted state to be a string, got NULL' . PHP_EOL .
'Excepted threshold to be a positive integer, got string (toto)' . PHP_EOL,
],
'3_invalid_parameters' => [
['http://www.prestashop.com', '1', null, 'toto'],
'Invalid parameters for Transaction' . PHP_EOL .
'Excepted failures to be a positive integer, got string (1)' . PHP_EOL .
'Excepted state to be a string, got NULL' . PHP_EOL .
'Excepted threshold to be a positive integer, got string (toto)' . PHP_EOL,
],
'2_invalid_parameters' => [
['http://www.prestashop.com', 10, null, null],
'Invalid parameters for Transaction' . PHP_EOL .
'Excepted state to be a string, got NULL' . PHP_EOL .
'Excepted threshold to be a positive integer, got NULL' . PHP_EOL,
],
'none_invalid' => [
['http://www.prestashop.com', 10, 'CLOSED_STATE', 1],
'Invalid parameters for Transaction' . PHP_EOL,
],
];
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* 2007-2019 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace Tests\PrestaShop\CircuitBreaker;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\FactorySettings;
class FactorySettingsTest extends TestCase
{
public function testSimpleSettings()
{
$settings = new FactorySettings(2, 0.5, 10);
$this->assertNotNull($settings);
$this->assertEquals(2, $settings->getFailures());
$this->assertEquals(0.5, $settings->getTimeout());
$this->assertEquals(10, $settings->getThreshold());
$this->assertEquals(2, $settings->getStrippedFailures());
$this->assertEquals(0.5, $settings->getStrippedTimeout());
}
public function testMergeSettings()
{
$defaultSettings = new FactorySettings(2, 0.5, 10);
$defaultSettings
->setStrippedTimeout(1.2)
->setStrippedFailures(1)
;
$this->assertEquals(2, $defaultSettings->getFailures());
$this->assertEquals(0.5, $defaultSettings->getTimeout());
$this->assertEquals(10, $defaultSettings->getThreshold());
$this->assertEquals(1, $defaultSettings->getStrippedFailures());
$this->assertEquals(1.2, $defaultSettings->getStrippedTimeout());
$settings = new FactorySettings(2, 1.5, 20);
$mergedSettings = FactorySettings::merge($defaultSettings, $settings);
$this->assertEquals(2, $mergedSettings->getFailures());
$this->assertEquals(1.5, $mergedSettings->getTimeout());
$this->assertEquals(20, $mergedSettings->getThreshold());
$this->assertEquals(2, $mergedSettings->getStrippedFailures());
$this->assertEquals(1.5, $mergedSettings->getStrippedTimeout());
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\Exception\InvalidPlaceException;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\State;
class ClosedPlaceTest extends PlaceTestCase
{
/**
* @dataProvider getFixtures
*
* @param mixed $failures
* @param mixed $timeout
* @param mixed $threshold
*/
public function testCreationWith($failures, $timeout, $threshold)
{
$closedPlace = new ClosedPlace($failures, $timeout, $threshold);
$this->assertSame($failures, $closedPlace->getFailures());
$this->assertSame($timeout, $closedPlace->getTimeout());
$this->assertSame($threshold, $closedPlace->getThreshold());
}
/**
* @dataProvider getInvalidFixtures
*
* @param mixed $failures
* @param mixed $timeout
* @param mixed $threshold
*/
public function testCreationWithInvalidValues($failures, $timeout, $threshold)
{
$this->expectException(InvalidPlaceException::class);
new ClosedPlace($failures, $timeout, $threshold);
}
public function testGetExpectedState()
{
$closedPlace = new ClosedPlace(1, 1, 1);
$this->assertSame(State::CLOSED_STATE, $closedPlace->getState());
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\Exception\InvalidPlaceException;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\State;
class HalfOpenPlaceTest extends PlaceTestCase
{
/**
* @dataProvider getFixtures
*
* @param mixed $failures
* @param mixed $timeout
* @param mixed $threshold
*/
public function testCreationWith($failures, $timeout, $threshold)
{
$halfOpenPlace = new HalfOpenPlace($failures, $timeout, $threshold);
$this->assertSame($failures, $halfOpenPlace->getFailures());
$this->assertSame($timeout, $halfOpenPlace->getTimeout());
$this->assertSame($threshold, $halfOpenPlace->getThreshold());
}
/**
* @dataProvider getInvalidFixtures
*
* @param mixed $failures
* @param mixed $timeout
* @param mixed $threshold
*/
public function testCreationWithInvalidValues($failures, $timeout, $threshold)
{
$this->expectException(InvalidPlaceException::class);
new HalfOpenPlace($failures, $timeout, $threshold);
}
public function testGetExpectedState()
{
$halfOpenPlace = new HalfOpenPlace(1, 1, 1);
$this->assertSame(State::HALF_OPEN_STATE, $halfOpenPlace->getState());
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Place;
use PrestaShop\CircuitBreaker\Exception\InvalidPlaceException;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use PrestaShop\CircuitBreaker\State;
class OpenPlaceTest extends PlaceTestCase
{
/**
* @dataProvider getFixtures
*
* @param mixed $failures
* @param mixed $timeout
* @param mixed $threshold
*/
public function testCreationWith($failures, $timeout, $threshold)
{
$openPlace = new OpenPlace($failures, $timeout, $threshold);
$this->assertSame($failures, $openPlace->getFailures());
$this->assertSame($timeout, $openPlace->getTimeout());
$this->assertSame($threshold, $openPlace->getThreshold());
}
/**
* @dataProvider getInvalidFixtures
*
* @param mixed $failures
* @param mixed $timeout
* @param mixed $threshold
*/
public function testCreationWithInvalidValues($failures, $timeout, $threshold)
{
$this->expectException(InvalidPlaceException::class);
new OpenPlace($failures, $timeout, $threshold);
}
public function testGetExpectedState()
{
$openPlace = new OpenPlace(1, 1, 1);
$this->assertSame(State::OPEN_STATE, $openPlace->getState());
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Place;
use PHPUnit\Framework\TestCase;
/**
* Helper to share fixtures accross Place tests.
*/
class PlaceTestCase extends TestCase
{
/**
* @return array
*/
public function getFixtures()
{
return [
'0_0_0' => [0, 0, 0],
'1_100_0' => [1, 100, 0],
'3_0.6_3' => [3, 0.6, 3],
];
}
/**
* @return array
*/
public function getArrayFixtures()
{
return [
'assoc_array' => [[
'timeout' => 3,
'threshold' => 2,
'failures' => 1,
]],
];
}
/**
* @return array
*/
public function getInvalidFixtures()
{
return [
'minus1_null_false' => [-1, null, false],
'3_0.6_3.14' => [3, 0.6, 3.14],
];
}
/**
* @return array
*/
public function getInvalidArrayFixtures()
{
return [
'invalid_indexes' => [[
0 => 3,
1 => 2,
4 => 1,
]],
'invalid_keys' => [[
'timeout' => 3,
'max_wait' => 2,
'failures' => 1,
]],
];
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Contract\FactorySettingsInterface;
use PrestaShop\CircuitBreaker\FactorySettings;
use PrestaShop\CircuitBreaker\SimpleCircuitBreaker;
use PrestaShop\CircuitBreaker\SimpleCircuitBreakerFactory;
class SimpleCircuitBreakerFactoryTest extends TestCase
{
/**
* @return void
*/
public function testCreation()
{
$factory = new SimpleCircuitBreakerFactory();
$this->assertInstanceOf(SimpleCircuitBreakerFactory::class, $factory);
}
/**
* @depends testCreation
* @dataProvider getSettings
*
* @param FactorySettingsInterface $settings the Circuit Breaker settings
*
* @return void
*/
public function testCircuitBreakerCreation(FactorySettingsInterface $settings)
{
$factory = new SimpleCircuitBreakerFactory();
$circuitBreaker = $factory->create($settings);
$this->assertInstanceOf(SimpleCircuitBreaker::class, $circuitBreaker);
}
/**
* @return array
*/
public function getSettings()
{
return [
[
(new FactorySettings(2, 0.1, 10))
->setStrippedTimeout(0.2)
->setStrippedFailures(1),
],
[
(new FactorySettings(2, 0.1, 10))
->setStrippedTimeout(0.2)
->setStrippedFailures(1)
->setClientOptions(['proxy' => '192.168.16.1:10']),
],
];
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Storage;
use Doctrine\Common\Cache\FilesystemCache;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Contract\StorageInterface;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
use PrestaShop\CircuitBreaker\Storage\DoctrineCache;
class DoctrineCacheTest extends TestCase
{
/**
* @var StorageInterface the Doctrine Cache storage
*/
private $doctrineCache;
/**
* {@inheritdoc}
*/
protected function setUp()
{
$this->doctrineCache = new DoctrineCache(
new FilesystemCache(sys_get_temp_dir() . '/ps__circuit_breaker')
);
}
/**
* {@inheritdoc}
*/
protected function tearDown()
{
$filesystemAdapter = new FilesystemCache(sys_get_temp_dir() . '/ps__circuit_breaker');
$filesystemAdapter->deleteAll();
}
public function testCreation()
{
$doctrineCache = new DoctrineCache(
new FilesystemCache(sys_get_temp_dir() . '/ps__circuit_breaker')
);
$this->assertInstanceOf(DoctrineCache::class, $doctrineCache);
}
/**
* @depends testCreation
*/
public function testSaveTransaction()
{
$operation = $this->doctrineCache->saveTransaction(
'http://test.com',
$this->createMock(TransactionInterface::class)
);
$this->assertTrue($operation);
}
/**
* @depends testCreation
* @depends testSaveTransaction
*/
public function testHasTransaction()
{
$this->doctrineCache->saveTransaction('http://test.com', $this->createMock(TransactionInterface::class));
$this->assertTrue($this->doctrineCache->hasTransaction('http://test.com'));
}
/**
* @depends testCreation
* @depends testSaveTransaction
* @depends testHasTransaction
*/
public function testGetTransaction()
{
$translationStub = $this->createMock(TransactionInterface::class);
$this->doctrineCache->saveTransaction('http://test.com', $translationStub);
$transaction = $this->doctrineCache->getTransaction('http://test.com');
$this->assertEquals($transaction, $translationStub);
}
/**
* @depends testCreation
* @depends testGetTransaction
* @depends testHasTransaction
*/
public function testGetNotFoundTransactionThrowsAnException()
{
$this->expectException(TransactionNotFoundException::class);
$this->doctrineCache->getTransaction('http://test.com');
}
/**
* @depends testSaveTransaction
* @depends testGetTransaction
*/
public function testClear()
{
$translationStub = $this->createMock(TransactionInterface::class);
$this->doctrineCache->saveTransaction('http://a.com', $translationStub);
$this->doctrineCache->saveTransaction('http://b.com', $translationStub);
// We have stored 2 transactions
$this->assertTrue($this->doctrineCache->clear());
$this->expectException(TransactionNotFoundException::class);
$this->doctrineCache->getTransaction('http://a.com');
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Storage;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
use PrestaShop\CircuitBreaker\Storage\SimpleArray;
class SimpleArrayTest extends TestCase
{
/**
* {@inheritdoc}
*/
protected function setUp()
{
$simpleArray = new SimpleArray();
$simpleArray::$transactions = [];
}
/**
* @return void
*/
public function testCreation()
{
$simpleArray = new SimpleArray();
$this->assertCount(0, $simpleArray::$transactions);
$this->assertInstanceOf(SimpleArray::class, $simpleArray);
}
/**
* @depends testCreation
*
* @return void
*/
public function testSaveTransaction()
{
$simpleArray = new SimpleArray();
$operation = $simpleArray->saveTransaction(
'http://test.com',
$this->createMock(TransactionInterface::class)
);
$this->assertTrue($operation);
$this->assertCount(1, $simpleArray::$transactions);
}
/**
* @depends testCreation
* @depends testSaveTransaction
*
* @return void
*/
public function testHasTransaction()
{
$simpleArray = new SimpleArray();
$simpleArray->saveTransaction('http://test.com', $this->createMock(TransactionInterface::class));
$this->assertTrue($simpleArray->hasTransaction('http://test.com'));
}
/**
* @depends testCreation
* @depends testSaveTransaction
* @depends testHasTransaction
*
* @return void
*/
public function testGetTransaction()
{
$simpleArray = new SimpleArray();
$translationStub = $this->createMock(TransactionInterface::class);
$simpleArray->saveTransaction('http://test.com', $translationStub);
$transaction = $simpleArray->getTransaction('http://test.com');
$this->assertSame($transaction, $translationStub);
}
/**
* @depends testCreation
* @depends testGetTransaction
* @depends testHasTransaction
*
* @return void
*/
public function testGetNotFoundTransactionThrowsAnException()
{
$this->expectException(TransactionNotFoundException::class);
$simpleArray = new SimpleArray();
$simpleArray->getTransaction('http://test.com');
}
/**
* @depends testSaveTransaction
* @depends testGetTransaction
*
* @return void
*/
public function testClear()
{
$simpleArray = new SimpleArray();
$translationStub = $this->createMock(TransactionInterface::class);
$simpleArray->saveTransaction('http://a.com', $translationStub);
$simpleArray->saveTransaction('http://b.com', $translationStub);
// We have stored 2 transactions
$simpleArray->clear();
$transactions = $simpleArray::$transactions;
$this->assertEmpty($transactions);
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Storage;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Contract\TransactionInterface;
use PrestaShop\CircuitBreaker\Exception\TransactionNotFoundException;
use PrestaShop\CircuitBreaker\Storage\SymfonyCache;
use Symfony\Component\Cache\Simple\FilesystemCache;
class SymfonyCacheTest extends TestCase
{
/**
* @var SymfonyCache the Symfony Cache storage
*/
private $symfonyCache;
/**
* {@inheritdoc}
*/
protected function setUp()
{
$this->symfonyCache = new SymfonyCache(
new FilesystemCache('ps__circuit_breaker', 20)
);
}
/**
* {@inheritdoc}
*/
protected function tearDown()
{
$filesystemAdapter = new FilesystemCache('ps__circuit_breaker', 20);
$filesystemAdapter->clear();
}
public function testCreation()
{
$symfonyCache = new SymfonyCache(
new FilesystemCache('ps__circuit_breaker')
);
$this->assertInstanceOf(SymfonyCache::class, $symfonyCache);
}
/**
* @depends testCreation
*/
public function testSaveTransaction()
{
$operation = $this->symfonyCache->saveTransaction(
'http://test.com',
$this->createMock(TransactionInterface::class)
);
$this->assertTrue($operation);
}
/**
* @depends testCreation
* @depends testSaveTransaction
*/
public function testHasTransaction()
{
$this->symfonyCache->saveTransaction('http://test.com', $this->createMock(TransactionInterface::class));
$this->assertTrue($this->symfonyCache->hasTransaction('http://test.com'));
}
/**
* @depends testCreation
* @depends testSaveTransaction
* @depends testHasTransaction
*/
public function testGetTransaction()
{
$translationStub = $this->createMock(TransactionInterface::class);
$this->symfonyCache->saveTransaction('http://test.com', $translationStub);
$transaction = $this->symfonyCache->getTransaction('http://test.com');
$this->assertEquals($transaction, $translationStub);
}
/**
* @depends testCreation
* @depends testGetTransaction
* @depends testHasTransaction
*/
public function testGetNotFoundTransactionThrowsAnException()
{
$this->expectException(TransactionNotFoundException::class);
$this->symfonyCache->getTransaction('http://test.com');
}
/**
* @depends testSaveTransaction
* @depends testGetTransaction
*/
public function testClear()
{
$translationStub = $this->createMock(TransactionInterface::class);
$this->symfonyCache->saveTransaction('http://a.com', $translationStub);
$this->symfonyCache->saveTransaction('http://b.com', $translationStub);
// We have stored 2 transactions
$this->assertTrue($this->symfonyCache->clear());
$this->expectException(TransactionNotFoundException::class);
$this->symfonyCache->getTransaction('http://a.com');
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker;
use PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
use Symfony\Component\EventDispatcher\EventDispatcher;
use PrestaShop\CircuitBreaker\SymfonyCircuitBreaker;
use PrestaShop\CircuitBreaker\Storage\SymfonyCache;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\System\MainSystem;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use Symfony\Component\Cache\Simple\ArrayCache;
class SymfonyCircuitBreakerEventsTest extends CircuitBreakerTestCase
{
/**
* Used to track the dispatched events.
*
* @var PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
*/
private $spy;
/**
* We should see the circuit breaker initialized,
* a call being done and then the circuit breaker closed.
*/
public function testCircuitBreakerEventsOnFirstFailedCall()
{
$circuitBreaker = $this->createCircuitBreaker();
$circuitBreaker->call(
'https://httpbin.org/get/foo',
[],
function () {
return '{}';
}
);
/**
* The circuit breaker is initiated
* the 2 failed trials are done
* then the conditions are met to open the circuit breaker
*/
$invocations = $this->spy->getInvocations();
$this->assertCount(4, $invocations);
$this->assertSame('INITIATING', $invocations[0]->parameters[0]);
$this->assertSame('TRIAL', $invocations[1]->parameters[0]);
$this->assertSame('TRIAL', $invocations[2]->parameters[0]);
$this->assertSame('OPENING', $invocations[3]->parameters[0]);
}
/**
* @return SymfonyCircuitBreaker the circuit breaker for testing purposes
*/
private function createCircuitBreaker()
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);
$symfonyCache = new SymfonyCache(new ArrayCache());
$eventDispatcherS = $this->createMock(EventDispatcher::class);
$eventDispatcherS->expects($this->spy = $this->any())
->method('dispatch')
;
return new SymfonyCircuitBreaker(
$system,
$this->getTestClient(),
$symfonyCache,
$eventDispatcherS
);
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\System;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\State;
use PrestaShop\CircuitBreaker\Place\OpenPlace;
use PrestaShop\CircuitBreaker\Place\HalfOpenPlace;
use PrestaShop\CircuitBreaker\Place\ClosedPlace;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use PrestaShop\CircuitBreaker\System\MainSystem;
class MainSystemTest extends TestCase
{
public function testCreation()
{
$openPlace = new OpenPlace(1, 1, 1);
$halfOpenPlace = new HalfOpenPlace(1, 1, 1);
$closedPlace = new ClosedPlace(1, 1, 1);
$mainSystem = new MainSystem(
$openPlace,
$halfOpenPlace,
$closedPlace
);
$this->assertInstanceOf(MainSystem::class, $mainSystem);
}
/**
* @depends testCreation
*/
public function testGetInitialPlace()
{
$mainSystem = $this->createMainSystem();
$initialPlace = $mainSystem->getInitialPlace();
$this->assertInstanceOf(PlaceInterface::class, $initialPlace);
$this->assertSame(State::CLOSED_STATE, $initialPlace->getState());
}
/**
* @depends testCreation
*/
public function testGetPlaces()
{
$mainSystem = $this->createMainSystem();
$places = $mainSystem->getPlaces();
$this->assertInternalType('array', $places);
$this->assertCount(3, $places);
foreach ($places as $place) {
$this->assertInstanceOf(PlaceInterface::class, $place);
}
}
/**
* Returns an instance of MainSystem for tests.
*
* @return MainSystem
*/
private function createMainSystem()
{
$openPlace = new OpenPlace(1, 1, 1);
$halfOpenPlace = new HalfOpenPlace(1, 1, 1);
$closedPlace = new ClosedPlace(1, 1, 1);
return new MainSystem(
$openPlace,
$halfOpenPlace,
$closedPlace
);
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Transaction;
use DateTime;
use PHPUnit\Framework\TestCase;
use PHPUnit_Framework_MockObject_MockObject;
use PrestaShop\CircuitBreaker\Contract\PlaceInterface;
use PrestaShop\CircuitBreaker\Transaction\SimpleTransaction;
class SimpleTransactionTest extends TestCase
{
public function testCreation()
{
$placeStub = $this->createPlaceStub();
$simpleTransaction = new SimpleTransaction(
'http://some-uri.domain',
0,
$placeStub->getState(),
2
);
$this->assertInstanceOf(SimpleTransaction::class, $simpleTransaction);
}
/**
* @depends testCreation
*/
public function testGetService()
{
$simpleTransaction = $this->createSimpleTransaction();
$this->assertSame('http://some-uri.domain', $simpleTransaction->getService());
}
/**
* @depends testCreation
*/
public function testGetFailures()
{
$simpleTransaction = $this->createSimpleTransaction();
$this->assertSame(0, $simpleTransaction->getFailures());
}
/**
* @depends testCreation
*/
public function testGetState()
{
$simpleTransaction = $this->createSimpleTransaction();
$this->assertSame('FAKE_STATE', $simpleTransaction->getState());
}
/**
* @depends testCreation
*/
public function testGetThresholdDateTime()
{
$simpleTransaction = $this->createSimpleTransaction();
$expectedDateTime = (new DateTime('+2 second'))->format('d/m/Y H:i:s');
$simpleTransactionDateTime = $simpleTransaction->getThresholdDateTime()->format('d/m/Y H:i:s');
$this->assertSame($expectedDateTime, $simpleTransactionDateTime);
}
/**
* @depends testCreation
* @depends testGetFailures
*/
public function testIncrementFailures()
{
$simpleTransaction = $this->createSimpleTransaction();
$simpleTransaction->incrementFailures();
$this->assertSame(1, $simpleTransaction->getFailures());
}
/**
* @depends testCreation
*/
public function testCreationFromPlaceHelper()
{
$simpleTransactionFromHelper = SimpleTransaction::createFromPlace(
$this->createPlaceStub(),
'http://some-uri.domain'
);
$simpleTransaction = $this->createSimpleTransaction();
$this->assertSame($simpleTransactionFromHelper->getState(), $simpleTransaction->getState());
$this->assertSame($simpleTransactionFromHelper->getFailures(), $simpleTransaction->getFailures());
$fromPlaceDate = $simpleTransactionFromHelper->getThresholdDateTime()->format('d/m/Y H:i:s');
$expectedDate = $simpleTransaction->getThresholdDateTime()->format('d/m/Y H:i:s');
$this->assertSame($fromPlaceDate, $expectedDate);
}
/**
* Returns an instance of SimpleTransaction for tests.
*
* @return SimpleTransaction
*/
private function createSimpleTransaction()
{
$placeStub = $this->createPlaceStub();
return new SimpleTransaction(
'http://some-uri.domain',
0,
$placeStub->getState(),
2
);
}
/**
* Returns an instance of Place with State equals to "FAKE_STATE"
* and threshold equals to 2.
*
* @return PlaceInterface&PHPUnit_Framework_MockObject_MockObject
*/
private function createPlaceStub()
{
$placeStub = $this->createMock(PlaceInterface::class);
$placeStub->expects($this->any())
->method('getState')
->willReturn('FAKE_STATE')
;
$placeStub->expects($this->any())
->method('getThreshold')
->willReturn(2)
;
return $placeStub;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Tests\PrestaShop\CircuitBreaker\Util;
use PHPUnit\Framework\TestCase;
use PrestaShop\CircuitBreaker\Util\Assert;
use stdClass;
class AssertTest extends TestCase
{
/**
* @dataProvider getValues
*
* @param mixed $value
* @param bool $expected
*/
public function testIsPositiveValue($value, $expected)
{
$this->assertSame($expected, Assert::isPositiveValue($value));
}
/**
* @dataProvider getURIs
*
* @param mixed $value
* @param bool $expected
*/
public function testIsURI($value, $expected)
{
$this->assertSame($expected, Assert::isURI($value));
}
/**
* @dataProvider getStrings
*
* @param mixed $value
* @param bool $expected
*/
public function testIsString($value, $expected)
{
$this->assertSame($expected, Assert::isString($value));
}
/**
* @return array
*/
public function getValues()
{
return [
'0' => [0, true],
'str_0' => ['0', false],
'float' => [0.1, true],
'stdclass' => [new stdClass(), false],
'callable' => [
function () {
return 0;
},
false,
],
'negative' => [-1, false],
'bool' => [false, false],
];
}
/**
* @return array
*/
public function getURIs()
{
return [
'valid' => ['http://www.prestashop.com', true],
'int' => [0, false],
'null' => [null, false],
'bool' => [false, false],
'local' => ['http://localhost', true],
'ssh' => ['ssh://git@git.example.com/FOO/my_project.git', true],
];
}
public function getStrings()
{
return [
'valid' => ['foo', true],
'empty' => ['', false],
'null' => [null, false],
'bool' => [false, false],
'stdclass' => [new stdClass(), false],
'valid2' => ['INVALID_STATE', true],
];
}
}