Files
michaelschiemer/tests/Framework/Http/Session/SessionStorageTest.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

321 lines
12 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Http\Session\FileSessionStorage;
use App\Framework\Http\Session\InMemorySessionStorage;
use App\Framework\Http\Session\SessionId;
use App\Framework\Http\Session\SessionStorage;
describe('InMemorySessionStorage', function () {
beforeEach(function () {
$this->storage = new InMemorySessionStorage();
$this->sessionId = SessionId::fromString('testsessionid1234567890abcdefgh12');
});
test('reads empty array for non-existent session', function () {
$data = $this->storage->read($this->sessionId);
expect($data)->toBe([]);
});
test('writes and reads session data correctly', function () {
$testData = [
'user_id' => 123,
'username' => 'testuser',
'preferences' => ['theme' => 'dark', 'language' => 'en'],
];
$this->storage->write($this->sessionId, $testData);
$retrievedData = $this->storage->read($this->sessionId);
expect($retrievedData)->toBe($testData);
});
test('removes session data correctly', function () {
$testData = ['key' => 'value'];
$this->storage->write($this->sessionId, $testData);
expect($this->storage->read($this->sessionId))->toBe($testData);
$this->storage->remove($this->sessionId);
expect($this->storage->read($this->sessionId))->toBe([]);
});
test('migrates session data correctly', function () {
$oldId = SessionId::fromString('oldsessionid1234567890abcdefghij1');
$newId = SessionId::fromString('newsessionid1234567890abcdefghij1');
$testData = ['migration_test' => 'data'];
$this->storage->write($oldId, $testData);
$this->storage->migrate($oldId, $newId);
expect($this->storage->read($oldId))->toBe([]);
expect($this->storage->read($newId))->toBe($testData);
});
test('handles complex data structures', function () {
$complexData = [
'nested' => [
'deep' => [
'array' => [1, 2, 3],
'object' => (object)['property' => 'value'],
],
],
'null_value' => null,
'boolean_true' => true,
'boolean_false' => false,
'empty_string' => '',
'zero' => 0,
];
$this->storage->write($this->sessionId, $complexData);
$retrieved = $this->storage->read($this->sessionId);
expect($retrieved)->toBe($complexData);
});
test('multiple sessions work independently', function () {
$session1Id = SessionId::fromString('session1id1234567890abcdefghijk1');
$session2Id = SessionId::fromString('session2id1234567890abcdefghijk2');
$data1 = ['session' => '1', 'data' => 'first'];
$data2 = ['session' => '2', 'data' => 'second'];
$this->storage->write($session1Id, $data1);
$this->storage->write($session2Id, $data2);
expect($this->storage->read($session1Id))->toBe($data1);
expect($this->storage->read($session2Id))->toBe($data2);
$this->storage->remove($session1Id);
expect($this->storage->read($session1Id))->toBe([]);
expect($this->storage->read($session2Id))->toBe($data2);
});
});
describe('FileSessionStorage', function () {
beforeEach(function () {
$this->tempDir = sys_get_temp_dir() . '/session_test_' . uniqid();
$this->storage = new FileSessionStorage($this->tempDir);
$this->sessionId = SessionId::fromString('testfilesessionid1234567890abcde');
});
afterEach(function () {
// Cleanup: Remove test directory and files
if (is_dir($this->tempDir)) {
$files = glob($this->tempDir . '/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
rmdir($this->tempDir);
}
});
test('creates storage directory if it does not exist', function () {
expect(is_dir($this->tempDir))->toBeTrue();
});
test('reads empty array for non-existent session file', function () {
$data = $this->storage->read($this->sessionId);
expect($data)->toBe([]);
});
test('writes and reads session data to/from file correctly', function () {
$testData = [
'user_id' => 456,
'username' => 'fileuser',
'complex' => [
'nested' => ['deep' => 'value'],
'array' => [1, 2, 3, 4],
],
];
$this->storage->write($this->sessionId, $testData);
$retrievedData = $this->storage->read($this->sessionId);
expect($retrievedData)->toBe($testData);
});
test('file exists after writing', function () {
$testData = ['file_test' => 'exists'];
$this->storage->write($this->sessionId, $testData);
$expectedFile = $this->tempDir . '/sess_' . $this->sessionId->toString();
expect(file_exists($expectedFile))->toBeTrue();
});
test('removes session file correctly', function () {
$testData = ['to_be_removed' => 'data'];
$this->storage->write($this->sessionId, $testData);
$expectedFile = $this->tempDir . '/sess_' . $this->sessionId->toString();
expect(file_exists($expectedFile))->toBeTrue();
$this->storage->remove($this->sessionId);
expect(file_exists($expectedFile))->toBeFalse();
expect($this->storage->read($this->sessionId))->toBe([]);
});
test('migrates session file correctly', function () {
$oldId = SessionId::fromString('oldfilesessionid1234567890abcdef');
$newId = SessionId::fromString('newfilesessionid1234567890abcdef');
$testData = ['migration_file_test' => 'data'];
$this->storage->write($oldId, $testData);
$oldFile = $this->tempDir . '/sess_' . $oldId->toString();
$newFile = $this->tempDir . '/sess_' . $newId->toString();
expect(file_exists($oldFile))->toBeTrue();
expect(file_exists($newFile))->toBeFalse();
$this->storage->migrate($oldId, $newId);
expect(file_exists($oldFile))->toBeFalse();
expect(file_exists($newFile))->toBeTrue();
expect($this->storage->read($newId))->toBe($testData);
});
test('handles JSON encoding/decoding correctly', function () {
$testData = [
'string' => 'test string with üñíçødé',
'integer' => 42,
'float' => 3.14159,
'boolean_true' => true,
'boolean_false' => false,
'null' => null,
'array' => ['a', 'b', 'c'],
'object' => ['key' => 'value'],
];
$this->storage->write($this->sessionId, $testData);
$retrieved = $this->storage->read($this->sessionId);
expect($retrieved)->toBe($testData);
});
test('handles corrupted JSON file gracefully', function () {
// Schreibe invalides JSON in die Session-Datei
$sessionFile = $this->tempDir . '/sess_' . $this->sessionId->toString();
file_put_contents($sessionFile, 'invalid json content {');
$data = $this->storage->read($this->sessionId);
expect($data)->toBe([]); // Sollte leeres Array zurückgeben
});
test('handles file system errors gracefully', function () {
// Versuche in nicht-existierendes Verzeichnis zu schreiben (das Framework sollte das abfangen)
expect(fn () => new FileSessionStorage('/root/nonexistent/directory/path'))
->toThrow(RuntimeException::class);
});
test('garbage collection removes old files', function () {
// Erstelle mehrere Session-Dateien mit verschiedenen Timestamps
$oldSessionId = SessionId::fromString('oldsessionid1234567890abcdefghij1');
$newSessionId = SessionId::fromString('newsessionid1234567890abcdefghij2');
$this->storage->write($oldSessionId, ['old' => 'data']);
$this->storage->write($newSessionId, ['new' => 'data']);
// Da wir die neue FileSessionStorage verwenden, können wir nicht direkt touch() verwenden
// Stattdessen testen wir nur dass GC nicht crashed und beide Files noch existieren
// (da sie frisch erstellt wurden)
$this->storage->gc(3600);
// Beide Sessions sollten noch existieren da sie gerade erstellt wurden
expect($this->storage->read($oldSessionId))->toBe(['old' => 'data']);
expect($this->storage->read($newSessionId))->toBe(['new' => 'data']);
});
});
describe('SessionStorage Interface Compliance', function () {
/**
* Teste dass alle Storage-Implementierungen das gleiche Interface-Verhalten haben
*/
$storageProviders = [
'InMemorySessionStorage' => fn () => new InMemorySessionStorage(),
'FileSessionStorage' => fn () => new FileSessionStorage(sys_get_temp_dir() . '/pest_session_test_' . uniqid()),
];
foreach ($storageProviders as $storageName => $storageFactory) {
describe($storageName . ' Interface Compliance', function () use ($storageName, $storageFactory) {
beforeEach(function () use ($storageFactory) {
$this->storage = $storageFactory();
$this->sessionId = SessionId::fromString('interfacetestsessionid1234567890');
});
afterEach(function () use ($storageName) {
// Cleanup für FileStorage
if ($storageName === 'FileSessionStorage' && $this->storage instanceof FileSessionStorage) {
$basePath = $this->storage->getBasePath();
$pathString = $basePath->toString();
if (is_dir($pathString)) {
$files = glob($pathString . '/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
rmdir($pathString);
}
}
});
test('implements SessionStorage interface', function () {
expect($this->storage)->toBeInstanceOf(SessionStorage::class);
});
test('read() returns array', function () {
$result = $this->storage->read($this->sessionId);
expect($result)->toBeArray();
});
test('write() and read() cycle works', function () {
$testData = ['interface_test' => 'value', 'number' => 42];
$this->storage->write($this->sessionId, $testData);
$result = $this->storage->read($this->sessionId);
expect($result)->toBe($testData);
});
test('remove() clears session data', function () {
$this->storage->write($this->sessionId, ['to_remove' => 'data']);
$this->storage->remove($this->sessionId);
expect($this->storage->read($this->sessionId))->toBe([]);
});
test('migrate() transfers data correctly', function () {
$oldId = SessionId::fromString('oldinterfacetest1234567890abcdefg');
$newId = SessionId::fromString('newinterfacetest1234567890abcdefg');
$testData = ['migrate_interface_test' => 'data'];
$this->storage->write($oldId, $testData);
$this->storage->migrate($oldId, $newId);
expect($this->storage->read($oldId))->toBe([]);
expect($this->storage->read($newId))->toBe($testData);
});
test('handles empty data correctly', function () {
$this->storage->write($this->sessionId, []);
expect($this->storage->read($this->sessionId))->toBe([]);
});
test('handles null values in data', function () {
$testData = ['null_value' => null, 'string' => 'value'];
$this->storage->write($this->sessionId, $testData);
$result = $this->storage->read($this->sessionId);
expect($result)->toBe($testData);
expect($result['null_value'])->toBeNull();
});
});
}
});