Completed
Push — master ( f59019...093bae )
by Guillaume
04:06
created

BreakerTest::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Eljam\CircuitBreaker;
4
5
use Doctrine\Common\Cache\FilesystemCache;
6
use Eljam\CircuitBreaker\Circuit;
7
use Eljam\CircuitBreaker\Event\CircuitEvents;
8
use Eljam\CircuitBreaker\Exception\CircuitOpenException;
9
use Eljam\CircuitBreaker\Exception\CustomException;
10
use Symfony\Component\EventDispatcher\Event;
11
12
/**
13
 * Class BreakerTest.
14
 */
15
class BreakerTest extends \PHPUnit_Framework_TestCase
16
{
17
    protected $dir;
18
19
    public function setUp()
20
    {
21
        $this->dir = sys_get_temp_dir().DIRECTORY_SEPARATOR.'store';
22
    }
23
24
    /**
25
     * testMultiProcess.
26
     */
27
    public function testMultiProcess()
28
    {
29
        $fileCache  = new FilesystemCache($this->dir, 'txt');
30
        $breaker = new Breaker('github_api', ['ignore_exceptions' => true], $fileCache);
31
        $breaker2 = new Breaker('github_api', ['ignore_exceptions' => true], $fileCache);
32
33
        $breaker1FailureCount = 0;
34
35
        $breaker->addListener(CircuitEvents::FAILURE, function (Event $event) use (&$breaker1FailureCount) {
36
            $breaker1FailureCount = $event->getCircuit()->getFailures();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getCircuit() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Eljam\CircuitBreaker\Event\CircuitEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
37
        });
38
39
        $breaker2->addListener(CircuitEvents::FAILURE, function (Event $event) use (&$breaker1FailureCount) {
40
            $this->assertEquals($breaker1FailureCount, $event->getCircuit()->getFailures());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getCircuit() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Eljam\CircuitBreaker\Event\CircuitEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
41
        });
42
43
        $fn = function () {
44
            throw new CustomException("An error as occured");
45
        };
46
47
        $breaker->protect($fn);
48
49
        $breaker2->protect($fn);
50
51
    }
52
53
    /**
54
     * testOpenBehavior.
55
     */
56
    public function testOpenBehavior()
57
    {
58
        $breaker = new Breaker(
59
            'exception breaker',
60
            ['exclude_exceptions' => [CustomException::class]]
61
        );
62
63
        $breaker->addListener(CircuitEvents::OPEN, function (Event $event) {
64
            $this->assertInstanceOf(Circuit::class, $event->getCircuit());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getCircuit() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Eljam\CircuitBreaker\Event\CircuitEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
65
        });
66
67
        $this->setExpectedException('Eljam\CircuitBreaker\Exception\CircuitOpenException');
68
69
        $fn = function () {
70
            throw new CustomException("An error as occured");
71
        };
72
73
        for ($i = 0; $i <= 5; $i++) {
74
            $breaker->protect($fn);
75
        }
76
    }
77
78
    /**
79
     * testHalfOpenBehavior.
80
     */
81
    public function testHalfOpenBehavior()
82
    {
83
        $breaker = new Breaker(
84
            'exception breaker',
85
            [
86
                'reset_timeout' => 1,
87
                'ignore_exceptions' => true,
88
            ]
89
        );
90
91
        $breaker->addListener(CircuitEvents::HALF_OPEN, function (Event $event) {
92
            $this->assertInstanceOf(Circuit::class, $event->getCircuit());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getCircuit() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Eljam\CircuitBreaker\Event\CircuitEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
93
        });
94
95
        $fn = function () {
96
            throw new CustomException("An error as occured");
97
        };
98
99
        try {
100
            for ($i = 0; $i <= 5; $i++) {
101
                $breaker->protect($fn);
102
            }
103
        } catch (CircuitOpenException $e) {
104
            $this->assertSame(CircuitOpenException::class, get_class($e));
105
        }
106
107
        sleep(2);
108
109
        $fnPass = function () {
110
            return 'ok';
111
        };
112
113
        $breaker->protect($fnPass);
114
    }
115
116
    /**
117
     * testGetTheResult.
118
     */
119
    public function testGetTheResult()
120
    {
121
        $breaker = new Breaker('simple_echo');
122
        $hello = 'eljam';
123
124
        $fn = function () use ($hello) {
125
            return $hello;
126
        };
127
128
        $result = $breaker->protect($fn);
129
130
        $this->assertSame($hello, $result);
131
    }
132
133
    /**
134
     * testIgnoreException.
135
     */
136
    public function testIgnoreAllException()
137
    {
138
        $breaker = new Breaker(
139
            'simple_echo',
140
            ['ignore_exceptions' => true]
141
        );
142
        $hello = 'eljam';
143
144
        $fn = function () use ($hello) {
145
            throw new CustomException("An error as occured");
146
147
            return $hello;
0 ignored issues
show
Unused Code introduced by
return $hello; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
148
        };
149
150
        $result = $breaker->protect($fn);
151
152
        $this->assertNull($result);
153
    }
154
155
    /**
156
     * testThrowCustomException.
157
     */
158
    public function testThrowCustomException()
159
    {
160
        $breaker = new Breaker(
161
            'custom_exception'
162
        );
163
        $hello = 'eljam';
164
165
        $this->setExpectedException('Eljam\CircuitBreaker\Exception\CustomException');
166
167
        $fn = function () use ($hello) {
168
            throw new CustomException("An error as occured");
169
170
            return $hello;
0 ignored issues
show
Unused Code introduced by
return $hello; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
171
        };
172
173
        $breaker->protect($fn);
174
175
        $this->assertInstanceOf(CustomException::class, $result);
0 ignored issues
show
Bug introduced by
The variable $result does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
176
    }
177
178
    public function tearDown()
179
    {
180
        @unlink($this->dir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
181
    }
182
}
183