clock = new SystemClock(); $this->repository = Mockery::mock(ContentTranslationRepository::class); $this->service = new ContentLocalizationService($this->repository, $this->clock); }); it('creates translation', function () { $contentId = ContentId::generate($this->clock); $locale = Locale::german(); $blocks = ContentBlocks::fromArray([ [ 'id' => 'text-1', 'type' => 'text', 'data' => ['content' => 'Deutscher Text'], ], ]); $this->repository->shouldReceive('save') ->once() ->andReturnNull(); $translation = $this->service->createTranslation( contentId: $contentId, locale: $locale, title: 'Deutscher Titel', blocks: $blocks ); expect($translation)->toBeInstanceOf(ContentTranslation::class); expect($translation->locale->equals($locale))->toBeTrue(); expect($translation->title)->toBe('Deutscher Titel'); }); it('updates translation', function () { $contentId = ContentId::generate($this->clock); $locale = Locale::german(); $translation = CmsTestHelpers::createContentTranslation($contentId, $locale); $this->repository->shouldReceive('findByContentAndLocale') ->once() ->with($contentId, $locale) ->andReturn($translation); $this->repository->shouldReceive('save') ->once() ->andReturnNull(); $updated = $this->service->updateTranslation( contentId: $contentId, locale: $locale, title: 'Aktualisierter Titel' ); expect($updated->title)->toBe('Aktualisierter Titel'); }); it('throws exception when updating non-existent translation', function () { $contentId = ContentId::generate($this->clock); $locale = Locale::german(); $this->repository->shouldReceive('findByContentAndLocale') ->once() ->with($contentId, $locale) ->andReturn(null); expect(fn () => $this->service->updateTranslation( contentId: $contentId, locale: $locale, title: 'New Title' ))->toThrow(DomainException::class); }); it('gets translation', function () { $contentId = ContentId::generate($this->clock); $locale = Locale::german(); $translation = CmsTestHelpers::createContentTranslation($contentId, $locale); $this->repository->shouldReceive('findByContentAndLocale') ->once() ->with($contentId, $locale) ->andReturn($translation); $found = $this->service->getTranslation($contentId, $locale); expect($found)->toBe($translation); }); it('returns content for default locale', function () { $content = CmsTestHelpers::createContent($this->clock); $locale = $content->defaultLocale; $localized = $this->service->getContentForLocale($content, $locale); expect($localized)->toBe($content); }); it('returns translated content for non-default locale', function () { $content = CmsTestHelpers::createContent($this->clock); $locale = Locale::german(); $translation = CmsTestHelpers::createContentTranslation($content->id, $locale); $this->repository->shouldReceive('findByContentAndLocale') ->once() ->with($content->id, $locale) ->andReturn($translation); $localized = $this->service->getContentForLocale($content, $locale); expect($localized->title)->toBe($translation->title); }); it('falls back to default locale when translation not found', function () { $content = CmsTestHelpers::createContent($this->clock); $locale = Locale::german(); $this->repository->shouldReceive('findByContentAndLocale') ->once() ->with($content->id, $locale) ->andReturn(null); $localized = $this->service->getContentForLocale($content, $locale); expect($localized->title)->toBe($content->title); }); it('gets all translations for content', function () { $contentId = ContentId::generate($this->clock); $translations = [ CmsTestHelpers::createContentTranslation($contentId, Locale::german()), ]; $this->repository->shouldReceive('findByContent') ->once() ->with($contentId) ->andReturn($translations); $found = $this->service->getAllTranslations($contentId); expect($found)->toBe($translations); }); it('deletes translation', function () { $contentId = ContentId::generate($this->clock); $locale = Locale::german(); $this->repository->shouldReceive('delete') ->once() ->with($contentId, $locale) ->andReturnNull(); $this->service->deleteTranslation($contentId, $locale); }); });