Add Popular Routes
Curated from/to destination pairs for marketing content, editable via a Filament resource and served publicly (no auth, IP-throttled like CMS pages) through GET /api/v1/popular-routes. The API response trims each nested destination down to id/name/mm_name rather than the full catalog resource.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Livewire\Livewire;
|
||||
use Modules\Catalog\Models\Destination;
|
||||
use Modules\Routing\Filament\Resources\PopularRoutes\Pages\CreatePopularRoute;
|
||||
use Modules\Routing\Filament\Resources\PopularRoutes\Pages\EditPopularRoute;
|
||||
use Modules\Routing\Filament\Resources\PopularRoutes\Pages\ListPopularRoutes;
|
||||
use Modules\Routing\Models\PopularRoute;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
use function Pest\Laravel\assertDatabaseHas;
|
||||
|
||||
beforeEach(function () {
|
||||
Permission::findOrCreate('manage_routes', 'web');
|
||||
|
||||
$this->admin = User::factory()->create()->givePermissionTo('manage_routes');
|
||||
$this->actingAs($this->admin);
|
||||
});
|
||||
|
||||
test('can list popular routes', function () {
|
||||
$routes = PopularRoute::factory()->count(3)->create();
|
||||
|
||||
Livewire::test(ListPopularRoutes::class)
|
||||
->assertOk()
|
||||
->assertCanSeeTableRecords($routes);
|
||||
});
|
||||
|
||||
test('can create a popular route', function () {
|
||||
$from = Destination::factory()->create();
|
||||
$to = Destination::factory()->create();
|
||||
|
||||
Livewire::test(CreatePopularRoute::class)
|
||||
->fillForm([
|
||||
'from_destination_id' => $from->id,
|
||||
'to_destination_id' => $to->id,
|
||||
'description' => 'A scenic drive.',
|
||||
'mm_description' => 'သာယာသောခရီးစဉ်။',
|
||||
'is_active' => true,
|
||||
])
|
||||
->call('create')
|
||||
->assertNotified()
|
||||
->assertRedirect();
|
||||
|
||||
assertDatabaseHas(PopularRoute::class, [
|
||||
'from_destination_id' => $from->id,
|
||||
'to_destination_id' => $to->id,
|
||||
'description' => 'A scenic drive.',
|
||||
'mm_description' => 'သာယာသောခရီးစဉ်။',
|
||||
'is_active' => true,
|
||||
]);
|
||||
});
|
||||
|
||||
test('from and to destinations must be different', function () {
|
||||
$destination = Destination::factory()->create();
|
||||
|
||||
Livewire::test(CreatePopularRoute::class)
|
||||
->fillForm([
|
||||
'from_destination_id' => $destination->id,
|
||||
'to_destination_id' => $destination->id,
|
||||
])
|
||||
->call('create')
|
||||
->assertHasFormErrors(['to_destination_id' => 'different']);
|
||||
});
|
||||
|
||||
test('the same from/to destination pair cannot be created twice', function () {
|
||||
$existing = PopularRoute::factory()->create();
|
||||
|
||||
Livewire::test(CreatePopularRoute::class)
|
||||
->fillForm([
|
||||
'from_destination_id' => $existing->from_destination_id,
|
||||
'to_destination_id' => $existing->to_destination_id,
|
||||
])
|
||||
->call('create')
|
||||
->assertHasFormErrors(['to_destination_id' => 'unique']);
|
||||
});
|
||||
|
||||
test('editing a popular route keeps its own from/to pair valid', function () {
|
||||
$popularRoute = PopularRoute::factory()->create();
|
||||
|
||||
Livewire::test(EditPopularRoute::class, ['record' => $popularRoute->getRouteKey()])
|
||||
->fillForm(['description' => 'Updated description.'])
|
||||
->call('save')
|
||||
->assertHasNoFormErrors();
|
||||
|
||||
assertDatabaseHas(PopularRoute::class, [
|
||||
'id' => $popularRoute->id,
|
||||
'description' => 'Updated description.',
|
||||
]);
|
||||
});
|
||||
|
||||
test('a user without manage_routes is forbidden from the popular routes page', function () {
|
||||
$support = User::factory()->create();
|
||||
$this->actingAs($support);
|
||||
|
||||
$this->get('/admin/popular-routes')->assertForbidden();
|
||||
});
|
||||
|
||||
test('the model rejects saving with the same from and to destination directly', function () {
|
||||
$destination = Destination::factory()->create();
|
||||
|
||||
expect(fn () => PopularRoute::factory()->create([
|
||||
'from_destination_id' => $destination->id,
|
||||
'to_destination_id' => $destination->id,
|
||||
]))->toThrow(InvalidArgumentException::class);
|
||||
});
|
||||
|
||||
test('the database rejects a duplicate from/to pair directly', function () {
|
||||
$existing = PopularRoute::factory()->create();
|
||||
|
||||
expect(fn () => PopularRoute::factory()->create([
|
||||
'from_destination_id' => $existing->from_destination_id,
|
||||
'to_destination_id' => $existing->to_destination_id,
|
||||
]))->toThrow(QueryException::class);
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
use Modules\Catalog\Models\Destination;
|
||||
use Modules\Routing\Models\PopularRoute;
|
||||
|
||||
test('lists active popular routes with nested destinations, no auth required', function () {
|
||||
$from = Destination::factory()->create();
|
||||
$to = Destination::factory()->create();
|
||||
|
||||
PopularRoute::factory()->create([
|
||||
'from_destination_id' => $from->id,
|
||||
'to_destination_id' => $to->id,
|
||||
'description' => 'A scenic drive.',
|
||||
'mm_description' => 'သာယာသောခရီးစဉ်။',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
PopularRoute::factory()->create(['is_active' => false]);
|
||||
|
||||
$this->getJson('/api/v1/popular-routes')
|
||||
->assertSuccessful()
|
||||
->assertJsonCount(1, 'data')
|
||||
->assertJsonPath('data.0.from_destination.id', $from->id)
|
||||
->assertJsonPath('data.0.to_destination.id', $to->id)
|
||||
->assertJsonPath('data.0.description', 'A scenic drive.')
|
||||
->assertJsonPath('data.0.mm_description', 'သာယာသောခရီးစဉ်။');
|
||||
});
|
||||
|
||||
test('the nested destinations only expose id, name and mm_name, not the full catalog fields', function () {
|
||||
$from = Destination::factory()->create();
|
||||
$to = Destination::factory()->create();
|
||||
|
||||
PopularRoute::factory()->create([
|
||||
'from_destination_id' => $from->id,
|
||||
'to_destination_id' => $to->id,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/v1/popular-routes')
|
||||
->assertSuccessful()
|
||||
->assertJsonPath('data.0.from_destination', [
|
||||
'id' => $from->id,
|
||||
'name' => $from->name,
|
||||
'mm_name' => $from->mm_name,
|
||||
])
|
||||
->assertJsonPath('data.0.to_destination', [
|
||||
'id' => $to->id,
|
||||
'name' => $to->name,
|
||||
'mm_name' => $to->mm_name,
|
||||
]);
|
||||
});
|
||||
|
||||
test('an inactive popular route is excluded from the list', function () {
|
||||
PopularRoute::factory()->create(['is_active' => false]);
|
||||
|
||||
$this->getJson('/api/v1/popular-routes')
|
||||
->assertSuccessful()
|
||||
->assertJsonCount(0, 'data');
|
||||
});
|
||||
Reference in New Issue
Block a user