feat: add postcode resolution to /api/stations and Filament SearchResource
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled

Extends NearbyStationsRequest to accept `postcode` (full or outcode) as an alternative to lat/lng. PostcodeService resolves it via postcodes.io and falls through to coordinates. Also adds SearchResource to the Filament admin panel for viewing logged search activity with fuel type filter and price/distance stats columns. Includes SQLite GREATEST/LEAST function polyfills in AppServiceProvider for test compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ovidiu U
2026-04-05 19:10:25 +01:00
parent 3ccdc28763
commit 7101ed3550
15 changed files with 392 additions and 45 deletions

View File

@@ -4,6 +4,7 @@ use App\Enums\FuelType;
use App\Models\Station;
use App\Models\StationPriceCurrent;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
uses(RefreshDatabase::class);
@@ -102,3 +103,53 @@ it('returns 422 when required params are missing', function () {
$this->getJson('/api/stations?lat=52.5')
->assertUnprocessable();
});
it('resolves a full postcode to coordinates and returns nearby stations', function () {
$station = Station::factory()->create(['lat' => 51.5010, 'lng' => -0.1415]);
StationPriceCurrent::factory()->create([
'station_id' => $station->node_id,
'fuel_type' => FuelType::E10,
'price_pence' => 14200,
]);
Http::fake([
'api.postcodes.io/postcodes/SW1A1AA' => Http::response([
'status' => 200,
'result' => ['postcode' => 'SW1A 1AA', 'latitude' => 51.5010, 'longitude' => -0.1415],
]),
]);
$this->getJson('/api/stations?postcode=SW1A+1AA&fuel_type=e10&radius=1')
->assertOk()
->assertJsonPath('meta.count', 1);
});
it('resolves an outcode to coordinates', function () {
$station = Station::factory()->create(['lat' => 51.5010, 'lng' => -0.1415]);
StationPriceCurrent::factory()->create([
'station_id' => $station->node_id,
'fuel_type' => FuelType::E10,
'price_pence' => 14200,
]);
Http::fake([
'api.postcodes.io/outcodes/SW1A' => Http::response([
'status' => 200,
'result' => ['outcode' => 'SW1A', 'latitude' => 51.5010, 'longitude' => -0.1415],
]),
]);
$this->getJson('/api/stations?postcode=SW1A&fuel_type=e10&radius=1')
->assertOk()
->assertJsonPath('meta.count', 1);
});
it('returns 422 when postcode cannot be resolved', function () {
Http::fake([
'api.postcodes.io/*' => Http::response(['status' => 404, 'error' => 'Postcode not found'], 404),
]);
$this->getJson('/api/stations?postcode=ZZ99+9ZZ&fuel_type=e10')
->assertUnprocessable()
->assertJsonValidationErrors(['postcode']);
});