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
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,354 @@
<?php
declare(strict_types=1);
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Email\CssInliner;
use App\Framework\Email\EmailContext;
use App\Framework\Email\EmailTemplateRenderer;
use App\Framework\Email\ValueObjects\EmailContent;
use App\Framework\Email\ValueObjects\EmailSubject;
use App\Framework\Template\Parser\DomTemplateParser;
beforeEach(function () {
$this->parser = new DomTemplateParser();
$this->cssInliner = new CssInliner($this->parser);
$this->renderer = new EmailTemplateRenderer($this->parser, $this->cssInliner);
});
describe('CssInliner', function () {
it('inlines CSS from style tags', function () {
$html = '
<html>
<head>
<style>
.button { background-color: red; padding: 10px; }
p { color: blue; }
</style>
</head>
<body>
<p>Hello</p>
<a class="button">Click me</a>
</body>
</html>
';
$result = $this->cssInliner->inline($html);
expect($result)->toContain('style="color: blue"');
expect($result)->toContain('style="background-color: red; padding: 10px"');
});
it('handles multiple selectors', function () {
$html = '
<html>
<head>
<style>
h1, h2, h3 { color: green; }
</style>
</head>
<body>
<h1>Title 1</h1>
<h2>Title 2</h2>
<h3>Title 3</h3>
</body>
</html>
';
$result = $this->cssInliner->inline($html);
expect($result)->toContain('<h1 style="color: green">');
expect($result)->toContain('<h2 style="color: green">');
expect($result)->toContain('<h3 style="color: green">');
});
it('preserves existing inline styles with higher priority', function () {
$html = '
<html>
<head>
<style>
p { color: blue; font-size: 14px; }
</style>
</head>
<body>
<p style="color: red;">Text</p>
</body>
</html>
';
$result = $this->cssInliner->inline($html);
// Existing inline style should win
expect($result)->toContain('color: red');
// But new property should be added
expect($result)->toContain('font-size: 14px');
});
it('removes !important from inline styles', function () {
$html = '
<html>
<head>
<style>
.urgent { color: red !important; }
</style>
</head>
<body>
<p class="urgent">Alert</p>
</body>
</html>
';
$result = $this->cssInliner->inline($html);
expect($result)->toContain('style="color: red"');
expect($result)->not->toContain('!important');
});
});
describe('EmailContext', function () {
it('implements TemplateContext interface', function () {
$context = new EmailContext(
data: ['name' => 'John', 'email' => 'john@example.com']
);
expect($context->getData())->toBe(['name' => 'John', 'email' => 'john@example.com']);
expect($context->get('name'))->toBe('John');
expect($context->get('missing', 'default'))->toBe('default');
expect($context->has('email'))->toBeTrue();
expect($context->has('missing'))->toBeFalse();
});
it('creates context with tracking parameters', function () {
$context = EmailContext::withTracking(
data: ['name' => 'John'],
source: 'newsletter',
campaign: 'welcome'
);
expect($context->utmParams)->toHaveKey('utm_source', 'newsletter');
expect($context->utmParams)->toHaveKey('utm_medium', 'email');
expect($context->utmParams)->toHaveKey('utm_campaign', 'welcome');
expect($context->trackingId)->toStartWith('email_');
});
});
describe('EmailContent', function () {
it('calculates size using Byte value object', function () {
$content = new EmailContent(
html: '<p>Hello World</p>', // 18 bytes
text: 'Hello World' // 11 bytes
);
$size = $content->getSize();
expect($size)->toBeInstanceOf(Byte::class);
expect($size->toBytes())->toBe(29);
});
it('detects multipart content', function () {
$withBoth = new EmailContent(
html: '<p>Hello</p>',
text: 'Hello'
);
$htmlOnly = new EmailContent(
html: '<p>Hello</p>',
text: ''
);
expect($withBoth->hasMultipart())->toBeTrue();
expect($htmlOnly->hasMultipart())->toBeFalse();
});
});
describe('EmailSubject', function () {
it('validates subject length', function () {
$subject = new EmailSubject('Valid subject');
expect($subject->value)->toBe('Valid subject');
expect($subject->isWithinRecommendedLength())->toBeTrue();
});
it('removes line breaks from subject', function () {
$subject = new EmailSubject("Subject with\nnewline");
expect($subject->value)->toBe('Subject with newline');
});
it('throws exception for empty subject', function () {
expect(fn () => new EmailSubject(' '))->toThrow(\InvalidArgumentException::class);
});
it('creates preview with ellipsis', function () {
$longSubject = new EmailSubject('This is a very long subject that needs to be truncated');
expect($longSubject->getPreview(20))->toBe('This is a very lo...');
});
});
describe('EmailTemplateRenderer', function () {
it('renders template with variable replacement', function () {
// Create a test template
$templatePath = __DIR__ . '/test-email.html';
file_put_contents($templatePath, '
<html>
<head>
<title>Test Email</title>
<meta name="email-subject" content="Hello {{name}}!">
</head>
<body>
<p>Dear {{name}},</p>
<p>Your email is {{email}}</p>
</body>
</html>
');
$context = new EmailContext(
data: [
'name' => 'John Doe',
'email' => 'john@example.com',
]
);
$content = $this->renderer->render($templatePath, $context);
expect($content->html)->toContain('Dear John Doe,');
expect($content->html)->toContain('Your email is john@example.com');
expect($content->subject)->toBeInstanceOf(EmailSubject::class);
expect($content->subject->value)->toBe('Hello John Doe!');
expect($content->text)->toContain('Dear John Doe,');
// Clean up
unlink($templatePath);
});
it('adds UTM tracking parameters to links', function () {
$templatePath = __DIR__ . '/test-tracking.html';
file_put_contents($templatePath, '
<html>
<body>
<a href="https://example.com">Link 1</a>
<a href="https://example.com?foo=bar">Link 2</a>
<a href="#anchor">Anchor</a>
<a href="mailto:test@example.com">Email</a>
</body>
</html>
');
$context = EmailContext::withTracking(
data: [],
source: 'email',
campaign: 'test'
);
$content = $this->renderer->render($templatePath, $context);
expect($content->html)->toContain('https://example.com?utm_source=email');
expect($content->html)->toContain('https://example.com?foo=bar&amp;utm_source=email');
expect($content->html)->toContain('href="#anchor"'); // Unchanged
expect($content->html)->toContain('href="mailto:test@example.com"'); // Unchanged
unlink($templatePath);
});
it('generates plain text from HTML', function () {
$templatePath = __DIR__ . '/test-plaintext.html';
file_put_contents($templatePath, '
<html>
<head>
<style>p { color: blue; }</style>
</head>
<body>
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<a href="https://example.com">Click here</a>
</body>
</html>
');
$context = new EmailContext();
$content = $this->renderer->render($templatePath, $context);
expect($content->text)->not->toContain('<');
expect($content->text)->not->toContain('>');
expect($content->text)->toContain("Title\n\n");
expect($content->text)->toContain("Paragraph 1\n\n");
expect($content->text)->toContain('• Item 1');
expect($content->text)->toContain('• Item 2');
expect($content->text)->toContain('Click here (https://example.com)');
unlink($templatePath);
});
it('adds preheader text', function () {
$templatePath = __DIR__ . '/test-preheader.html';
file_put_contents($templatePath, '
<html>
<body>
<p>Content</p>
</body>
</html>
');
$context = new EmailContext(
data: [],
preheader: 'This is a preview text'
);
$content = $this->renderer->render($templatePath, $context);
expect($content->html)->toContain('<body>');
expect($content->html)->toContain('display:none');
expect($content->html)->toContain('This is a preview text');
unlink($templatePath);
});
});
describe('Integration: Welcome Email Template', function () {
it('renders the welcome email template correctly', function () {
$context = new EmailContext(
data: [
'name' => 'Max Mustermann',
'email' => 'max@example.com',
'company_name' => 'Test Company',
'login_url' => 'https://app.example.com/login',
'current_year' => '2024',
'facebook_url' => 'https://facebook.com/testcompany',
'twitter_url' => 'https://twitter.com/testcompany',
'linkedin_url' => 'https://linkedin.com/company/testcompany',
'unsubscribe_url' => 'https://app.example.com/unsubscribe',
'webview_url' => 'https://app.example.com/email/view/123',
],
preheader: 'Willkommen bei Test Company!'
);
$content = $this->renderer->render('welcome', $context);
// Check HTML content
expect($content->html)->toContain('Hallo Max Mustermann,');
expect($content->html)->toContain('max@example.com');
expect($content->html)->toContain('Test Company');
expect($content->html)->toContain('https://app.example.com/login');
// Check subject extraction
expect($content->subject)->toBeInstanceOf(EmailSubject::class);
expect($content->subject->value)->toBe('Willkommen bei Test Company!');
// Check plain text generation
expect($content->text)->toContain('Hallo Max Mustermann,');
expect($content->text)->not->toContain('<div>');
expect($content->text)->not->toContain('<style>');
// Check CSS was inlined
expect($content->html)->toContain('style=');
});
});