- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
278 lines
9.0 KiB
PHP
278 lines
9.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Cache\Cache;
|
|
use App\Framework\Cache\CacheKey;
|
|
use App\Framework\View\Caching\Strategies\FullPageCacheStrategy;
|
|
use App\Framework\View\Caching\TemplateContext;
|
|
|
|
describe('FullPageCacheStrategy', function () {
|
|
beforeEach(function () {
|
|
$this->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);
|
|
});
|
|
});
|
|
});
|