SessionWareTest::testSessionName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
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
        // Set a high probability to launch garbage collector
42
        ini_set('session.gc_probability', 1);
43
        ini_set('session.gc_divisor', 4);
44
45
        if (session_status() === PHP_SESSION_ACTIVE) {
46
            session_unset();
47
            session_destroy();
48
        }
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
     * @preserveGlobalState disabled
60
     *
61
     * @expectedException \RuntimeException
62
     * @expectedExceptionMessageRegExp /^Session has already been started/
63
     */
64
    public function testSessionAlreadyStarted()
65
    {
66
        session_id('SessionWareSession');
67
68
        session_start();
69
70
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
71
72
        $middleware($this->request, $this->response, $this->callback);
73
    }
74
75
    /**
76
     * @runInSeparateProcess
77
     * @preserveGlobalState disabled
78
     */
79
    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...
80
    {
81
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'timeoutKey' => '__TIMEOUT__']);
82
83
        $middleware($this->request, $this->response, $this->callback);
84
85
        $limitTimeout = time() - SessionWare::SESSION_LIFETIME_EXTENDED;
86
        $_SESSION['__TIMEOUT__'] = $limitTimeout;
87
        session_write_close();
88
89
        $sessionHolder = new \stdClass();
90
        $middleware->addListener('pre.session_timeout', function ($sessionId) use ($sessionHolder) {
91
            $sessionHolder->id = $sessionId;
92
        });
93
94
        $assert = $this;
95
        $middleware->addListener('post.session_timeout', function ($sessionId) use ($assert, $sessionHolder) {
96
            $assert::assertNotNull($sessionHolder->id);
97
            $assert::assertNotEquals($sessionHolder->id, $sessionId);
98
        });
99
100
        $middleware($this->request, $this->response, $this->callback);
101
102
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
103
        self::assertTrue(array_key_exists('__TIMEOUT__', $_SESSION));
104
        self::assertNotEquals($_SESSION['__TIMEOUT__'], $limitTimeout);
105
    }
106
107
    /**
108
     * @runInSeparateProcess
109
     * @preserveGlobalState disabled
110
     *
111
     * @expectedException \InvalidArgumentException
112
     * @expectedExceptionMessage "  " is not a valid session timeout
113
     */
114
    public function testSessionErrorTimeoutControlKey()
115
    {
116
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'timeoutKey' => '  ']);
117
118
        $middleware($this->request, $this->response, $this->callback);
119
    }
120
121
    /**
122
     * @runInSeparateProcess
123
     * @preserveGlobalState disabled
124
     *
125
     * @expectedException \InvalidArgumentException
126
     * @expectedExceptionMessage Session name must be a non empty string
127
     */
128
    public function testEmptySessionName()
129
    {
130
        $middleware = new SessionWare(['name' => '']);
131
132
        $middleware($this->request, $this->response, $this->callback);
133
    }
134
135
    /**
136
     * @runInSeparateProcess
137
     * @preserveGlobalState disabled
138
     */
139
    public function testSessionName()
140
    {
141
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
142
143
        /** @var Response $response */
144
        $response = $middleware($this->request, $this->response, $this->callback);
145
146
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
147
        self::assertEquals('SessionWareSession', session_name());
148
        self::assertSame(strpos($response->getHeaderLine('Set-Cookie'), 'SessionWareSession'), 0);
149
    }
150
151
    /**
152
     * @runInSeparateProcess
153
     * @preserveGlobalState disabled
154
     */
155
    public function testSessionIdFromFunction()
156
    {
157
        $sessionId = SessionWare::generateSessionId();
158
159
        session_id($sessionId);
160
161
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
162
163
        /** @var Response $response */
164
        $response = $middleware($this->request, $this->response, $this->callback);
165
166
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
167
        self::assertNotSame(strpos($response->getHeaderLine('Set-Cookie'), $sessionId), false);
168
    }
169
170
    /**
171
     * @runInSeparateProcess
172
     * @preserveGlobalState disabled
173
     */
174
    public function testSessionIdFromRequest()
