Files
Investidor-V2/tests/Feature/QuickOpportunitySearchTest.php
2026-11-02 20:31:15 +02:00

90 lines
6.0 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Opportunity;
use App\Models\Sector;
use Database\Seeders\TerritoryReferenceSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Inertia\Testing\AssertableInertia as Assert;
use Tests\TestCase;
final class QuickOpportunitySearchTest extends TestCase
{
use RefreshDatabase;
private Sector $sector;
protected function setUp(): void
{
parent::setUp();
$this->withoutVite();
$this->sector = Sector::create(['name' => 'Agronegócio sintético QA', 'slug' => 'agronegocio-synthetic', 'is_active' => true, 'published_at' => now()]);
}
private function record(string $title, string $summary, array $attributes = []): Opportunity
{
return Opportunity::create([...['sector_id' => $this->sector->id, 'title' => $title, 'summary' => $summary,
'reference' => 'SYNTHETIC-'.Str::uuid(), 'slug' => 'synthetic-'.Str::uuid(), 'status' => 'published', 'published_at' => now()->subMinute()], ...$attributes]);
}
public function test_plantio_de_algodao_finds_cultivation_variants_without_relabelling_processing_and_combines_province_and_sector(): void
{
$this->seed(TerritoryReferenceSeeder::class);
$province = DB::table('geo_locations')->where('location_type', 'province')->where('name', 'Nampula')->value('id');
$district = DB::table('geo_locations')->where('parent_id', $province)->where('location_type', 'district')->value('id');
$this->assertNotEmpty($district);
$crop = $this->record('SINTÉTICO QA — exploração agrícola', 'Cultivo comercial de algodão, exemplo fictício para ensaio.', ['primary_territory_id' => $district]);
$processing = $this->record('SINTÉTICO QA — unidade de transformação', 'Descaroçamento, fiação e produção de óleo de algodão.', ['primary_territory_id' => $district]);
$this->record('SINTÉTICO QA — outra cultura', 'Cultivo de milho, sem relação com a pesquisa solicitada.');
$this->record('SINTÉTICO QA — privado', 'Cultivo de algodão.', ['status' => 'draft']);
$this->record('SINTÉTICO QA — futuro', 'Cultivo de algodão.', ['published_at' => now()->addDay()]);
$deleted = $this->record('SINTÉTICO QA — eliminado', 'Cultivo de algodão.');
$deleted->delete();
$before = DB::table('opportunities')->orderBy('id')->get()->toJson();
foreach (['plantio de algodao', 'PLANTIO DE ALGODÃO', 'algodao cultivo', 'plantação de algodão Nampula'] as $text) {
foreach (['/api/v1/investment/search', '/api/v1/opportunities/search'] as $endpoint) {
$this->getJson($endpoint.'?'.http_build_query(['q' => $text]))->assertOk()
->assertJsonPath('total', 1)->assertJsonPath('data.0.id', $crop->id);
}
}
$this->getJson('/api/v1/investment/search?'.http_build_query(['q' => 'AGRONEGOCIO, NAMPULA']))->assertOk()->assertJsonPath('total', 2);
$this->get('/oportunidades?'.http_build_query(['q' => 'plantio de algodao']))->assertOk()->assertInertia(fn (Assert $page) => $page
->component('investment/catalogue')->where('filters.q', 'plantio de algodao')->where('results.total', 1)
->where('results.data.0.id', $crop->id)->where('relatedSearch', null));
$this->getJson('/api/v1/investment/map?'.http_build_query(['q' => 'plantio de algodao']))->assertOk()->assertJsonCount(1, 'unlocated');
$csv = $this->get('/api/v1/investment/export?'.http_build_query(['q' => 'plantio de algodao']))->assertOk()->streamedContent();
$this->assertStringContainsString($crop->title, $csv);
$this->assertStringNotContainsString($processing->title, $csv);
$this->assertSame($before, DB::table('opportunities')->orderBy('id')->get()->toJson());
}
public function test_if_only_processing_exists_related_fiches_are_separate_and_never_widen_other_filters(): void
{
$processing = $this->record('SINTÉTICO QA — transformação de algodão', 'Descaroçamento e transformação de algodão.');
$this->record('SINTÉTICO QA — privado', 'Descaroçamento de algodão.', ['status' => 'draft']);
$this->record('SINTÉTICO QA — outra cadeia', 'Cultivo de milho.');
$this->get('/oportunidades?'.http_build_query(['q' => 'plantio de algodao']))->assertOk()->assertInertia(fn (Assert $page) => $page
->where('results.total', 0)->where('relatedSearch.query', 'plantio de algodao')->where('relatedSearch.subject', 'algodao')
->has('relatedSearch.items', 1)->where('relatedSearch.items.0.id', $processing->id)
->where('relatedSearch.href', '/oportunidades?q=algodao'));
$this->get('/oportunidades?'.http_build_query(['q' => 'plantio de algodao', 'category' => 'financing']))->assertOk()->assertInertia(fn (Assert $page) => $page
->where('results.total', 0)->has('relatedSearch.items', 0)->where('filters.category', 'financing')
->where('relatedSearch.href', '/oportunidades?category=financing&q=algodao'));
}
public function test_literal_wildcards_and_unmatched_words_do_not_return_unrelated_records(): void
{
$literal = $this->record('SINTÉTICO QA — 100% algodão', 'Referência sintética.', ['reference' => 'SYNTHETIC_REF_ABC']);
$this->record('SINTÉTICO QA — 1000 algodão', 'Referência sintética.', ['reference' => 'SYNTHETICXREFXABC']);
foreach (['100%', 'SYNTHETIC_REF_ABC'] as $query) {
$this->getJson('/api/v1/investment/search?'.http_build_query(['q' => $query]))->assertOk()
->assertJsonPath('total', 1)->assertJsonPath('data.0.id', $literal->id);
}
$this->getJson('/api/v1/investment/search?'.http_build_query(['q' => 'de e em']))->assertOk()->assertJsonPath('total', 0);
$this->getJson('/api/v1/investment/search?'.http_build_query(['q' => 'palavra-inexistente-synthetic']))->assertOk()->assertJsonPath('total', 0);
}
}