b2in/tests/Feature/InteriorReleaseSeederTest.php
Kevin Adametz b5ed756d67 Git-Deploy per Tag, Betriebsüberwachung und Release-Seeder für den Livegang
- dev/betrieb/deploy.sh: Deploy nur per Tag, mit --seed (Seeder im Wartungsmodus)
  und --initial (Vorschau/Live einmalig an Ort und Stelle auf Git umstellen)
- dev/betrieb/vorschau-auffrischen.sh: Live-DB und Live-Medien in die Vorschau (Generalprobe)
- InteriorReleaseSeeder: alle Inhaltsschritte des Relaunchs in einem Lauf, idempotent;
  Displays, Playlists und Mediathek bleiben unberührt
- DisplayInteriorBrandingSeeder überschreibt keinen inzwischen gepflegten Claim mehr
- Monitoring: GlitchTip (Server + Browser), /health mit Token, Scheduler-Herzschlag,
  Alarme per Mail und ntfy, Version in about und /health
- public/build versioniert; .gitignore: macOS-Muster Icon traf resources/views/flux/icon/
- Runbook: dev/betrieb/README.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:56:23 +00:00

88 lines
3.8 KiB
PHP
Raw Permalink 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\DisplayVersionType;
use App\Models\Display;
use App\Models\DisplayMedia;
use App\Models\DisplayPlaylist;
use App\Models\DisplayVersion;
use App\Models\DisplayVersionItem;
use Database\Seeders\InteriorReleaseSeeder;
use FluxCms\Core\Models\CmsContent;
use FluxCms\Core\Models\CmsMedia;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
/**
* Release-Seeder für Vorschau und Live (08.10.2026): bringt den alten Website-Stand auf den
* neuen, lässt Displays und Mediathek stehen und ändert beim zweiten Lauf nichts mehr.
*/
beforeEach(function () {
Storage::fake('public');
});
/**
* Inhalt der Tabellen, die der Release-Seeder beschreibt – ohne Zeitstempel.
*
* @return array<string, mixed>
*/
function releaseSnapshot(): array
{
$tables = ['flux_cms_contents', 'cms_brands', 'cms_articles', 'flux_cms_media', 'display_versions', 'display_version_items', 'display_media', 'displays'];
return collect($tables)->mapWithKeys(fn (string $table): array => [
$table => DB::table($table)->orderBy('id')->get()
->map(fn (object $row): array => collect((array) $row)->except(['created_at', 'updated_at'])->all())
->all(),
])->all();
}
it('brings the old website content up to date and keeps the displays and the media library', function () {
// Alter Website-Stand, wie er auf Live liegt
CmsContent::create(['group' => 'home', 'key' => 'hero', 'type' => 'json', 'order' => 0, 'value' => [
'de' => ['title' => 'Connecting Design and Property', 'subtitle' => 'Alt'],
]]);
// Seit der Vorschau auf Live gepflegt: Display, Modul, Playlist, Display-Mediathek, CMS-Mediathek
$module = DisplayVersion::factory()->create([
'type' => DisplayVersionType::B2in,
'settings' => ['theme' => 'dark', 'header_logo_url' => '/_cabinet/assets/b2in-interior-logo-negative.svg', 'header_claim' => 'Herbstaktion'],
]);
$item = DisplayVersionItem::factory()->create([
'display_version_id' => $module->id,
'content' => ['image_url' => '/storage/display-media/herbst.webp', 'duration' => 8],
]);
$display = Display::factory()->create(['name' => 'Kundenstopper']);
$playlist = DisplayPlaylist::factory()->create(['display_id' => $display->id]);
$displayMedia = DisplayMedia::factory()->create(['filename' => 'herbst.webp']);
$upload = CmsMedia::create([
'filename' => 'kunde-upload.jpg', 'disk' => 'public', 'path' => 'cms/media/originals/kunde.jpg',
'type' => 'image', 'mime_type' => 'image/jpeg', 'file_size' => 1, 'collection' => 'Website', 'conversions' => [],
]);
$this->seed(InteriorReleaseSeeder::class);
$hero = CmsContent::query()->where('group', 'home')->where('key', 'hero')->first()->getTranslation('value', 'de');
expect($hero['title'])->not->toContain('Connecting Design')
->and($hero['slides'])->toHaveCount(11);
expect($module->fresh()->settings['header_claim'])->toBe('Herbstaktion')
->and($item->fresh()->content)->toBe(['image_url' => '/storage/display-media/herbst.webp', 'duration' => 8])
->and($display->fresh()->name)->toBe('Kundenstopper')
->and($playlist->fresh())->not->toBeNull()
->and($displayMedia->fresh()->filename)->toBe('herbst.webp')
->and($upload->fresh())->not->toBeNull()
->and(CmsMedia::query()->where('filename', 'b2in/slideshow/rotpunkt-fenix-house.webp')->exists())->toBeTrue();
$this->get('/')->assertSuccessful()->assertSee('B2in Interior');
$this->get('/marken')->assertSuccessful();
$this->get('/about')->assertSuccessful();
});
it('changes nothing on a second run', function () {
$this->seed(InteriorReleaseSeeder::class);
$first = releaseSnapshot();
$this->seed(InteriorReleaseSeeder::class);
expect(releaseSnapshot())->toEqual($first);
});