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,127 @@
<?php
declare(strict_types=1);
namespace Tests\Support;
use App\Domain\Cms\Entities\Content;
use App\Domain\Cms\Entities\ContentType;
use App\Domain\Cms\Enums\ContentStatus;
use App\Domain\Cms\ValueObjects\BlockData;
use App\Domain\Cms\ValueObjects\BlockId;
use App\Domain\Cms\ValueObjects\BlockType;
use App\Domain\Cms\ValueObjects\ContentBlock;
use App\Domain\Cms\ValueObjects\ContentBlocks;
use App\Domain\Cms\ValueObjects\ContentId;
use App\Domain\Cms\ValueObjects\ContentSlug;
use App\Domain\Cms\ValueObjects\ContentTypeId;
use App\Domain\Cms\ValueObjects\Locale;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\DateTime\Clock;
final class CmsTestHelpers
{
public static function createContent(
Clock $clock,
?ContentId $id = null,
?ContentTypeId $contentTypeId = null,
?ContentSlug $slug = null,
?string $title = null,
?ContentBlocks $blocks = null,
?ContentStatus $status = null,
?Locale $defaultLocale = null
): Content {
return new Content(
id: $id ?? ContentId::generate($clock),
contentTypeId: $contentTypeId ?? ContentTypeId::fromString('page'),
slug: $slug ?? ContentSlug::fromString('test-page'),
title: $title ?? 'Test Page',
blocks: $blocks ?? self::createSimpleBlocks(),
status: $status ?? ContentStatus::DRAFT,
authorId: null,
publishedAt: null,
metaData: null,
defaultLocale: $defaultLocale ?? Locale::english(),
createdAt: Timestamp::now(),
updatedAt: Timestamp::now()
);
}
public static function createContentType(
?ContentTypeId $id = null,
?string $name = null,
?string $slug = null,
bool $isSystem = false
): ContentType {
return new ContentType(
id: $id ?? ContentTypeId::fromString('page'),
name: $name ?? 'Page',
slug: $slug ?? 'page',
description: 'A test page content type',
isSystem: $isSystem,
createdAt: Timestamp::now(),
updatedAt: Timestamp::now()
);
}
public static function createSimpleBlocks(): ContentBlocks
{
return ContentBlocks::fromArray([
[
'id' => 'hero-1',
'type' => 'hero',
'data' => ['title' => 'Hero Title'],
],
]);
}
public static function createHeroBlock(
?BlockId $blockId = null,
?string $title = null
): ContentBlock {
return ContentBlock::create(
type: BlockType::hero(),
blockId: $blockId ?? BlockId::fromString('hero-1'),
data: BlockData::fromArray(['title' => $title ?? 'Hero Title'])
);
}
public static function createTextBlock(
?BlockId $blockId = null,
?string $content = null
): ContentBlock {
return ContentBlock::create(
type: BlockType::text(),
blockId: $blockId ?? BlockId::fromString('text-1'),
data: BlockData::fromArray(['content' => $content ?? 'Text content'])
);
}
public static function createImageBlock(
?BlockId $blockId = null,
?string $imageId = null
): ContentBlock {
return ContentBlock::create(
type: BlockType::image(),
blockId: $blockId ?? BlockId::fromString('image-1'),
data: BlockData::fromArray(['imageId' => $imageId ?? 'img-123'])
);
}
public static function createContentTranslation(
ContentId $contentId,
?Locale $locale = null,
?string $title = null,
?ContentBlocks $blocks = null
): \App\Domain\Cms\Entities\ContentTranslation {
return new \App\Domain\Cms\Entities\ContentTranslation(
contentId: $contentId,
locale: $locale ?? Locale::german(),
title: $title ?? 'Deutscher Titel',
blocks: $blocks ?? self::createSimpleBlocks(),
createdAt: Timestamp::now(),
updatedAt: Timestamp::now()
);
}
}