cache = Mockery::mock(Cache::class); $this->strategy = new FullPageCacheStrategy($this->cache); }); afterEach(function () { Mockery::close(); }); describe('shouldCache()', function () { it('returns true for static content without user data', function () { $context = new TemplateContext( template: 'static-page', data: ['title' => 'Welcome', 'content' => 'Hello World'] ); expect($this->strategy->shouldCache($context))->toBeTrue(); }); it('returns false when user data is present', function () { $context = new TemplateContext( template: 'user-dashboard', data: ['user' => ['name' => 'John'], 'title' => 'Dashboard'] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns false when auth data is present', function () { $context = new TemplateContext( template: 'profile', data: ['auth' => ['authenticated' => true]] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns false when session data is present', function () { $context = new TemplateContext( template: 'checkout', data: ['session' => ['cart_items' => 3]] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns false when csrf token is present', function () { $context = new TemplateContext( template: 'contact-form', data: ['csrf_token' => 'abc123'] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns false when flash messages are present', function () { $context = new TemplateContext( template: 'result-page', data: ['flash' => ['success' => 'Saved!']] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns false when errors are present', function () { $context = new TemplateContext( template: 'form', data: ['errors' => ['email' => 'Invalid email']] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns false when timestamp is present', function () { $context = new TemplateContext( template: 'news', data: ['timestamp' => time()] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('returns true when only non-volatile data is present', function () { $context = new TemplateContext( template: 'product-page', data: [ 'product' => ['name' => 'Laptop', 'price' => 999], 'reviews' => [['rating' => 5]] ] ); expect($this->strategy->shouldCache($context))->toBeTrue(); }); }); describe('generateKey()', function () { it('generates consistent keys for same template and data', function () { $context = new TemplateContext( template: 'product', data: ['id' => 123, 'name' => 'Laptop'] ); $key1 = $this->strategy->generateKey($context); $key2 = $this->strategy->generateKey($context); expect((string) $key1)->toBe((string) $key2); }); it('generates different keys for different templates', function () { $context1 = new TemplateContext( template: 'product', data: ['id' => 123] ); $context2 = new TemplateContext( template: 'category', data: ['id' => 123] ); $key1 = $this->strategy->generateKey($context1); $key2 = $this->strategy->generateKey($context2); expect((string) $key1)->not->toBe((string) $key2); }); it('generates different keys for different data', function () { $context1 = new TemplateContext( template: 'product', data: ['id' => 123] ); $context2 = new TemplateContext( template: 'product', data: ['id' => 456] ); $key1 = $this->strategy->generateKey($context1); $key2 = $this->strategy->generateKey($context2); expect((string) $key1)->not->toBe((string) $key2); }); it('generates same key when volatile data changes', function () { $context1 = new TemplateContext( template: 'page', data: ['title' => 'Test', 'csrf_token' => 'abc'] ); $context2 = new TemplateContext( template: 'page', data: ['title' => 'Test', 'csrf_token' => 'xyz'] ); $key1 = $this->strategy->generateKey($context1); $key2 = $this->strategy->generateKey($context2); // CSRF token sollte ignoriert werden expect((string) $key1)->toBe((string) $key2); }); it('includes template name in cache key', function () { $context = new TemplateContext( template: 'about-page', data: ['content' => 'About us'] ); $key = $this->strategy->generateKey($context); expect((string) $key)->toContain('page:about-page:'); }); }); describe('getTtl()', function () { it('returns 3600 seconds for layout templates', function () { $context = new TemplateContext( template: 'layouts/main', data: [] ); expect($this->strategy->getTtl($context))->toBe(3600); }); it('returns 7200 seconds for static templates', function () { $context = new TemplateContext( template: 'static/about', data: [] ); expect($this->strategy->getTtl($context))->toBe(7200); }); it('returns 1800 seconds for content pages', function () { $context = new TemplateContext( template: 'page/home', data: [] ); expect($this->strategy->getTtl($context))->toBe(1800); }); it('returns 900 seconds for default templates', function () { $context = new TemplateContext( template: 'misc-template', data: [] ); expect($this->strategy->getTtl($context))->toBe(900); }); }); describe('canInvalidate()', function () { it('returns true for all templates', function () { expect($this->strategy->canInvalidate('any-template'))->toBeTrue(); expect($this->strategy->canInvalidate('layout'))->toBeTrue(); expect($this->strategy->canInvalidate('component'))->toBeTrue(); }); }); describe('edge cases', function () { it('handles empty data array', function () { $context = new TemplateContext( template: 'empty', data: [] ); expect($this->strategy->shouldCache($context))->toBeTrue(); }); it('handles nested user data', function () { $context = new TemplateContext( template: 'nested', data: [ 'content' => ['text' => 'Hello'], 'user' => ['name' => 'John'] // Top-level user key ] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('handles mixed volatile and non-volatile data', function () { $context = new TemplateContext( template: 'mixed', data: [ 'product' => ['id' => 1], 'flash' => ['message' => 'Success'] ] ); expect($this->strategy->shouldCache($context))->toBeFalse(); }); it('generates valid cache key from complex data', function () { $context = new TemplateContext( template: 'complex', data: [ 'nested' => ['deep' => ['value' => 123]], 'array' => [1, 2, 3], 'string' => 'test' ] ); $key = $this->strategy->generateKey($context); expect($key)->toBeInstanceOf(CacheKey::class); expect((string) $key)->toBeString(); expect(strlen((string) $key))->toBeGreaterThan(0); }); }); });