[TASK] Run tests on new bamboo infrastructure
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / Session / Backend / RedisSessionBackendTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Functional\Session\Backend;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotCreatedException;
19 use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
20 use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotUpdatedException;
21 use TYPO3\CMS\Core\Session\Backend\RedisSessionBackend;
22 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
23
24 /**
25 * Test case
26 */
27 class RedisSessionBackendTest extends FunctionalTestCase
28 {
29 /**
30 * @var RedisSessionBackend Prepared and connected redis test subject
31 */
32 protected $subject;
33
34 /**
35 * @var array
36 */
37 protected $testSessionRecord = [
38 'ses_id' => 'randomSessionId',
39 'ses_userid' => 1,
40 // serialize(['foo' => 'bar', 'boo' => 'far'])
41 'ses_data' => 'a:2:{s:3:"foo";s:3:"bar";s:3:"boo";s:3:"far";}',
42 ];
43
44 /**
45 * Set configuration for RedisSessionBackend
46 */
47 protected function setUp()
48 {
49 parent::setUp();
50
51 if (!extension_loaded('redis')) {
52 $this->markTestSkipped('redis extension was not available');
53 }
54 if (!getenv('typo3TestingRedisHost')) {
55 $this->markTestSkipped('environment variable "typo3TestingRedisHost" must be set to run this test');
56 }
57 // Note we assume that if that typo3TestingRedisHost env is set, we can use that for testing,
58 // there is no test to see if the daemon is actually up and running. Tests will fail if env
59 // is set but daemon is down.
60
61 // We know this env is set, otherwise setUp() would skip the tests
62 $redisHost = getenv('typo3TestingRedisHost');
63 // If typo3TestingRedisPort env is set, use it, otherwise fall back to standard port
64 $env = getenv('typo3TestingRedisPort');
65 $redisPort = is_string($env) ? (int)$env : 6379;
66
67 $redis = new \Redis();
68 $redis->connect($redisHost, $redisPort);
69 $redis->select(0);
70 // Clear db to ensure no sessions exist currently
71 $redis->flushDB();
72
73 $this->subject = new RedisSessionBackend();
74 $this->subject->initialize(
75 'default',
76 [
77 'database' => 0,
78 'port' => $redisPort,
79 'hostname' => $redisHost
80 ]
81 );
82 }
83
84 /**
85 * @test
86 */
87 public function cannotUpdateNonExistingRecord()
88 {
89 $this->expectException(SessionNotUpdatedException::class);
90 $this->expectExceptionCode(1484389971);
91 $this->subject->update('iSoNotExist', []);
92 }
93
94 /**
95 * @test
96 */
97 public function canValidateSessionBackend()
98 {
99 $this->subject->validateConfiguration();
100 }
101
102 /**
103 * @test
104 * @covers SessionBackendInterface::set
105 */
106 public function sessionDataIsStoredProperly()
107 {
108 $record = $this->subject->set('randomSessionId', $this->testSessionRecord);
109
110 $expected = array_merge($this->testSessionRecord, ['ses_tstamp' => $GLOBALS['EXEC_TIME']]);
111
112 $this->assertEquals($record, $expected);
113 $this->assertArraySubset($expected, $this->subject->get('randomSessionId'));
114 }
115
116 /**
117 * @test
118 */
119 public function anonymousSessionDataIsStoredProperly()
120 {
121 $record = $this->subject->set('randomSessionId', array_merge($this->testSessionRecord, ['ses_anonymous' => 1]));
122
123 $expected = array_merge($this->testSessionRecord, ['ses_anonymous' => 1, 'ses_tstamp' => $GLOBALS['EXEC_TIME']]);
124
125 $this->assertEquals($record, $expected);
126 $this->assertArraySubset($expected, $this->subject->get('randomSessionId'));
127 }
128
129 /**
130 * @test
131 * @covers SessionBackendInterface::get
132 */
133 public function throwExceptionOnNonExistingSessionId()
134 {
135 $this->expectException(SessionNotFoundException::class);
136 $this->expectExceptionCode(1481885583);
137 $this->subject->get('IDoNotExist');
138 }
139
140 /**
141 * @test
142 * @covers SessionBackendInterface::update
143 */
144 public function mergeSessionDataWithNewData()
145 {
146 $this->subject->set('randomSessionId', $this->testSessionRecord);
147
148 $updateData = [
149 'ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire']),
150 'ses_tstamp' => $GLOBALS['EXEC_TIME']
151 ];
152 $expectedMergedData = array_merge($this->testSessionRecord, $updateData);
153 $this->subject->update('randomSessionId', $updateData);
154 $fetchedRecord = $this->subject->get('randomSessionId');
155 $this->assertArraySubset($expectedMergedData, $fetchedRecord);
156 }
157
158 /**
159 * @test
160 * @covers SessionBackendInterface::set
161 */
162 public function existingSessionMustNotBeOverridden()
163 {
164 $this->expectException(SessionNotCreatedException::class);
165 $this->expectExceptionCode(1481895647);
166
167 $this->subject->set('randomSessionId', $this->testSessionRecord);
168
169 $newData = array_merge($this->testSessionRecord, ['ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire'])]);
170 $this->subject->set('randomSessionId', $newData);
171 }
172
173 /**
174 * @test
175 * @covers SessionBackendInterface::update
176 */
177 public function cannotChangeSessionId()
178 {
179 $this->subject->set('randomSessionId', $this->testSessionRecord);
180
181 $newSessionId = 'newRandomSessionId';
182 $newData = array_merge($this->testSessionRecord, ['ses_id' => $newSessionId]);
183
184 // old session id has to exist, no exception must be thrown at this point
185 $this->subject->get('randomSessionId');
186
187 // Change session id
188 $this->subject->update('randomSessionId', $newData);
189
190 // no session with key newRandomSessionId should exist
191 $this->expectException(SessionNotFoundException::class);
192 $this->expectExceptionCode(1481885583);
193 $this->subject->get('newRandomSessionId');
194 }
195
196 /**
197 * @test
198 * @covers SessionBackendInterface::remove
199 */
200 public function sessionGetsDestroyed()
201 {
202 $this->subject->set('randomSessionId', $this->testSessionRecord);
203
204 // Remove session
205 $this->assertTrue($this->subject->remove('randomSessionId'));
206
207 // Check if session was really removed
208 $this->expectException(SessionNotFoundException::class);
209 $this->expectExceptionCode(1481885583);
210 $this->subject->get('randomSessionId');
211 }
212
213 /**
214 * @test
215 * @covers SessionBackendInterface::getAll
216 */
217 public function canLoadAllSessions()
218 {
219 $this->subject->set('randomSessionId', $this->testSessionRecord);
220 $this->subject->set('randomSessionId2', $this->testSessionRecord);
221
222 // Check if session was really removed
223 $this->assertEquals(2, count($this->subject->getAll()));
224 }
225
226 /**
227 * @test
228 */
229 public function canCollectGarbage()
230 {
231 $GLOBALS['EXEC_TIME'] = 150;
232 $authenticatedSession = array_merge($this->testSessionRecord, ['ses_id' => 'authenticatedSession']);
233 $anonymousSession = array_merge($this->testSessionRecord, ['ses_id' => 'anonymousSession', 'ses_anonymous' => 1]);
234
235 $this->subject->set('authenticatedSession', $authenticatedSession);
236 $this->subject->set('anonymousSession', $anonymousSession);
237
238 // Assert that we set authenticated session correctly
239 $this->assertArraySubset(
240 $authenticatedSession,
241 $this->subject->get('authenticatedSession')
242 );
243
244 // assert that we set anonymous session correctly
245 $this->assertArraySubset(
246 $anonymousSession,
247 $this->subject->get('anonymousSession')
248 );
249
250 // Run the garbage collection
251 $GLOBALS['EXEC_TIME'] = 200;
252 // 150 + 10 < 200 but 150 + 60 >= 200
253 $this->subject->collectGarbage(60, 10);
254
255 // Authenticated session should still be there
256 $this->assertArraySubset(
257 $authenticatedSession,
258 $this->subject->get('authenticatedSession')
259 );
260
261 // Non-authenticated session should be removed
262 $this->expectException(SessionNotFoundException::class);
263 $this->expectExceptionCode(1481885583);
264 $this->subject->get('anonymousSession');
265 }
266
267 /**
268 * @test
269 */
270 public function canPartiallyUpdateAfterGet()
271 {
272 $updatedRecord = array_merge(
273 $this->testSessionRecord,
274 ['ses_tstamp' => $GLOBALS['EXEC_TIME']]
275 );
276 $sessionId = 'randomSessionId';
277 $this->subject->set($sessionId, $this->testSessionRecord);
278 $this->subject->update($sessionId, []);
279 $this->assertArraySubset($updatedRecord, $this->subject->get($sessionId));
280 }
281 }