diff --git a/app/Http/Controllers/Settings/BankSearchController.php b/app/Http/Controllers/Settings/BankSearchController.php index de18f9d5..2578bddf 100644 --- a/app/Http/Controllers/Settings/BankSearchController.php +++ b/app/Http/Controllers/Settings/BankSearchController.php @@ -9,9 +9,6 @@ use Illuminate\Http\Request; class BankSearchController extends Controller { - /** - * Search banks by query. - */ public function __invoke(Request $request): JsonResponse { $query = $request->input('query', ''); @@ -29,7 +26,14 @@ class BankSearchController extends Controller ->orWhereNull('user_id'); }) ->where('name', 'like', '%'.$query.'%') - ->orderBy('name') + ->orderByRaw(' + CASE + WHEN LOWER(name) = LOWER(?) THEN 1 + WHEN LOWER(name) LIKE LOWER(?) THEN 2 + ELSE 3 + END, + name + ', [$query, $query.'%']) ->limit(100) ->get(['id', 'name', 'logo']); diff --git a/tests/Feature/Settings/BankSearchTest.php b/tests/Feature/Settings/BankSearchTest.php new file mode 100644 index 00000000..6644d7ab --- /dev/null +++ b/tests/Feature/Settings/BankSearchTest.php @@ -0,0 +1,109 @@ +user = User::factory()->create(); + actingAs($this->user); +}); + +it('requires at least 3 characters to search', function () { + $response = $this->getJson(route('banks.search', ['query' => 'ab'])); + + $response->assertSuccessful() + ->assertJson([ + 'banks' => [], + 'message' => 'Type at least 3 characters to search', + ]); +}); + +it('prioritizes exact matches first', function () { + Bank::factory()->create(['name' => 'ABC Banking']); + Bank::factory()->create(['name' => 'ING']); + Bank::factory()->create(['name' => 'Test ING Bank']); + Bank::factory()->create(['name' => 'Other Company']); + + $response = $this->getJson(route('banks.search', ['query' => 'ING'])); + + $response->assertSuccessful(); + + $banks = $response->json('banks'); + + expect($banks)->toHaveCount(3); + expect($banks[0]['name'])->toBe('ING'); +}); + +it('prioritizes names starting with query second', function () { + Bank::factory()->create(['name' => 'ABC Bank']); + Bank::factory()->create(['name' => 'Test Bank']); + Bank::factory()->create(['name' => 'Bank of America']); + Bank::factory()->create(['name' => 'Banking Solutions']); + + $response = $this->getJson(route('banks.search', ['query' => 'Bank'])); + + $response->assertSuccessful(); + + $banks = $response->json('banks'); + + expect($banks)->toHaveCount(4); + expect($banks[0]['name'])->toBe('Bank of America'); + expect($banks[1]['name'])->toBe('Banking Solutions'); + expect($banks[2]['name'])->toBe('ABC Bank'); + expect($banks[3]['name'])->toBe('Test Bank'); +}); + +it('sorts results alphabetically within priority groups', function () { + Bank::factory()->create(['name' => 'Zebra ING Bank']); + Bank::factory()->create(['name' => 'ING Direct']); + Bank::factory()->create(['name' => 'ING']); + Bank::factory()->create(['name' => 'Alpha ING Bank']); + Bank::factory()->create(['name' => 'ING Bank']); + + $response = $this->getJson(route('banks.search', ['query' => 'ING'])); + + $response->assertSuccessful(); + + $banks = $response->json('banks'); + + expect($banks)->toHaveCount(5); + expect($banks[0]['name'])->toBe('ING'); + expect($banks[1]['name'])->toBe('ING Bank'); + expect($banks[2]['name'])->toBe('ING Direct'); + expect($banks[3]['name'])->toBe('Alpha ING Bank'); + expect($banks[4]['name'])->toBe('Zebra ING Bank'); +}); + +it('is case insensitive', function () { + Bank::factory()->create(['name' => 'ING']); + Bank::factory()->create(['name' => 'ing direct']); + Bank::factory()->create(['name' => 'Test ing Bank']); + + $response = $this->getJson(route('banks.search', ['query' => 'ing'])); + + $response->assertSuccessful(); + + $banks = $response->json('banks'); + + expect($banks)->toHaveCount(3); + expect($banks[0]['name'])->toBe('ING'); +}); + +it('includes both user banks and global banks', function () { + Bank::factory()->create(['name' => 'User Bank', 'user_id' => $this->user->id]); + Bank::factory()->create(['name' => 'Global Bank', 'user_id' => null]); + Bank::factory()->create(['name' => 'Other User Bank', 'user_id' => User::factory()->create()->id]); + + $response = $this->getJson(route('banks.search', ['query' => 'Bank'])); + + $response->assertSuccessful(); + + $banks = $response->json('banks'); + $bankNames = array_column($banks, 'name'); + + expect($banks)->toHaveCount(2); + expect($bankNames)->toContain('User Bank'); + expect($bankNames)->toContain('Global Bank'); +});