setPath($path); } beforeEach(function () { $artworksMock = \Mockery::mock(ArtworkService::class); $artworksMock->shouldReceive('getFeaturedArtworks')->andReturn(dynamicRoutingEmptyPaginator('/'))->byDefault(); $artworksMock->shouldReceive('getLatestArtworks')->andReturn(collect())->byDefault(); $this->app->instance(ArtworkService::class, $artworksMock); $this->contentTypeId = DB::table('content_types')->insertGetId([ 'name' => 'Digital Art', 'slug' => 'digital-art', 'description' => 'Digital art uploads', 'order' => 1, 'created_at' => now(), 'updated_at' => now(), ]); DB::table('content_type_slug_histories')->insert([ 'content_type_id' => $this->contentTypeId, 'old_slug' => 'concept-art', 'created_at' => now(), 'updated_at' => now(), ]); }); it('redirects old explore content type slugs to the current canonical slug', function () { $this->get('/explore/concept-art?sort=latest') ->assertRedirect('/digital-art?sort=latest'); }); it('redirects old rss explore slugs while preserving mode and query string', function () { $this->get('/rss/explore/concept-art/trending?limit=10') ->assertRedirect('/rss/explore/digital-art/trending?limit=10'); }); it('redirects old artwork paths to the current canonical content type slug', function () { $this->get('/concept-art/fantasy/castle-sunrise') ->assertRedirect('/digital-art/fantasy/castle-sunrise'); }); it('returns 404 for unknown dynamic content type slugs', function () { $this->get('/made-up-content-type') ->assertNotFound(); }); it('lists resolver-backed content types on the rss feeds page', function () { $this->get('/rss-feeds') ->assertOk() ->assertSee('/rss/explore/digital-art', false) ->assertSee('Digital Art'); }); it('keeps static routes ahead of dynamic content type routes even with bad data', function () { DB::table('content_types')->insert([ 'name' => 'RSS Collision', 'slug' => 'rss', 'description' => 'Invalid but useful for route-order regression coverage', 'order' => 99, 'created_at' => now(), 'updated_at' => now(), ]); $this->get('/rss') ->assertOk() ->assertHeader('Content-Type', 'application/rss+xml; charset=utf-8'); }); it('blocks reserved content type slugs in the admin save flow', function () { $admin = User::factory()->create(['role' => 'admin']); $this->withoutMiddleware(); $this->actingAs($admin) ->post(route('admin.cp.artworks.content-types.store'), [ 'name' => 'Reserved Help', 'slug' => 'help', 'description' => 'Should be rejected', 'order' => 1, ]) ->assertStatus(422) ->assertSeeText('This content type slug is reserved by the public routing layer.'); }); it('allows updating an existing content type that already uses the same reserved slug', function () { $admin = User::factory()->create(['role' => 'admin']); $contentTypeId = DB::table('content_types')->insertGetId([ 'name' => 'Members', 'slug' => 'members', 'description' => 'Legacy reserved slug still in use', 'order' => 5, 'created_at' => now(), 'updated_at' => now(), ]); $this->withoutMiddleware(); $this->actingAs($admin) ->post(route('admin.cp.artworks.content-types.update', ['id' => $contentTypeId]), [ 'name' => 'Members Updated', 'slug' => 'members', 'description' => 'Updated without renaming slug', 'order' => 5, ]) ->assertRedirect(route('admin.cp.artworks.content-types.main')); $this->assertDatabaseHas('content_types', [ 'id' => $contentTypeId, 'slug' => 'members', 'name' => 'Members Updated', 'description' => 'Updated without renaming slug', ]); });