testDir = '/home/michael/dev/michaelschiemer/tests/tmp/smart_analyzer_' . uniqid(); if (!is_dir($this->testDir)) { mkdir($this->testDir, 0755, true); } // Create real PathProvider $this->pathProvider = new PathProvider($this->testDir); // Create mocked Cache $this->cache = Mockery::mock(Cache::class); $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems(CacheItem::miss(CacheKey::fromString('test')))); $this->cache->shouldReceive('set')->andReturn(true); $this->cache->shouldReceive('clear')->andReturn(true); // Create TemplateLoader with test directory as template path $this->loader = new TemplateLoader( pathProvider: $this->pathProvider, cache: $this->cache, templates: [], templatePath: '', // Empty to use base path directly cacheEnabled: false ); $this->analyzer = new SmartTemplateAnalyzer($this->loader); }); afterEach(function () { Mockery::close(); // Clean up test files if (is_dir($this->testDir)) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($this->testDir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($files as $file) { $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath()); } rmdir($this->testDir); } }); describe('analyze()', function () { it('recommends FULL_PAGE strategy for layouts with high static ratio', function () { $content = 'SiteMostly static content here'; // Create template file $filePath = $this->testDir . '/layouts/main.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('layouts/main'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::FULL_PAGE); expect($analysis->cacheability->staticContentRatio)->toBeGreaterThan(0.8); }); it('recommends COMPONENT strategy for component templates', function () { $content = ''; // Create template file $filePath = $this->testDir . '/components/button.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('components/button'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::COMPONENT); }); it('recommends COMPONENT strategy for partial templates', function () { $content = ''; // Create template file $filePath = $this->testDir . '/partials/navigation.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('partials/navigation'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::COMPONENT); }); it('recommends NO_CACHE for templates with user content', function () { $content = '
Welcome {{ user.name }}!

Your profile

