Completed
Push — master ( 9cefc1...f5ce4e )
by Julián
10:26
created

SessionWareTest::testSessionIdFromRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 1
eloc 7
nc 1
nop 0
1
<?php
2
/**
3
 * SessionWare (https://github.com/juliangut/sessionware)
4
 * PSR7 session management middleware
5
 *
6
 * @license BSD-3-Clause
7
 * @author Julián Gutiérrez <[email protected]>
8
 */
9
10
namespace Jgut\Middleware\Sessionware\Tests;
11
12
use Jgut\Middleware\SessionWare;
13
use Zend\Diactoros\Response;
14
use Zend\Diactoros\ServerRequestFactory;
15
16
/**
17
 * PHP session handler middleware test class.
18
 */
19
class SessionWareTest extends \PHPUnit_Framework_TestCase
20
{
21
    /**
22
     * @var \Zend\Diactoros\Request
23
     */
24
    protected $request;
25
26
    /**
27
     * @var Response
28
     */
29
    protected $response;
30
31
    /**
32
     * @var callable
33
     */
34
    protected $callback;
35
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public function setUp()
40
    {
41
        if (session_status() === PHP_SESSION_ACTIVE) {
42
            session_unset();
43
            session_destroy();
44
        }
45
46
        // Set a high probability to launch garbage collector
47
        ini_set('session.gc_probability', 1);
48
        ini_set('session.gc_divisor', 4);
49
50
        $this->request = ServerRequestFactory::fromGlobals();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Zend\Diactoros\ServerRe...tFactory::fromGlobals() of type object<Zend\Diactoros\ServerRequest> is incompatible with the declared type object<Zend\Diactoros\Request> of property $request.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
51
        $this->response = new Response;
52
        $this->callback = function ($request, $response) {
53
            return $response;
54
        };
55
    }
56
57
    /**
58
     * @runInSeparateProcess
59
     *
60
     * @expectedException \RuntimeException
61
     * @expectedExceptionMessageRegExp /^Session has already been started/
62
     */
63
    public function testSessionAlreadyStarted()
64
    {
65
        session_id('SessionWareSession');
66
67
        session_start();
68
69
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
70
71
        $middleware($this->request, $this->response, $this->callback);
72
    }
73
74
    /**
75
     * @runInSeparateProcess
76
     */
77
    public function testSessionTimeoutControlKey()
0 ignored issues
show
Coding Style introduced by
testSessionTimeoutControlKey uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
78
    {
79
        $defaultTimeout = time() - SessionWare::SESSION_LIFETIME_EXTENDED;
80
        $middleware = new SessionWare(
81
            ['name' => 'SessionWareSession', 'timeoutKey' => '__TIMEOUT__'],
82
            ['__TIMEOUT__' => $defaultTimeout]
83
        );
84
85
        $sessionHolder = new \stdClass();
86
        $middleware->addListener('pre.session_timeout', function($sessionId) use ($sessionHolder) {
87
            $sessionHolder->id = $sessionId;
88
        });
89
90
        $assert = $this;
91
        $middleware->addListener('post.session_timeout', function($sessionId) use ($assert, $sessionHolder) {
92
            $assert::assertNotNull($sessionHolder->id);
93
            $assert::assertNotEquals($sessionHolder->id, $sessionId);
94
        });
95
96
        $middleware($this->request, $this->response, $this->callback);
97
98
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
99
        self::assertTrue(array_key_exists('__TIMEOUT__', $_SESSION));
100
        self::assertNotEquals($_SESSION['__TIMEOUT__'], $defaultTimeout);
101
    }
102
103
    /**
104
     * @runInSeparateProcess
105
     *
106
     * @expectedException \InvalidArgumentException
107
     * @expectedExceptionMessage "  " is not a valid session timeout
108
     */
109
    public function testSessionErrorTimeoutControlKey()
110
    {
111
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'timeoutKey' => '  ']);
112
113
        $middleware($this->request, $this->response, $this->callback);
114
    }
115
116
    /**
117
     * @runInSeparateProcess
118
     */
119
    public function testSessionName()
120
    {
121
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
122
123
        /** @var Response $response */
124
        $response = $middleware($this->request, $this->response, $this->callback);
125
126
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
127
        self::assertEquals('SessionWareSession', session_name());
128
        self::assertSame(strpos($response->getHeaderLine('Set-Cookie'), 'SessionWareSession'), 0);
129
    }
130
131
    /**
132
     * @runInSeparateProcess
133
     */
134
    public function testSessionIdFromFunction()
135
    {
136
        session_id('madeUpSessionId');
137
138
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
139
140
        $middleware($this->request, $this->response, $this->callback);
141
142
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
143
        self::assertEquals('madeUpSessionId', session_id());
144
    }
145
146
    /**
147
     * @runInSeparateProcess
148
     */
149
    public function testSessionIdFromRequest()
150
    {
151
        $request = ServerRequestFactory::fromGlobals(null, null, null, ['SessionWareSession' => 'madeUpSessionId']);
152
153
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
154
155
        /** @var Response $response */
156
        $response = $middleware($request, $this->response, $this->callback);
157
158
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
159
        self::assertEquals('madeUpSessionId', session_id());
160
        self::assertNotSame(strpos($response->getHeaderLine('Set-Cookie'), 'madeUpSessionId'), false);
161
    }
162
163
    /**
164
     * @runInSeparateProcess
165
     */
166
    public function testGeneratedSessionId()
