feat(search): implement catalog item search functionality with request validation and resource formatting

This commit is contained in:
2026-07-24 14:17:34 -03:00
parent 9e6923e732
commit 135a3bea3e
11 changed files with 337 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
<?php
namespace Tests\Feature\Catalog;
use App\Domains\Attachable\Enums\AttachmentType;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Catalog\Enums\GroupLayout;
use App\Domains\Catalog\Enums\ProductLayout;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Inventory;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class CatalogSearchTest extends TestCase
{
use RefreshDatabase;
public function test_every_tenant_gets_default_search_layouts(): void
{
$tenant = $this->createTenant('search-default');
$this->assertDatabaseHas('tenants', [
'codigo' => $tenant->codigo,
'search_product_layout' => ProductLayout::ColumnWithImage->value,
'search_group_layout' => GroupLayout::Paginated->value,
'search_items_per_page' => 12,
]);
}
public function test_search_layouts_can_be_updated_on_the_tenant_and_are_returned_by_bootstrap(): void
{
$tenant = $this->createTenant('search-config');
$this->putJson("/api/tenants/{$tenant->codigo}", [
'search_product_layout' => ProductLayout::Row->value,
'search_group_layout' => GroupLayout::SimpleVertical->value,
'search_items_per_page' => 24,
])
->assertOk()
->assertJsonPath('data.search_product_layout', ProductLayout::Row->value)
->assertJsonPath('data.search_group_layout', GroupLayout::SimpleVertical->value)
->assertJsonPath('data.search_items_per_page', 24);
$this->getJson("/api/tenants/bootstrap/{$tenant->dominio}")
->assertOk()
->assertJsonPath('data.search_product_layout', ProductLayout::Row->value)
->assertJsonPath('data.search_group_layout', GroupLayout::SimpleVertical->value)
->assertJsonPath('data.search_items_per_page', 24);
}
public function test_search_is_tenant_scoped_relevant_and_uses_configured_page_size(): void
{
$tenant = $this->createTenant('search-items');
$otherTenant = $this->createTenant('other-search-items');
$tenant->update(['search_items_per_page' => 4]);
foreach (range(1, 5) as $number) {
$this->createCatalogItem($tenant, "Running {$number}");
}
$exactMatch = $this->createCatalogItem($tenant, 'Running');
$this->createCatalogItem($tenant, 'Unrelated');
$this->createCatalogItem($otherTenant, 'Running foreign');
$response = $this->getJson(
"/api/tenants/{$tenant->codigo}/catalog-items?q=running"
);
$response
->assertOk()
->assertJsonPath('meta.current_page', 1)
->assertJsonPath('meta.per_page', 4)
->assertJsonPath('meta.total', 6)
->assertJsonCount(4, 'data')
->assertJsonPath('data.0.id', $exactMatch->id)
->assertJsonMissing(['nombre' => 'Running foreign'])
->assertJsonMissing(['nombre' => 'Unrelated']);
}
public function test_search_validates_the_query(): void
{
$tenant = $this->createTenant('search-validation');
$this->getJson("/api/tenants/{$tenant->codigo}/catalog-items?q=a")
->assertUnprocessable()
->assertJsonValidationErrors('q');
}
private function createCatalogItem(Tenant $tenant, string $name): CatalogItem
{
$inventory = Inventory::query()->create(['real_stock' => 10]);
return CatalogItem::query()->create([
'tenant_code' => $tenant->codigo,
'inventory_id' => $inventory->id,
'slug' => str($name)->slug()->toString(),
'nombre' => $name,
'descripcion' => "{$name} description",
'precio' => 100,
]);
}
private function createTenant(string $code): Tenant
{
$headerLogo = $this->createAttachment("{$code}-header");
$footerLogo = $this->createAttachment("{$code}-footer");
return Tenant::query()->create([
'codigo' => $code,
'nombre' => ucfirst($code),
'dominio' => "{$code}.local",
'primary_color' => '#000000',
'secondary_color' => '#000000',
'danger_color' => '#000000',
'success_color' => '#000000',
'header_bg_color' => '#000000',
'footer_bg_color' => '#000000',
'header_logo_id' => $headerLogo->id,
'footer_logo_id' => $footerLogo->id,
]);
}
private function createAttachment(string $name): Attachment
{
return Attachment::query()->create([
'path' => "test/{$name}.png",
'filename' => "{$name}.png",
'type' => AttachmentType::Image,
'mime_type' => 'image/png',
]);
}
}