Files
Investidor-V2/tests/Unit/SearchTermsTest.php
T

112 lines
6.1 KiB
PHP

<?php
namespace Tests\Unit;
use App\Http\Controllers\GlobalSearchController;
use App\Services\SearchTerms;
use Illuminate\Database\PostgresConnection;
use PHPUnit\Framework\TestCase;
final class SearchTermsTest extends TestCase
{
public function test_count_questions_select_a_category_instead_of_searching_question_words(): void
{
$search = new SearchTerms;
$intent = $search->countIntent('Quantos corredores existem?');
$this->assertSame('corridor', $intent['kind']);
$this->assertSame('', $intent['filter']);
$this->assertSame('corredores', $intent['label']);
$this->assertSame('Oportunidades', $search->countIntent('Quantas oportunidades existem no portal?')['category']);
$this->assertSame('em nampula', $search->countIntent('Quantos corredores existem em Nampula?')['filter']);
$this->assertSame('cluster', $search->countIntent('Qual é o número de clusters disponíveis?')['kind']);
$this->assertNull($search->countIntent('oportunidades nos corredores'));
$this->assertNull($search->countIntent('documentos sobre quantos corredores existem'));
}
public function test_investor_requests_distinguish_products_guidance_and_lists(): void
{
$search = new SearchTerms;
$this->assertSame('guide', $search->requestIntent('como licenciar uma empresa')['type']);
$this->assertSame('guide', $search->requestIntent('como faço para licenciar a empresa em Nampula?')['type']);
$this->assertNull($search->requestIntent('como licenciar software'));
$this->assertSame('list', $search->requestIntent('quais são os corredores existentes')['type']);
$this->assertSame('Produtos financeiros', $search->requestIntent('produtos financeiros em Nampula')['category']);
$this->assertNull($search->requestIntent('tipo de produtos e sua localização')['category']);
$this->assertSame('', $search->requestIntent('tipo de produtos e sua localização')['filter']);
$this->assertSame('grafite em cabo delgado', $search->requestIntent('onde encontrar grafite em Cabo Delgado?')['filter']);
}
public function test_matches_all_words_in_any_order_without_case_or_accents(): void
{
$search = new SearchTerms;
$tokens = $search->tokens(' NAMPULA agronegócio Agronegócio ');
$this->assertSame(['nampula', 'agronegocio'], $tokens);
$this->assertTrue($search->matches('Agronegócio em Nampula', $tokens));
$this->assertFalse($search->matches('Agronegócio em Gaza', $tokens));
$this->assertSame([], $search->tokens(' '));
}
public function test_sql_matches_each_word_across_fields_with_bound_literal_patterns(): void
{
$connection = new PostgresConnection(fn () => throw new \LogicException('A database must not be opened.'));
$query = $connection->table('records');
$search = new SearchTerms;
$search->constrain($query, ['title', 'description'], $search->tokens('100% a_b'));
$sql = $query->toSql();
$this->assertStringContainsString('translate(lower(coalesce(title::text', $sql);
$this->assertStringContainsString(' or ', $sql);
$this->assertStringContainsString(' and ', $sql);
$this->assertSame(['%100!%%', '%100!%%', '%a!_b%', '%a!_b%'], $query->getBindings());
$this->assertStringNotContainsString('100%', $sql);
}
public function test_static_services_can_be_found_without_a_database(): void
{
$pages = (new \ReflectionMethod(GlobalSearchController::class, 'pages'))->invoke(new GlobalSearchController);
$search = new SearchTerms;
$matches = array_values(array_filter($pages, fn ($page) => $search->matches(implode(' ', $page), $search->tokens('FISCAL simulador'))));
$this->assertCount(1, $matches);
$this->assertSame('/simulador-fiscal', $matches[0]['url']);
}
public function test_spelling_variants_common_words_and_title_relevance(): void
{
$search = new SearchTerms;
$tokens = $search->tokens('setores de investimento');
$this->assertTrue($search->matches('Sectores de investimento', $tokens));
$this->assertFalse($search->matches('Sector de saúde', $tokens));
$this->assertTrue($search->matches('Projectos âncora', $search->tokens('projeto')));
$this->assertTrue($search->matches('Oportunidades de investimento em energia', $search->tokens('Como investir em energia?')));
$this->assertGreaterThan($search->score('Como investir', 'investir', ['investir']), $search->score('Investir', 'investir', ['investir']));
$this->assertSame(['de'], $search->tokens('de'));
}
public function test_direct_titles_precede_titles_and_descriptions_that_reference_the_query(): void
{
$search = new SearchTerms;
$phrase = 'corredor da beira';
$tokens = $search->tokens($phrase);
$titles = ['Açúcar no Corredor da Beira', 'Corredor da Beira e investimentos', 'Corredor da Beira', 'Produção de açúcar'];
usort($titles, fn ($a, $b) => $search->score($b, $phrase, $tokens) <=> $search->score($a, $phrase, $tokens));
$this->assertSame(['Corredor da Beira', 'Corredor da Beira e investimentos', 'Açúcar no Corredor da Beira', 'Produção de açúcar'], $titles);
$this->assertSame($search->score('Corredor da Beira', $phrase, $tokens), $search->score('CORREDOR DA BEIRA', $phrase, $tokens));
}
public function test_database_relevance_uses_the_same_levels_and_escaped_prefix(): void
{
$connection = new PostgresConnection(fn () => throw new \LogicException('A database must not be opened.'));
$query = $connection->table('records');
$search = new SearchTerms;
$search->orderByRelevance($query, 'title', '100% açúcar', $search->tokens('100% açúcar'), 'id');
$this->assertStringContainsString('THEN 1000', $query->toSql());
$this->assertStringContainsString('THEN 600', $query->toSql());
$this->assertStringContainsString('THEN 400', $query->toSql());
$this->assertSame(['100% acucar', '100!% acucar%', '%100!% acucar%'], array_slice($query->getBindings(), 0, 3));
}
}