'; // Create template file $filePath = $this->testDir . '/pages/profile.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('pages/profile'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::NO_CACHE); expect($analysis->cacheability->hasUserSpecificContent)->toBeTrue(); }); it('detects CSRF tokens and adjusts cacheability score', function () { $content = '
'; // Create template file $filePath = $this->testDir . '/forms/contact.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('forms/contact'); // CSRF tokens detected but high static ratio (0.839) means still cacheable (0.839 - 0.2 = 0.639 > 0.5) expect($analysis->cacheability->hasCsrfTokens)->toBeTrue(); expect($analysis->cacheability->getScore())->toBeGreaterThan(0.5); }); it('detects timestamps and adjusts cacheability score', function () { $content = '
Current time: {{ now }}
'; // Create template file $filePath = $this->testDir . '/widgets/clock.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('widgets/clock'); // Timestamps detected but high static ratio means still cacheable expect($analysis->cacheability->hasTimestamps)->toBeTrue(); expect($analysis->cacheability->getScore())->toBeGreaterThan(0.5); }); it('recommends NO_CACHE for templates with random elements', function () { $content = '
Random ID: {{ uuid }}
'; // Create template file $filePath = $this->testDir . '/widgets/random.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('widgets/random'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::NO_CACHE); expect($analysis->cacheability->hasRandomElements)->toBeTrue(); }); it('recommends FRAGMENT strategy for medium cacheability templates', function () { $content = '

Title

Some content

{{ small_dynamic_part }}
'; // Create template file $filePath = $this->testDir . '/pages/mixed.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('pages/mixed'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::FRAGMENT); expect($analysis->cacheability->getScore())->toBeGreaterThan(0.5); }); it('creates fallback analysis when loader throws exception', function () { $analysis = $this->analyzer->analyze('missing/template'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::NO_CACHE); expect($analysis->recommendedTtl)->toBe(0); expect($analysis->dependencies)->toBeEmpty(); }); it('adjusts TTL based on cacheability score', function () { $staticContent = 'All static content here'; // Create template file $filePath = $this->testDir . '/static/page.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $staticContent); $analysis = $this->analyzer->analyze('static/page'); expect($analysis->recommendedTtl)->toBeGreaterThan(0); }); }); describe('getDependencies()', function () { it('finds @include directives', function () { $content = '@include("header")
Content
@include("footer")'; // Create template file $filePath = $this->testDir . '/page/test.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $deps = $this->analyzer->getDependencies('page/test'); expect($deps)->toHaveKey('includes'); expect($deps['includes'])->toContain('header'); expect($deps['includes'])->toContain('footer'); }); it('finds tags', function () { $content = ' '; // Create template file $filePath = $this->testDir . '/page/test2.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $deps = $this->analyzer->getDependencies('page/test2'); expect($deps)->toHaveKey('components'); expect($deps['components'])->toContain('button'); expect($deps['components'])->toContain('card'); }); it('finds both includes and components', function () { $content = '@include("layout") '; // Create template file $filePath = $this->testDir . '/page/test3.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $deps = $this->analyzer->getDependencies('page/test3'); expect($deps)->toHaveKey('includes'); expect($deps)->toHaveKey('components'); expect($deps['includes'])->toContain('layout'); expect($deps['components'])->toContain('nav'); }); it('returns empty array when no dependencies found', function () { $content = '
No dependencies here
'; // Create template file $filePath = $this->testDir . '/page/simple.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $deps = $this->analyzer->getDependencies('page/simple'); expect($deps)->toBeEmpty(); }); it('returns empty array when loader throws exception', function () { $deps = $this->analyzer->getDependencies('page/error'); expect($deps)->toBeEmpty(); }); }); describe('getCacheability()', function () { it('detects user-specific content patterns', function () { $content = '{{ user.name }} {{ current_user.email }} {{ auth.role }}'; // Create template file $filePath = $this->testDir . '/test/user.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $score = $this->analyzer->getCacheability('test/user'); expect($score->hasUserSpecificContent)->toBeTrue(); expect($score->isCacheable())->toBeFalse(); }); it('detects CSRF token patterns', function () { $content = '{{ csrf_token }} {{ _token }}'; // Create template file $filePath = $this->testDir . '/test/csrf.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $score = $this->analyzer->getCacheability('test/csrf'); expect($score->hasCsrfTokens)->toBeTrue(); expect($score->isCacheable())->toBeFalse(); }); it('detects timestamp patterns', function () { $content = '{{ now }} {{ timestamp }} {{ date() }}'; // Create template file $filePath = $this->testDir . '/test/time.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $score = $this->analyzer->getCacheability('test/time'); expect($score->hasTimestamps)->toBeTrue(); expect($score->isCacheable())->toBeFalse(); }); it('detects random element patterns', function () { $content = '{{ random }} {{ rand() }} {{ uuid }}'; // Create template file $filePath = $this->testDir . '/test/random.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $score = $this->analyzer->getCacheability('test/random'); expect($score->hasRandomElements)->toBeTrue(); expect($score->isCacheable())->toBeFalse(); }); it('calculates high static content ratio for mostly static templates', function () { $content = '
Static content

More static content here with lots of text

Even more static HTML content
{{ small_dynamic }}'; // Create template file $filePath = $this->testDir . '/test/static.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $score = $this->analyzer->getCacheability('test/static'); expect($score->staticContentRatio)->toBeGreaterThan(0.8); expect($score->isCacheable())->toBeTrue(); }); it('calculates low static content ratio for mostly dynamic templates', function () { $content = '{{ var1 }} {{ var2 }} {{ var3 }} {{ var4 }}

static

'; // Create template file $filePath = $this->testDir . '/test/dynamic.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $score = $this->analyzer->getCacheability('test/dynamic'); expect($score->staticContentRatio)->toBeLessThan(0.5); }); it('returns default score when loader throws exception', function () { $score = $this->analyzer->getCacheability('test/error'); // Default CacheabilityScore has staticContentRatio = 0.0 expect($score->staticContentRatio)->toBe(0.0); expect($score->hasUserSpecificContent)->toBeFalse(); expect($score->hasCsrfTokens)->toBeFalse(); expect($score->hasTimestamps)->toBeFalse(); expect($score->hasRandomElements)->toBeFalse(); }); it('handles empty content gracefully', function () { // Create empty template file $filePath = $this->testDir . '/test/empty.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, ''); $score = $this->analyzer->getCacheability('test/empty'); expect($score->staticContentRatio)->toBe(0.0); }); }); describe('integration scenarios', function () { it('correctly analyzes a typical blog post template', function () { $content = <<

{{ post.title }}

{{ post.content }}

Read more articles

HTML; // Create template file $filePath = $this->testDir . '/blog/post.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('blog/post'); expect($analysis->cacheability->hasTimestamps)->toBeFalse(); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::FRAGMENT); }); it('correctly analyzes a static landing page', function () { $content = << Welcome

Welcome to our site

Static marketing content
More static content
Copyright 2024
HTML; // Create template file $filePath = $this->testDir . '/pages/landing.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('pages/landing'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::FULL_PAGE); expect($analysis->cacheability->staticContentRatio)->toBeGreaterThan(0.9); }); it('correctly analyzes a reusable button component', function () { $content = ''; // Create template file $filePath = $this->testDir . '/components/button2.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('components/button2'); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::COMPONENT); expect($analysis->recommendedTtl)->toBeGreaterThan(0); }); it('correctly analyzes a form with CSRF protection', function () { $content = << {{ csrf_token }} HTML; // Create template file $filePath = $this->testDir . '/forms/contact2.view.php'; $dir = dirname($filePath); if (!is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($filePath, $content); $analysis = $this->analyzer->analyze('forms/contact2'); // CSRF detected and high static ratio means still cacheable, recommends FULL_PAGE expect($analysis->cacheability->hasCsrfTokens)->toBeTrue(); expect($analysis->cacheability->staticContentRatio)->toBeGreaterThan(0.8); expect($analysis->recommendedStrategy)->toBe(CacheStrategy::FULL_PAGE); }); }); });