167
    {
168
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
169
170
        $middleware($this->request, $this->response, $this->callback);
171
172
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
173
        self::assertEquals(80, strlen(session_id()));
174
    }
175
176
    /**
177
     * @runInSeparateProcess
178
     */
179
    public function testSessionSavePathFromFunction()
180
    {
181
        $tmpPath = sys_get_temp_dir() . '/SessionWareSession';
182
183
        session_save_path($tmpPath);
184
185
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
186
187
        $middleware($this->request, $this->response, $this->callback);
188
189
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
190
        self::assertTrue(is_dir($tmpPath));
191
    }
192
193
    /**
194
     * @runInSeparateProcess
195
     */
196
    public function testSessionSavePathFromParameter()
197
    {
198
        $tmpPath = sys_get_temp_dir() . '/SessionWareSession';
199
200
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'savePath' => $tmpPath]);
201
202
        $middleware($this->request, $this->response, $this->callback);
203
204
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
205
        self::assertTrue(is_dir($tmpPath));
206
    }
207
208
    /**
209
     * @runInSeparateProcess
210
     *
211
     * @expectedException \RuntimeException
212
     * @expectedExceptionMessageRegExp /^Failed to create session save path/
213
     */
214
    public function testSessionErrorSavePath()
215
    {
216
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'savePath' => '/my-fake-dir']);
217
218
        $middleware($this->request, $this->response, $this->callback);
219
    }
220
221
    /**
222
     * @runInSeparateProcess
223
     */
224
    public function testSessionTimeoutDefault()
225
    {
226
        ini_set('session.cookie_lifetime', 0);
227
        ini_set('session.gc_maxlifetime', 0);
228
229
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
230
231
        $middleware($this->request, $this->response, $this->callback);
232
233
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
234
        self::assertEquals(SessionWare::SESSION_LIFETIME_DEFAULT, ini_get('session.gc_maxlifetime'));
235
    }
236
237
    /**
238
     * @runInSeparateProcess
239
     */
240
    public function testSessionTimeoutByCookieLifetime()
241
    {
242
        ini_set('session.cookie_lifetime', 10);
243
        ini_set('session.gc_maxlifetime', SessionWare::SESSION_LIFETIME_EXTENDED);
244
245
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
246
247
        $middleware($this->request, $this->response, $this->callback);
248
249
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
250
        self::assertEquals(10, ini_get('session.gc_maxlifetime'));
251
    }
252
253
    /**
254
     * @runInSeparateProcess
255
     */
256
    public function testSessionTimeoutByMaxLifetime()
257
    {
258
        ini_set('session.cookie_lifetime', 0);
259
        ini_set('session.gc_maxlifetime', SessionWare::SESSION_LIFETIME_NORMAL);
260
261
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
262
263
        $middleware($this->request, $this->response, $this->callback);
264
265
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
266
        self::assertEquals(SessionWare::SESSION_LIFETIME_NORMAL, ini_get('session.gc_maxlifetime'));
267
    }
268
269
    /**
270
     * @runInSeparateProcess
271
     */
272
    public function testSessionTimeoutByParameter()
273
    {
274
        $middleware = new SessionWare([
275
            'name' => 'SessionWareSession',
276
            'lifetime' => SessionWare::SESSION_LIFETIME_SHORT,
277
        ]);
278
279
        $middleware($this->request, $this->response, $this->callback);
280
281
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
282
        self::assertEquals(SessionWare::SESSION_LIFETIME_SHORT, ini_get('session.gc_maxlifetime'));
283
    }
284
285
    /**
286
     * @runInSeparateProcess
287
     *
288
     * @expectedException \InvalidArgumentException
289
     * @expectedExceptionMessage "0" is not a valid session lifetime
290
     */
291
    public function testSessionErrorTimeout()
292
    {
293
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'lifetime' => 0]);
294
295
        $middleware($this->request, $this->response, $this->callback);
296
    }
297
298
    /**
299
     * @runInSeparateProcess
300
     */
301
    public function testSessionDefaultParams()
0 ignored issues
show
Coding Style introduced by
testSessionDefaultParams uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
302
    {
303
        $middleware = new SessionWare(['name' => 'SessionWareSession'], ['parameter' => 'value']);
304
305
        $middleware($this->request, $this->response, $this->callback);
306
307
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
308
        self::assertEquals('value', $_SESSION['parameter']);
309
    }
310
311
    /**
312
     * @runInSeparateProcess
313
     */
314
    public function testSessionCookieParams()
315
    {
316
        $middleware = new SessionWare([
317
            'name' => 'SessionWareSession',
318
            'lifetime' => 300,
319
            'domain' => 'http://example.com',
320
            'path' => 'path',
321
            'secure' => true,
322
            'httponly' => true,
323
        ]);
324
325
        /** @var Response $response */
326
        $response = $middleware($this->request, $this->response, $this->callback);
327
328
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
329
330
        $cookieHeader = $response->getHeaderLine('Set-Cookie');
331
        self::assertTrue(strpos($cookieHeader, 'SessionWare') !== false);
332
        self::assertTrue(strpos($cookieHeader, 'http://example.com') !== false);
333
        self::assertTrue(strpos($cookieHeader, 'path') !== false);
334
        self::assertTrue(strpos($cookieHeader, 'secure') !== false);
335
        self::assertTrue(strpos($cookieHeader, 'httponly') !== false);
336
    }
337
}
338