Files
SkinbaseNova/tests/Feature/ContentTypeDynamicRoutingTest.php

126 lines
4.2 KiB
PHP

<?php
use App\Services\ArtworkService;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
uses(RefreshDatabase::class);
function dynamicRoutingEmptyPaginator(string $path = '/'): LengthAwarePaginator
{
return (new LengthAwarePaginator(collect(), 0, 20, 1))->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',
]);
});