toBeTrue(); expect(Schema::hasColumns('display_playlists', [ 'id', 'display_id', 'status', 'published_at', 'published_by', 'notes', 'created_at', 'updated_at', ]))->toBeTrue(); }); it('creates the display_playlist_items table', function () { expect(Schema::hasTable('display_playlist_items'))->toBeTrue(); expect(Schema::hasColumns('display_playlist_items', [ 'id', 'display_playlist_id', 'display_version_id', 'sort_order', 'created_at', 'updated_at', ]))->toBeTrue(); }); it('adds is_test and preview_token to displays', function () { expect(Schema::hasColumns('displays', ['is_test', 'preview_token']))->toBeTrue(); }); it('migrates existing pivot entries into a published playlist with same ordering', function () { $display = Display::factory()->create(); $moduleA = DisplayVersion::factory()->create(['name' => 'Modul A']); $moduleB = DisplayVersion::factory()->create(['name' => 'Modul B']); DB::table('display_playlists')->where('display_id', $display->id)->delete(); DB::table('display_display_version')->insert([ ['display_id' => $display->id, 'display_version_id' => $moduleA->id, 'sort_order' => 0], ['display_id' => $display->id, 'display_version_id' => $moduleB->id, 'sort_order' => 1], ]); $migration = require database_path('migrations/2026_05_11_113330_migrate_pivot_to_display_playlists.php'); $migration->up(); $playlist = DisplayPlaylist::query() ->where('display_id', $display->id) ->where('status', DisplayPlaylist::STATUS_PUBLISHED) ->first(); expect($playlist)->not->toBeNull(); expect($playlist->published_at)->not->toBeNull(); $orderedIds = $playlist->items()->pluck('display_version_id')->all(); expect($orderedIds)->toBe([$moduleA->id, $moduleB->id]); }); it('is idempotent and does not duplicate published playlists on re-run', function () { $display = Display::factory()->create(); $module = DisplayVersion::factory()->create(); DB::table('display_playlists')->where('display_id', $display->id)->delete(); DB::table('display_display_version')->insert([ ['display_id' => $display->id, 'display_version_id' => $module->id, 'sort_order' => 0], ]); $migration = require database_path('migrations/2026_05_11_113330_migrate_pivot_to_display_playlists.php'); $migration->up(); $migration->up(); $count = DisplayPlaylist::query() ->where('display_id', $display->id) ->where('status', DisplayPlaylist::STATUS_PUBLISHED) ->count(); expect($count)->toBe(1); }); it('does not break the legacy versions() relation', function () { $display = Display::factory()->create(); $module = DisplayVersion::factory()->create(); DB::table('display_display_version')->insert([ ['display_id' => $display->id, 'display_version_id' => $module->id, 'sort_order' => 0], ]); expect($display->fresh()->versions)->toHaveCount(1); }); it('exposes a live playlist relation and a draft playlist relation on display', function () { $display = Display::factory()->create(); $live = DisplayPlaylist::factory()->published()->create(['display_id' => $display->id]); $draft = DisplayPlaylist::factory()->draft()->create(['display_id' => $display->id]); expect($display->livePlaylist->is($live))->toBeTrue(); expect($display->draftPlaylist->is($draft))->toBeTrue(); }); it('prevents two playlists with the same status for one display', function () { $display = Display::factory()->create(); DisplayPlaylist::factory()->published()->create(['display_id' => $display->id]); DisplayPlaylist::factory()->published()->create(['display_id' => $display->id]); })->throws(\Illuminate\Database\QueryException::class); it('orders playlist items by sort_order on the modules relation', function () { $display = Display::factory()->create(); $playlist = DisplayPlaylist::factory()->published()->create(['display_id' => $display->id]); $modules = DisplayVersion::factory()->count(3)->create(); DisplayPlaylistItem::factory()->create([ 'display_playlist_id' => $playlist->id, 'display_version_id' => $modules[2]->id, 'sort_order' => 2, ]); DisplayPlaylistItem::factory()->create([ 'display_playlist_id' => $playlist->id, 'display_version_id' => $modules[0]->id, 'sort_order' => 0, ]); DisplayPlaylistItem::factory()->create([ 'display_playlist_id' => $playlist->id, 'display_version_id' => $modules[1]->id, 'sort_order' => 1, ]); expect($playlist->modules->pluck('id')->all())->toBe([ $modules[0]->id, $modules[1]->id, $modules[2]->id, ]); }); it('generates a preview token on demand and persists it', function () { $display = Display::factory()->create(['preview_token' => null]); $token = $display->ensurePreviewToken(); expect($token)->toBeString(); expect(strlen($token))->toBeGreaterThanOrEqual(40); expect($display->fresh()->preview_token)->toBe($token); expect($display->ensurePreviewToken())->toBe($token); }); it('marks a display as test display via factory state', function () { $display = Display::factory()->test()->create(); expect($display->is_test)->toBeTrue(); expect($display->name)->toBe('Test-Display'); });