tempDir = sys_get_temp_dir() . '/php_sessions_test_' . uniqid(); mkdir($this->tempDir, 0700, true); $this->clock = new SystemClock(); $this->storage = new FileSessionStorage($this->tempDir, $this->clock); $this->sessionIdGenerator = new SessionIdGenerator(new SecureRandomGenerator()); $this->csrfTokenGenerator = new CsrfTokenGenerator(new SecureRandomGenerator()); $this->formIdGenerator = new FormIdGenerator(); $this->cookieConfig = new SessionCookieConfig( name: 'test_session', lifetime: 3600, path: '/', domain: null, secure: false, httpOnly: true, sameSite: \App\Framework\Http\Cookies\SameSite::LAX ); $this->sessionManager = new SessionManager( generator: $this->sessionIdGenerator, responseManipulator: Mockery::mock(\App\Framework\Http\ResponseManipulator::class), clock: $this->clock, csrfTokenGenerator: $this->csrfTokenGenerator, storage: $this->storage, cookieConfig: $this->cookieConfig ); $this->sessionId = $this->sessionIdGenerator->generate(); $this->session = Session::fromArray($this->sessionId, $this->clock, $this->csrfTokenGenerator, []); $this->processor = new FormDataResponseProcessor( $this->formIdGenerator, $this->sessionManager ); }); afterEach(function () { if (isset($this->tempDir) && is_dir($this->tempDir)) { array_map('unlink', glob($this->tempDir . '/*')); rmdir($this->tempDir); } }); it('processes form HTML and replaces token placeholder', function () { $formId = $this->formIdGenerator->generateFormId('/test', 'post'); $html = << Test
HTML; $processed = $this->processor->process($html, $this->session); // Token should be replaced expect($processed)->not->toContain("___TOKEN_{$formId}___"); // Should contain a valid token preg_match('/name="_token"[^>]*value="([^"]+)"/', $processed, $matches); expect($matches)->toHaveCount(2); $token = $matches[1]; expect(strlen($token))->toBe(64); expect(ctype_xdigit($token))->toBeTrue(); // Token should be valid in session $tokenObj = \App\Framework\Security\CsrfToken::fromString($token); $result = $this->session->csrf->validateTokenWithDebug($formId, $tokenObj); expect($result['valid'])->toBeTrue(); }); it('processes multiple forms with different form IDs', function () { $formId1 = $this->formIdGenerator->generateFormId('/form1', 'post'); $formId2 = $this->formIdGenerator->generateFormId('/form2', 'post'); $html = <<
HTML; $processed = $this->processor->process($html, $this->session); // Both tokens should be replaced expect($processed)->not->toContain("___TOKEN_{$formId1}___"); expect($processed)->not->toContain("___TOKEN_{$formId2}___"); // Extract tokens preg_match_all('/name="_token"[^>]*value="([^"]+)"/', $processed, $matches); expect($matches[1])->toHaveCount(2); $token1 = $matches[1][0]; $token2 = $matches[1][1]; // Tokens should be different expect($token1)->not->toBe($token2); // Both should be valid expect(strlen($token1))->toBe(64); expect(strlen($token2))->toBe(64); }); it('handles malformed HTML gracefully', function () { $formId = $this->formIdGenerator->generateFormId('/test', 'post'); // HTML with unclosed tags $html = <<
HTML; // Should not throw exception $processed = $this->processor->process($html, $this->session); // Should still replace token (via regex fallback) expect($processed)->not->toContain("___TOKEN_{$formId}___"); }); it('preserves HTML structure after processing', function () { $formId = $this->formIdGenerator->generateFormId('/test', 'post'); $html = << Test Page

Test Form

HTML; $processed = $this->processor->process($html, $this->session); // Should preserve structure expect($processed)->toContain(''); expect($processed)->toContain(''); expect($processed)->toContain(''); expect($processed)->toContain('Test Page'); expect($processed)->toContain('

Test Form

'); expect($processed)->toContain(''); expect($processed)->toContain(''); // Token should be replaced expect($processed)->not->toContain("___TOKEN_{$formId}___"); });