feat(Production): Complete production deployment infrastructure

- 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.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
use App\Framework\Core\ValueObjects\Dimensions;
use App\Framework\Svg\Builder\SvgBuilder;
use App\Framework\Svg\ValueObjects\Geometry\Position;
use App\Framework\Svg\ValueObjects\Geometry\Radius;
use App\Framework\Svg\ValueObjects\Styling\Fill;
use App\Framework\Svg\ValueObjects\Styling\SvgColor;
test('can create basic canvas', function () {
$canvas = SvgBuilder::create(100, 100);
expect($canvas)->toBeInstanceOf(\App\Framework\Svg\Builder\SvgCanvas::class)
->and($canvas->count())->toBe(0);
});
test('can create square canvas', function () {
$canvas = SvgBuilder::square(50);
$svg = $canvas->toSvg();
expect($svg)->toContain('width="50"')
->and($svg)->toContain('height="50"');
});
test('can create responsive canvas with viewBox', function () {
$canvas = SvgBuilder::responsive(800, 600);
$svg = $canvas->toSvg();
expect($svg)->toContain('viewBox="0.00 0.00 800.00 600.00"');
});
test('can add rectangle to canvas', function () {
$canvas = SvgBuilder::create(200, 200);
$canvas->rect(
new Position(10, 10),
new Dimensions(50, 30),
Fill::solid(SvgColor::blue())
);
expect($canvas->count())->toBe(1)
->and($canvas->toSvg())->toContain('<rect');
});
test('can add circle to canvas', function () {
$canvas = SvgBuilder::create(200, 200);
$canvas->circle(
new Position(100, 100),
new Radius(50),
Fill::solid(SvgColor::red())
);
expect($canvas->count())->toBe(1)
->and($canvas->toSvg())->toContain('<circle');
});
test('can add text to canvas', function () {
$canvas = SvgBuilder::create(200, 200);
$canvas->text(
'Hello SVG',
new Position(100, 100),
\App\Framework\Svg\ValueObjects\Text\TextStyle::default()
);
expect($canvas->count())->toBe(1)
->and($canvas->toSvg())->toContain('Hello SVG');
});
test('can add multiple elements', function () {
$canvas = SvgBuilder::create(200, 200);
$canvas->rect(
new Position(0, 0),
new Dimensions(200, 200),
Fill::solid(SvgColor::white())
)->circle(
new Position(100, 100),
new Radius(50),
Fill::solid(SvgColor::blue())
);
expect($canvas->count())->toBe(2);
});
test('can clear canvas', function () {
$canvas = SvgBuilder::create(200, 200);
$canvas->circle(
new Position(100, 100),
new Radius(50),
Fill::solid(SvgColor::red())
);
expect($canvas->count())->toBe(1);
$canvas->clear();
expect($canvas->count())->toBe(0);
});
test('generates valid SVG with XML declaration', function () {
$canvas = SvgBuilder::create(100, 100);
$svg = $canvas->toSvg();
expect($svg)->toStartWith('<?xml version="1.0" encoding="UTF-8"?>')
->and($svg)->toContain('<svg')
->and($svg)->toContain('xmlns="http://www.w3.org/2000/svg"')
->and($svg)->toEndWith('</svg>');
});
test('can generate inline SVG without XML declaration', function () {
$canvas = SvgBuilder::create(100, 100);
$svg = $canvas->toInlineSvg();
expect($svg)->not->toContain('<?xml')
->and($svg)->toStartWith('<svg');
});
test('can add title and description for accessibility', function () {
$canvas = SvgBuilder::create(100, 100)
->withTitle('Test Chart')
->withDescription('A test SVG chart');
$svg = $canvas->toSvg();
expect($svg)->toContain('<title>Test Chart</title>')
->and($svg)->toContain('<desc>A test SVG chart</desc>');
});

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
use App\Framework\Svg\Charts\BarChart;
use App\Framework\Svg\Charts\ValueObjects\ChartConfig;
use App\Framework\Svg\Charts\ValueObjects\ChartData;
use App\Framework\Svg\Charts\ValueObjects\DataPoint;
test('can create bar chart from array', function () {
$data = [
'Jan' => 100,
'Feb' => 150,
'Mar' => 120,
];
$chart = BarChart::fromArray($data);
expect($chart->data->count())->toBe(3);
});
test('can render bar chart to SVG', function () {
$data = [
'Jan' => 100,
'Feb' => 150,
'Mar' => 120,
];
$chart = BarChart::fromArray($data);
$svg = $chart->render();
expect($svg)->toContain('<svg')
->and($svg)->toContain('<rect')
->and($svg)->toContain('Jan')
->and($svg)->toContain('Feb')
->and($svg)->toContain('Mar');
});
test('chart data validates minimum value correctly', function () {
$data = new ChartData([
new DataPoint('A', 10),
new DataPoint('B', 5),
new DataPoint('C', 15),
]);
expect($data->getMinValue())->toBe(5.0);
});
test('chart data validates maximum value correctly', function () {
$data = new ChartData([
new DataPoint('A', 10),
new DataPoint('B', 5),
new DataPoint('C', 15),
]);
expect($data->getMaxValue())->toBe(15.0);
});
test('chart data calculates sum correctly', function () {
$data = new ChartData([
new DataPoint('A', 10),
new DataPoint('B', 20),
new DataPoint('C', 30),
]);
expect($data->getSum())->toBe(60.0);
});
test('chart data calculates average correctly', function () {
$data = new ChartData([
new DataPoint('A', 10),
new DataPoint('B', 20),
new DataPoint('C', 30),
]);
expect($data->getAverage())->toBe(20.0);
});
test('chart config can create dark theme', function () {
$config = ChartConfig::default();
$darkConfig = $config->darkTheme();
expect($darkConfig->backgroundColor->toHex())->toContain('11'); // Dark background
});
test('chart data cannot be empty', function () {
new ChartData([]);
})->throws(\App\Framework\Exception\FrameworkException::class);
test('data point can check if positive', function () {
$positive = new DataPoint('A', 10);
$negative = new DataPoint('B', -5);
$zero = new DataPoint('C', 0);
expect($positive->isPositive())->toBeTrue()
->and($negative->isPositive())->toBeFalse()
->and($zero->isPositive())->toBeFalse();
});
test('data point can scale value', function () {
$point = new DataPoint('A', 10);
$scaled = $point->scale(2.5);
expect($scaled->value)->toBe(25.0)
->and($scaled->label)->toBe('A');
});

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
use App\Framework\Svg\ValueObjects\Geometry\Position;
test('can create position with coordinates', function () {
$position = new Position(10.5, 20.3);
expect($position->x)->toBe(10.5)
->and($position->y)->toBe(20.3);
});
test('can create zero position', function () {
$position = Position::zero();
expect($position->x)->toBe(0.0)
->and($position->y)->toBe(0.0);
});
test('can translate position', function () {
$position = new Position(10, 20);
$translated = $position->translate(5, -10);
expect($translated->x)->toBe(15.0)
->and($translated->y)->toBe(10.0);
});
test('can calculate distance between positions', function () {
$pos1 = new Position(0, 0);
$pos2 = new Position(3, 4);
$distance = $pos1->distanceTo($pos2);
expect($distance)->toBe(5.0); // 3-4-5 triangle
});
test('can scale position', function () {
$position = new Position(10, 20);
$scaled = $position->scale(2.0);
expect($scaled->x)->toBe(20.0)
->and($scaled->y)->toBe(40.0);
});
test('can check equality with tolerance', function () {
$pos1 = new Position(10.0, 20.0);
$pos2 = new Position(10.0001, 20.0001);
expect($pos1->equals($pos2))->toBeTrue();
});
test('converts to SVG string', function () {
$position = new Position(10.5, 20.3);
expect($position->toSvgString())->toBe('10.50,20.30');
});

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
use App\Framework\Core\ValueObjects\RGBColor;
use App\Framework\Svg\ValueObjects\Styling\NamedColor;
use App\Framework\Svg\ValueObjects\Styling\SvgColor;
test('can create color from RGB', function () {
$color = SvgColor::fromRgb(255, 0, 0);
expect($color->toHex())->toBe('#ff0000');
});
test('can create color from hex', function () {
$color = SvgColor::fromHex('#00ff00');
expect($color->rgbColor->green)->toBe(255);
});
test('can create named colors', function () {
$black = SvgColor::black();
$white = SvgColor::white();
$red = SvgColor::red();
expect($black->toSvgValue())->toBe('black')
->and($white->toSvgValue())->toBe('white')
->and($red->toSvgValue())->toBe('red');
});
test('named color uses name over hex in SVG output', function () {
$color = SvgColor::fromNamed(NamedColor::BLUE);
expect($color->toSvgValue())->toBe('blue')
->and($color->toHex())->toBe('#0000ff');
});
test('can check if transparent', function () {
$transparent = SvgColor::transparent();
$opaque = SvgColor::red();
expect($transparent->isTransparent())->toBeTrue()
->and($opaque->isTransparent())->toBeFalse();
});