feat(cms,asset): add comprehensive test suite and finalize modules

- Add comprehensive test suite for CMS and Asset modules using Pest Framework
- Implement ContentTypeService::delete() protection against deletion of in-use content types
- Add CannotDeleteContentTypeInUseException for better error handling
- Fix DerivatPipelineRegistry::getAllPipelines() to handle object uniqueness correctly
- Fix VariantName::getScale() to correctly parse scales with file extensions
- Update CMS module documentation with new features, exceptions, and test coverage
- Add CmsTestHelpers and AssetTestHelpers for test data factories
- Fix BlockTypeRegistry to be immutable after construction
- Update ContentTypeService to check for associated content before deletion
- Improve BlockRendererRegistry initialization

Test coverage:
- Value Objects: All CMS and Asset value objects
- Services: ContentService, ContentTypeService, SlugGenerator, BlockValidator, ContentLocalizationService, AssetService, DeduplicationService, MetadataExtractor
- Repositories: All database repositories with mocked connections
- Rendering: Block renderers and ContentRenderer
- Controllers: API endpoints for both modules

254 tests passing, 38 remaining (mostly image processing pipeline tests)
This commit is contained in:
2025-11-10 02:12:28 +01:00
parent 74d50a29cc
commit 2d53270056
53 changed files with 5699 additions and 15 deletions

View File

@@ -0,0 +1,158 @@
<?php
declare(strict_types=1);
use App\Domain\Asset\ValueObjects\AssetMetadata;
describe('AssetMetadata', function () {
it('can be created as empty', function () {
$meta = AssetMetadata::empty();
expect($meta->toArray())->toBe([]);
});
it('can be created from array', function () {
$meta = AssetMetadata::fromArray([
'width' => 1920,
'height' => 1080,
]);
expect($meta->getWidth())->toBe(1920);
expect($meta->getHeight())->toBe(1080);
});
it('can get width and height', function () {
$meta = AssetMetadata::fromArray([
'width' => 1920,
'height' => 1080,
]);
expect($meta->getWidth())->toBe(1920);
expect($meta->getHeight())->toBe(1080);
});
it('returns null for missing dimensions', function () {
$meta = AssetMetadata::empty();
expect($meta->getWidth())->toBeNull();
expect($meta->getHeight())->toBeNull();
});
it('can set width immutably', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withWidth(1920);
expect($newMeta->getWidth())->toBe(1920);
expect($meta->getWidth())->toBeNull(); // Original unchanged
});
it('can set height immutably', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withHeight(1080);
expect($newMeta->getHeight())->toBe(1080);
expect($meta->getHeight())->toBeNull(); // Original unchanged
});
it('can set dimensions immutably', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withDimensions(1920, 1080);
expect($newMeta->getWidth())->toBe(1920);
expect($newMeta->getHeight())->toBe(1080);
expect($meta->getWidth())->toBeNull(); // Original unchanged
});
it('can set duration', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withDuration(120);
expect($newMeta->getDuration())->toBe(120);
});
it('can set EXIF data', function () {
$exif = ['ISO' => 400, 'Aperture' => 'f/2.8'];
$meta = AssetMetadata::empty();
$newMeta = $meta->withExif($exif);
expect($newMeta->getExif())->toBe($exif);
});
it('can set IPTC data', function () {
$iptc = ['Copyright' => '2025'];
$meta = AssetMetadata::empty();
$newMeta = $meta->withIptc($iptc);
expect($newMeta->getIptc())->toBe($iptc);
});
it('can set color profile', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withColorProfile('sRGB');
expect($newMeta->getColorProfile())->toBe('sRGB');
});
it('can get color profile with snake_case key', function () {
$meta = AssetMetadata::fromArray(['color_profile' => 'sRGB']);
expect($meta->getColorProfile())->toBe('sRGB');
});
it('can set focal point', function () {
$focalPoint = ['x' => 0.5, 'y' => 0.5];
$meta = AssetMetadata::empty();
$newMeta = $meta->withFocalPoint($focalPoint);
expect($newMeta->getFocalPoint())->toBe($focalPoint);
});
it('can get focal point with snake_case key', function () {
$meta = AssetMetadata::fromArray(['focal_point' => ['x' => 0.5, 'y' => 0.5]]);
expect($meta->getFocalPoint())->not->toBeNull();
});
it('can set dominant color', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withDominantColor('#FF5733');
expect($newMeta->getDominantColor())->toBe('#FF5733');
});
it('can get dominant color with snake_case key', function () {
$meta = AssetMetadata::fromArray(['dominant_color' => '#FF5733']);
expect($meta->getDominantColor())->toBe('#FF5733');
});
it('can set blurhash', function () {
$meta = AssetMetadata::empty();
$newMeta = $meta->withBlurhash('LGF5]+Yk^6#M@-5c,1J5@[or[Q6.');
expect($newMeta->getBlurhash())->toBe('LGF5]+Yk^6#M@-5c,1J5@[or[Q6.');
});
it('can get and set arbitrary values', function () {
$meta = AssetMetadata::fromArray(['custom' => 'value']);
expect($meta->get('custom'))->toBe('value');
expect($meta->has('custom'))->toBeTrue();
expect($meta->has('missing'))->toBeFalse();
});
it('can convert to array', function () {
$data = ['width' => 1920, 'height' => 1080];
$meta = AssetMetadata::fromArray($data);
expect($meta->toArray())->toBe($data);
});
it('can remove width by setting to null', function () {
$meta = AssetMetadata::fromArray(['width' => 1920]);
$newMeta = $meta->withWidth(null);
expect($newMeta->getWidth())->toBeNull();
});
});