‪TYPO3CMS  ‪main
RedisSessionBackendTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
24 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
25 
29 final class ‪RedisSessionBackendTest extends FunctionalTestCase
30 {
31  protected bool ‪$initializeDatabase = false;
32 
36  protected ‪$subject;
37 
41  protected ‪$redis;
42 
46  protected ‪$testSessionRecord = [
47  // RedisSessionBackend::hash('randomSessionId') with encryption key 12345
48  'ses_id' => '21c0e911565a67315cdc384889c470fd291feafbfa62e31ecf7409430640bc7a',
49  'ses_userid' => 1,
50  // serialize(['foo' => 'bar', 'boo' => 'far'])
51  'ses_data' => 'a:2:{s:3:"foo";s:3:"bar";s:3:"boo";s:3:"far";}',
52  ];
53 
57  protected function ‪setUp(): void
58  {
59  parent::setUp();
60  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '12345';
61 
62  if (!getenv('typo3TestingRedisHost')) {
63  self::markTestSkipped('environment variable "typo3TestingRedisHost" must be set to run this test');
64  }
65  // Note we assume that if that typo3TestingRedisHost env is set, we can use that for testing,
66  // there is no test to see if the daemon is actually up and running. Tests will fail if env
67  // is set but daemon is down.
68 
69  // We know this env is set, otherwise setUp() would skip the tests
70  $redisHost = getenv('typo3TestingRedisHost');
71  // If typo3TestingRedisPort env is set, use it, otherwise fall back to standard port
72  $env = getenv('typo3TestingRedisPort');
73  $redisPort = is_string($env) ? (int)$env : 6379;
74 
75  $this->redis = new \Redis();
76  $this->redis->connect($redisHost, $redisPort);
77  $this->redis->select(0);
78  // Clear db to ensure no sessions exist currently
79  $this->redis->flushDB();
80 
81  $this->subject = new ‪RedisSessionBackend();
82  $this->subject->initialize(
83  'default',
84  [
85  'database' => 0,
86  'port' => $redisPort,
87  'hostname' => $redisHost,
88  ]
89  );
90  }
91 
95  public function ‪cannotUpdateNonExistingRecord(): void
96  {
97  $this->expectException(SessionNotUpdatedException::class);
98  $this->expectExceptionCode(1484389971);
99  $this->subject->update('iSoNotExist', []);
100  }
101 
105  public function ‪canValidateSessionBackend(): void
106  {
107  $this->subject->validateConfiguration();
108  }
109 
113  public function ‪sessionDataIsStoredProperly(): void
114  {
115  ‪$record = $this->subject->set('randomSessionId', $this->testSessionRecord);
116 
117  $expected = array_merge($this->testSessionRecord, ['ses_tstamp' => ‪$GLOBALS['EXEC_TIME']]);
118 
119  self::assertEquals(‪$record, $expected);
120  $result = $this->subject->get('randomSessionId');
121  self::assertSame($expected, $result);
122  }
123 
127  public function ‪anonymousSessionDataIsStoredProperly(): void
128  {
129  ‪$record = $this->subject->set('randomSessionId', array_merge($this->testSessionRecord, ['ses_userid' => 0]));
130 
131  $expected = array_merge($this->testSessionRecord, ['ses_userid' => 0, 'ses_tstamp' => ‪$GLOBALS['EXEC_TIME']]);
132 
133  self::assertEquals(‪$record, $expected);
134  self::assertSame($expected, $this->subject->get('randomSessionId'));
135  }
136 
140  public function ‪throwExceptionOnNonExistingSessionId(): void
141  {
142  $this->expectException(SessionNotFoundException::class);
143  $this->expectExceptionCode(1481885583);
144  $this->subject->get('IDoNotExist');
145  }
146 
150  public function ‪mergeSessionDataWithNewData(): void
151  {
152  $this->subject->set('randomSessionId', $this->testSessionRecord);
153 
154  $updateData = [
155  'ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire']),
156  'ses_tstamp' => ‪$GLOBALS['EXEC_TIME'],
157  ];
158  $expectedMergedData = array_merge($this->testSessionRecord, $updateData);
159  $this->subject->update('randomSessionId', $updateData);
160  $fetchedRecord = $this->subject->get('randomSessionId');
161  self::assertSame($expectedMergedData, $fetchedRecord);
162  }
163 
167  public function ‪existingSessionMustNotBeOverridden(): void
168  {
169  $this->expectException(SessionNotCreatedException::class);
170  $this->expectExceptionCode(1481895647);
171 
172  $this->subject->set('randomSessionId', $this->testSessionRecord);
173 
174  $newData = array_merge($this->testSessionRecord, ['ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire'])]);
175  $this->subject->set('randomSessionId', $newData);
176  }
177 
181  public function ‪cannotChangeSessionId(): void
182  {
183  $this->subject->set('randomSessionId', $this->testSessionRecord);
184 
185  $newSessionId = 'newRandomSessionId';
186  $newData = array_merge($this->testSessionRecord, ['ses_id' => $newSessionId]);
187 
188  // old session id has to exist, no exception must be thrown at this point
189  $this->subject->get('randomSessionId');
190 
191  // Change session id
192  $this->subject->update('randomSessionId', $newData);
193 
194  // no session with key newRandomSessionId should exist
195  $this->expectException(SessionNotFoundException::class);
196  $this->expectExceptionCode(1481885583);
197  $this->subject->get('newRandomSessionId');
198  }
199 
203  public function ‪sessionGetsDestroyed(): void
204  {
205  $this->subject->set('randomSessionId', $this->testSessionRecord);
206 
207  // Remove session
208  self::assertTrue($this->subject->remove('randomSessionId'));
209 
210  // Check if session was really removed
211  $this->expectException(SessionNotFoundException::class);
212  $this->expectExceptionCode(1481885583);
213  $this->subject->get('randomSessionId');
214  }
215 
219  public function ‪canLoadAllSessions(): void
220  {
221  $this->subject->set('randomSessionId', $this->testSessionRecord);
222  $this->subject->set('randomSessionId2', $this->testSessionRecord);
223 
224  // Check if session was really removed
225  self::assertCount(2, $this->subject->getAll());
226  }
227 
231  public function ‪canCollectGarbage(): void
232  {
233  ‪$GLOBALS['EXEC_TIME'] = 150;
234  $authenticatedSession = array_merge($this->testSessionRecord, ['ses_id' => 'authenticatedSession']);
235  $anonymousSession = array_merge($this->testSessionRecord, ['ses_id' => 'anonymousSession', 'ses_userid' => 0]);
236 
237  $this->subject->set('authenticatedSession', $authenticatedSession);
238  $this->subject->set('anonymousSession', $anonymousSession);
239 
240  // Assert that we set authenticated session correctly
241  self::assertSame(
242  $authenticatedSession['ses_data'],
243  $this->subject->get('authenticatedSession')['ses_data']
244  );
245  self::assertSame(
246  $authenticatedSession['ses_userid'],
247  $this->subject->get('authenticatedSession')['ses_userid']
248  );
249 
250  // assert that we set anonymous session correctly
251  self::assertSame(
252  $anonymousSession['ses_data'],
253  $this->subject->get('anonymousSession')['ses_data']
254  );
255 
256  // Run the garbage collection
257  ‪$GLOBALS['EXEC_TIME'] = 200;
258  // 150 + 10 < 200 but 150 + 60 >= 200
259  $this->subject->collectGarbage(60, 10);
260 
261  // Authenticated session should still be there
262  self::assertSame(
263  $authenticatedSession['ses_data'],
264  $this->subject->get('authenticatedSession')['ses_data']
265  );
266  self::assertSame(
267  $authenticatedSession['ses_userid'],
268  $this->subject->get('authenticatedSession')['ses_userid']
269  );
270 
271  // Non-authenticated session should be removed
272  $this->expectException(SessionNotFoundException::class);
273  $this->expectExceptionCode(1481885583);
274  $this->subject->get('anonymousSession');
275  }
276 
280  public function ‪canPartiallyUpdateAfterGet(): void
281  {
282  $updatedRecord = array_merge(
283  $this->testSessionRecord,
284  ['ses_tstamp' => ‪$GLOBALS['EXEC_TIME']]
285  );
286  $sessionId = 'randomSessionId';
287  $this->subject->set($sessionId, $this->testSessionRecord);
288  $this->subject->update($sessionId, []);
289  self::assertSame($updatedRecord, $this->subject->get($sessionId));
290  }
291 }
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$initializeDatabase
‪bool $initializeDatabase
Definition: RedisSessionBackendTest.php:31
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\setUp
‪setUp()
Definition: RedisSessionBackendTest.php:54
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotCreatedException
Definition: SessionNotCreatedException.php:23
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\mergeSessionDataWithNewData
‪mergeSessionDataWithNewData()
Definition: RedisSessionBackendTest.php:147
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canPartiallyUpdateAfterGet
‪canPartiallyUpdateAfterGet()
Definition: RedisSessionBackendTest.php:277
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\anonymousSessionDataIsStoredProperly
‪anonymousSessionDataIsStoredProperly()
Definition: RedisSessionBackendTest.php:124
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\existingSessionMustNotBeOverridden
‪existingSessionMustNotBeOverridden()
Definition: RedisSessionBackendTest.php:164
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$testSessionRecord
‪array $testSessionRecord
Definition: RedisSessionBackendTest.php:43
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$subject
‪RedisSessionBackend $subject
Definition: RedisSessionBackendTest.php:35
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canValidateSessionBackend
‪canValidateSessionBackend()
Definition: RedisSessionBackendTest.php:102
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\sessionDataIsStoredProperly
‪sessionDataIsStoredProperly()
Definition: RedisSessionBackendTest.php:110
‪TYPO3\CMS\Core\Session\Backend\RedisSessionBackend
Definition: RedisSessionBackend.php:33
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canCollectGarbage
‪canCollectGarbage()
Definition: RedisSessionBackendTest.php:228
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotUpdatedException
Definition: SessionNotUpdatedException.php:23
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canLoadAllSessions
‪canLoadAllSessions()
Definition: RedisSessionBackendTest.php:216
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\cannotChangeSessionId
‪cannotChangeSessionId()
Definition: RedisSessionBackendTest.php:178
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend
Definition: DatabaseSessionBackendTest.php:18
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$redis
‪Redis $redis
Definition: RedisSessionBackendTest.php:39
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\sessionGetsDestroyed
‪sessionGetsDestroyed()
Definition: RedisSessionBackendTest.php:200
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\cannotUpdateNonExistingRecord
‪cannotUpdateNonExistingRecord()
Definition: RedisSessionBackendTest.php:92
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\throwExceptionOnNonExistingSessionId
‪throwExceptionOnNonExistingSessionId()
Definition: RedisSessionBackendTest.php:137
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException
Definition: SessionNotFoundException.php:23
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest
Definition: RedisSessionBackendTest.php:30