Make DocumentService locale-aware

createDocument/updateDocument now accept a \$locale parameter and
write to document_translations. Adds addTranslation, deleteTranslation,
setDefaultLocale (with path/slug regen), distinct-document search,
and findByTitle that delegates to WikiLinkResolver.
This commit is contained in:
Yutaka Kurosaki
2026-05-10 12:23:14 +09:00
parent d7522f592d
commit 7909c33074
3 changed files with 265 additions and 180 deletions
@@ -0,0 +1,129 @@
<?php
namespace Tests\Unit\Services;
use App\Models\Document;
use App\Models\DocumentTranslation;
use App\Models\User;
use App\Services\DocumentService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\App;
use Tests\TestCase;
class DocumentServiceTest extends TestCase
{
use RefreshDatabase;
private DocumentService $service;
protected function setUp(): void
{
parent::setUp();
$this->service = new DocumentService();
}
public function test_create_document_creates_one_translation_in_given_locale(): void
{
App::setLocale('en'); // ensure deterministic
$user = User::factory()->create();
$doc = $this->service->createDocument('Hello', '# Hi', $user->id, 'en');
$this->assertSame('en', $doc->default_locale);
$this->assertSame('Hello.md', $doc->path);
$this->assertSame('hello', $doc->slug);
$this->assertCount(1, $doc->translations);
$this->assertSame('Hello', $doc->translations->first()->title);
$this->assertSame('# Hi', $doc->translations->first()->content);
$this->assertStringContainsString('<h1>', $doc->translations->first()->rendered_html);
}
public function test_update_document_in_default_locale_regenerates_path_and_slug(): void
{
$doc = $this->service->createDocument('Old', 'body', null, 'en');
$updated = $this->service->updateDocument($doc, 'New Title', 'body2', null, 'en');
$this->assertSame('New Title.md', $updated->path);
$this->assertSame('new-title', $updated->slug);
}
public function test_update_document_in_non_default_locale_does_not_change_path(): void
{
$doc = $this->service->createDocument('English', 'body', null, 'en');
$originalPath = $doc->path;
$originalSlug = $doc->slug;
$updated = $this->service->updateDocument($doc, '日本語タイトル', '本文', null, 'ja');
$this->assertSame($originalPath, $updated->path);
$this->assertSame($originalSlug, $updated->slug);
$this->assertSame('日本語タイトル', $updated->translationFor('ja', false)->title);
}
public function test_add_translation_creates_new_locale_row(): void
{
$doc = $this->service->createDocument('Hello', 'body', null, 'en');
$this->service->addTranslation($doc, 'ja', 'こんにちは', 'やあ', null);
$this->assertCount(2, $doc->fresh()->translations);
$this->assertSame('こんにちは', $doc->fresh()->translationFor('ja', false)->title);
}
public function test_add_translation_throws_on_duplicate_locale(): void
{
$doc = $this->service->createDocument('Hello', 'body', null, 'en');
$this->expectException(\InvalidArgumentException::class);
$this->service->addTranslation($doc, 'en', 'X', 'Y', null);
}
public function test_delete_translation_removes_non_default(): void
{
$doc = $this->service->createDocument('Hello', 'body', null, 'en');
$this->service->addTranslation($doc, 'ja', 'こんにちは', 'やあ', null);
$this->service->deleteTranslation($doc, 'ja');
$this->assertNull($doc->fresh()->translationFor('ja', false));
}
public function test_delete_translation_refuses_default_locale(): void
{
$doc = $this->service->createDocument('Hello', 'body', null, 'en');
$this->expectException(\InvalidArgumentException::class);
$this->service->deleteTranslation($doc, 'en');
}
public function test_set_default_locale_requires_existing_translation(): void
{
$doc = $this->service->createDocument('Hello', 'body', null, 'en');
$this->expectException(\InvalidArgumentException::class);
$this->service->setDefaultLocale($doc, 'ja');
}
public function test_set_default_locale_regenerates_path_from_new_locale_title(): void
{
$doc = $this->service->createDocument('Hello', 'body', null, 'en');
$this->service->addTranslation($doc, 'ja', 'こんにちは', 'やあ', null);
$updated = $this->service->setDefaultLocale($doc, 'ja');
$this->assertSame('ja', $updated->default_locale);
$this->assertSame('こんにちは.md', $updated->path);
}
public function test_search_returns_distinct_documents_across_locales(): void
{
$doc = $this->service->createDocument('Searchword', 'body', null, 'en');
$this->service->addTranslation($doc, 'ja', 'Searchword JA', 'Searchword body', null);
$results = $this->service->search('Searchword');
$this->assertCount(1, $results); // distinct, even though 2 translations match
$this->assertSame($doc->id, $results->first()->id);
}
}