175
    {
176
        $sessionId = SessionWare::generateSessionId();
177
178
        $request = ServerRequestFactory::fromGlobals(null, null, null, ['SessionWareSession' => $sessionId]);
179
180
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
181
182
        /** @var Response $response */
183
        $response = $middleware($request, $this->response, $this->callback);
184
185
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
186
        self::assertNotSame(strpos($response->getHeaderLine('Set-Cookie'), $sessionId), false);
187
    }
188
189
    /**
190
     * @runInSeparateProcess
191
     * @preserveGlobalState disabled
192
     */
193
    public function testGeneratedSessionId()
194
    {
195
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
196
197
        $middleware($this->request, $this->response, $this->callback);
198
199
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
200
    }
201
202
    /**
203
     * @runInSeparateProcess
204
     * @preserveGlobalState disabled
205
     */
206
    public function testSessionEmptySavePath()
207
    {
208
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'savePath' => '']);
209
210
        $middleware($this->request, $this->response, $this->callback);
211
212
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
213
        self::assertTrue(is_dir(sys_get_temp_dir() . '/SessionWareSession'));
214
    }
215
216
    /**
217
     * @runInSeparateProcess
218
     * @preserveGlobalState disabled
219
     */
220
    public function testSessionSavePathFromFunction()
221
    {
222
        $tmpPath = sys_get_temp_dir() . '/SessionWareSession';
223
224
        session_save_path($tmpPath);
225
226
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
227
228
        $middleware($this->request, $this->response, $this->callback);
229
230
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
231
        self::assertTrue(is_dir($tmpPath));
232
    }
233
234
    /**
235
     * @runInSeparateProcess
236
     * @preserveGlobalState disabled
237
     */
238
    public function testSessionSavePathFromParameter()
239
    {
240
        $tmpPath = sys_get_temp_dir() . '/SessionWareSession';
241
242
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'savePath' => $tmpPath]);
243
244
        $middleware($this->request, $this->response, $this->callback);
245
246
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
247
        self::assertTrue(is_dir($tmpPath));
248
    }
249
250
    /**
251
     * @runInSeparateProcess
252
     * @preserveGlobalState disabled
253
     *
254
     * @expectedException \RuntimeException
255
     * @expectedExceptionMessageRegExp /^Failed to create session save path/
256
     */
257
    public function testSessionErrorSavePath()
258
    {
259
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'savePath' => '/my-fake-dir']);
260
261
        $middleware($this->request, $this->response, $this->callback);
262
    }
263
264
    /**
265
     * @runInSeparateProcess
266
     * @preserveGlobalState disabled
267
     */
268
    public function testSessionTimeoutDefault()
269
    {
270
        ini_set('session.cookie_lifetime', 0);
271
        ini_set('session.gc_maxlifetime', 0);
272
273
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
274
275
        $middleware($this->request, $this->response, $this->callback);
276
277
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
278
        self::assertEquals(SessionWare::SESSION_LIFETIME_DEFAULT, ini_get('session.gc_maxlifetime'));
279
    }
280
281
    /**
282
     * @runInSeparateProcess
283
     * @preserveGlobalState disabled
284
     */
285
    public function testSessionTimeoutByCookieLifetime()
286
    {
287
        ini_set('session.cookie_lifetime', 10);
288
        ini_set('session.gc_maxlifetime', SessionWare::SESSION_LIFETIME_EXTENDED);
289
290
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
291
292
        $middleware($this->request, $this->response, $this->callback);
293
294
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
295
        self::assertEquals(10, ini_get('session.gc_maxlifetime'));
296
    }
297
298
    /**
299
     * @runInSeparateProcess
300
     * @preserveGlobalState disabled
301
     */
302
    public function testSessionTimeoutByMaxLifetime()
303
    {
304
        ini_set('session.cookie_lifetime', 0);
305
        ini_set('session.gc_maxlifetime', SessionWare::SESSION_LIFETIME_NORMAL);
306
307
        $middleware = new SessionWare(['name' => 'SessionWareSession']);
308
309
        $middleware($this->request, $this->response, $this->callback);
310
311
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
312
        self::assertEquals(SessionWare::SESSION_LIFETIME_NORMAL, ini_get('session.gc_maxlifetime'));
313
    }
