presseportale/tests/Feature/LegacyPressReleaseDateImportTest.php
Kevin Adametz 5a9aab7012 Migration: PM-Datum korrekt übernehmen, UUID stabil, Zwei-Phasen-Runbook
- published_at = Legacy-created_at (Publikationsdatum, Frontend-Sortier-
  schlüssel) statt updated_at, das bei Alt-Daten oft den Massen-/Migrations-
  stempel trägt und ein falsches "frisches" Datum erzeugte.
- created_at/updated_at per forceFill direkt im Import gesetzt (waren nicht
  fillable, wurden still verworfen) – Import allein ist jetzt datumssauber.
- legacy:fix-timestamps korrigiert zusätzlich published_at (status=published).
- PM-UUID bleibt beim Re-Import/Delta-Lauf erhalten (kein Neu-Würfeln).
- MIGRATION-STEPS auf Zwei-Phasen-Strategie umgestellt: migrate, Phase 1 mit
  --force, Phase 2 (Delta) ohne --force, Grandfather-Re-Run, Idempotenz-Tabelle.
- Tests: LegacyPressReleaseDateImportTest (published_at-Quelle, Entwurf,
  Force-Re-Import erhält UUID + Datum).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 13:32:48 +00:00

180 lines
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use App\Enums\PressReleaseStatus;
use App\Models\Category;
use App\Models\LegacyImportMap;
use App\Models\PressRelease;
use App\Models\User;
use App\Services\Import\ImportContext;
use App\Services\Import\PressReleaseImporter;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;
/**
* Baut das minimale Legacy-Schema (press_release + leere Bild-/Kontakt-Pivots)
* und legt Kategorie + User samt Import-Map-Einträgen an, damit der Importer
* Kategorie und Autor auflösen kann (ohne Autor wird eine PM übersprungen).
*/
function seedLegacyPressReleaseSchema(): Category
{
Config::set('database.connections.mysql_presseecho', [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
'foreign_key_constraints' => false,
]);
DB::purge('mysql_presseecho');
Schema::connection('mysql_presseecho')->create('press_release', function (Blueprint $table): void {
$table->integer('id')->primary();
$table->string('title')->nullable();
$table->string('language')->nullable();
$table->text('text')->nullable();
$table->string('backlink_url')->nullable();
$table->integer('category_id')->nullable();
$table->string('keywords')->nullable();
$table->integer('user_id')->nullable();
$table->integer('company_id')->nullable();
$table->string('status')->nullable();
$table->integer('hits')->nullable();
$table->integer('teaser_begin')->nullable();
$table->integer('teaser_end')->nullable();
$table->boolean('no_export')->nullable();
$table->dateTime('created_at')->nullable();
$table->dateTime('updated_at')->nullable();
$table->string('slug')->nullable();
});
Schema::connection('mysql_presseecho')->create('press_release_image', function (Blueprint $table): void {
$table->integer('id')->primary();
$table->integer('press_release_id');
});
Schema::connection('mysql_presseecho')->create('press_release_contact', function (Blueprint $table): void {
$table->integer('press_release_id');
$table->integer('contact_id');
});
$category = Category::factory()->withTranslations()->create();
LegacyImportMap::query()->create([
'legacy_portal' => 'presseecho',
'legacy_table' => 'category',
'legacy_id' => 500,
'target_table' => 'categories',
'target_id' => $category->id,
]);
$author = User::factory()->create();
LegacyImportMap::query()->create([
'legacy_portal' => 'presseecho',
'legacy_table' => 'sf_guard_user',
'legacy_id' => 700,
'target_table' => 'users',
'target_id' => $author->id,
]);
return $category;
}
test('a published release takes published_at from the legacy created_at, not updated_at', function () {
/** @var TestCase $this */
seedLegacyPressReleaseSchema();
// updated_at trägt im Altsystem den Massen-/Migrationsstempel (2026) das
// darf NICHT als Veröffentlichungsdatum landen.
DB::connection('mysql_presseecho')->table('press_release')->insert([
'id' => 10,
'title' => 'Alte veröffentlichte Meldung',
'language' => 'de',
'text' => 'Inhalt',
'category_id' => 500,
'user_id' => 700,
'status' => 'published',
'hits' => 5,
'no_export' => false,
'created_at' => '2008-11-18 12:05:42',
'updated_at' => '2026-04-23 01:08:26',
'slug' => 'alte-meldung',
]);
app(PressReleaseImporter::class)->run(new ImportContext('presseecho', false, true));
$pr = PressRelease::withoutGlobalScopes()
->where('legacy_portal', 'presseecho')
->where('legacy_id', 10)
->firstOrFail();
expect($pr->status)->toBe(PressReleaseStatus::Published);
expect($pr->published_at?->format('Y-m-d H:i:s'))->toBe('2008-11-18 12:05:42');
expect($pr->created_at?->format('Y-m-d H:i:s'))->toBe('2008-11-18 12:05:42');
});
test('a non-published release has no published_at and keeps its legacy created_at', function () {
/** @var TestCase $this */
seedLegacyPressReleaseSchema();
DB::connection('mysql_presseecho')->table('press_release')->insert([
'id' => 11,
'title' => 'Entwurf',
'language' => 'de',
'text' => 'Inhalt',
'category_id' => 500,
'user_id' => 700,
'status' => 'new',
'no_export' => false,
'created_at' => '2010-03-01 09:00:00',
'updated_at' => '2026-04-23 01:08:26',
'slug' => 'entwurf',
]);
app(PressReleaseImporter::class)->run(new ImportContext('presseecho', false, true));
$pr = PressRelease::withoutGlobalScopes()
->where('legacy_portal', 'presseecho')
->where('legacy_id', 11)
->firstOrFail();
expect($pr->status)->toBe(PressReleaseStatus::Draft);
expect($pr->published_at)->toBeNull();
expect($pr->created_at?->format('Y-m-d H:i:s'))->toBe('2010-03-01 09:00:00');
});
test('a force re-import preserves the uuid and the publication date', function () {
/** @var TestCase $this */
seedLegacyPressReleaseSchema();
DB::connection('mysql_presseecho')->table('press_release')->insert([
'id' => 12,
'title' => 'Stabile Referenz',
'language' => 'de',
'text' => 'Inhalt',
'category_id' => 500,
'user_id' => 700,
'status' => 'published',
'no_export' => false,
'created_at' => '2009-04-08 15:43:42',
'updated_at' => '2026-04-21 22:07:40',
'slug' => 'stabile-referenz',
]);
app(PressReleaseImporter::class)->run(new ImportContext('presseecho', false, true));
$first = PressRelease::withoutGlobalScopes()
->where('legacy_portal', 'presseecho')->where('legacy_id', 12)->firstOrFail();
$originalUuid = $first->uuid;
// Zweiter --force-Lauf (Delta-Szenario): darf uuid nicht neu würfeln.
app(PressReleaseImporter::class)->run(new ImportContext('presseecho', false, true));
$second = PressRelease::withoutGlobalScopes()
->where('legacy_portal', 'presseecho')->where('legacy_id', 12)->firstOrFail();
expect($second->uuid)->toBe($originalUuid);
expect($second->published_at?->format('Y-m-d H:i:s'))->toBe('2009-04-08 15:43:42');
});