Completed
Push — master ( 5ca900...5fe513 )
by Julián
08:26
created

SessionWareTest::testSessionEndedCookieParams()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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