314
315
    /**
316
     * @runInSeparateProcess
317
     * @preserveGlobalState disabled
318
     */
319
    public function testSessionTimeoutByParameter()
320
    {
321
        $middleware = new SessionWare([
322
            'name' => 'SessionWareSession',
323
            'lifetime' => SessionWare::SESSION_LIFETIME_SHORT,
324
        ]);
325
326
        $middleware($this->request, $this->response, $this->callback);
327
328
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
329
        self::assertEquals(SessionWare::SESSION_LIFETIME_SHORT, ini_get('session.gc_maxlifetime'));
330
    }
331
332
    /**
333
     * @runInSeparateProcess
334
     * @preserveGlobalState disabled
335
     *
336
     * @expectedException \InvalidArgumentException
337
     * @expectedExceptionMessage Session lifetime must be at least 1
338
     */
339
    public function testSessionErrorTimeout()
340
    {
341
        $middleware = new SessionWare(['name' => 'SessionWareSession', 'lifetime' => 0]);
342
343
        $middleware($this->request, $this->response, $this->callback);
344
    }
345
346
    /**
347
     * @runInSeparateProcess
348
     * @preserveGlobalState disabled
349
     */
350
    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...
351
    {
352
        $middleware = new SessionWare(['name' => 'SessionWareSession'], ['parameter' => 'value']);
353
354
        $middleware($this->request, $this->response, $this->callback);
355
356
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
357
        self::assertEquals('value', $_SESSION['parameter']);
358
    }
359
360
    /**
361
     * @runInSeparateProcess
362
     * @preserveGlobalState disabled
363
     */
364
    public function testSessionCookieParams()
365
    {
366
        $middleware = new SessionWare([
367
            'name' => 'SessionWareSession',
368
            'lifetime' => 300,
369
            'domain' => 'http://example.com',
370
            'path' => 'path',
371
            'secure' => true,
372
            'httponly' => true,
373
        ]);
374
375
        /** @var Response $response */
376
        $response = $middleware($this->request, $this->response, $this->callback);
377
378
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
379
380
        $cookieHeader = $response->getHeaderLine('Set-Cookie');
381
        self::assertTrue(strpos($cookieHeader, 'SessionWareSession') !== false);
382
        self::assertTrue(strpos($cookieHeader, 'http://example.com') !== false);
383
        self::assertTrue(strpos($cookieHeader, 'path') !== false);
384
        self::assertTrue(strpos($cookieHeader, 'secure') !== false);
385
        self::assertTrue(strpos($cookieHeader, 'httponly') !== false);
386
    }
387
388
    /**
389
     * @runInSeparateProcess
390
     * @preserveGlobalState disabled
391
     */
392
    public function testSessionEndedCookieParams()
0 ignored issues
show
Coding Style introduced by
testSessionEndedCookieParams 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...
393
    {
394
        $middleware = new SessionWare([
395
            'name' => 'SessionWareSession',
396
            'lifetime' => 300,
397
            'domain' => 'http://example.com',
398
        ]);
399
        $expiration = gmdate('D, d M Y H:i:s T', time() - 300);
400
401
        /** @var Response $response */
402
        $response = $middleware(
403
            $this->request,
404
            $this->response,
405
            function ($request, $response) {
406
                $_SESSION = [];
407
408
                // Serialization is not allowed in PHPUnit running on a separate process
409
                //session_unset();
410
                //session_destroy();
411
412
                return $response;
413
            }
414
        );
415
416
        self::assertEquals(PHP_SESSION_ACTIVE, session_status());
417
418
        $cookieHeader = $response->getHeaderLine('Set-Cookie');
419
        self::assertTrue(strpos($cookieHeader, 'SessionWareSession') !== false);
420
        self::assertTrue(strpos($cookieHeader, 'http://example.com') !== false);
421
        self::assertTrue(strpos($cookieHeader, $expiration) !== false);
422
    }
423
}
424