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); } }