feat: add GET /api/stations nearby stations endpoint with haversine query and search logging
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
104
tests/Feature/Api/StationControllerTest.php
Normal file
104
tests/Feature/Api/StationControllerTest.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\FuelType;
|
||||
use App\Models\Station;
|
||||
use App\Models\StationPriceCurrent;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('returns stations near coordinates filtered by fuel type', function () {
|
||||
$station = Station::factory()->create(['lat' => 52.555064, 'lng' => -0.256119]);
|
||||
StationPriceCurrent::factory()->create([
|
||||
'station_id' => $station->node_id,
|
||||
'fuel_type' => FuelType::B7Standard,
|
||||
'price_pence' => 14500,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10&sort=price')
|
||||
->assertOk()
|
||||
->assertJsonStructure([
|
||||
'data' => [['station_id', 'name', 'brand', 'is_supermarket', 'lat', 'lng', 'distance_km', 'fuel_type', 'price_pence', 'price', 'price_updated_at']],
|
||||
'meta' => ['count', 'fuel_type', 'radius_km', 'lowest_pence'],
|
||||
])
|
||||
->assertJsonPath('data.0.price_pence', 14500)
|
||||
->assertJsonPath('meta.fuel_type', 'b7_standard');
|
||||
});
|
||||
|
||||
it('excludes stations with no matching fuel type', function () {
|
||||
$station = Station::factory()->create(['lat' => 52.555064, 'lng' => -0.256119]);
|
||||
StationPriceCurrent::factory()->create([
|
||||
'station_id' => $station->node_id,
|
||||
'fuel_type' => FuelType::E10, // not diesel
|
||||
'price_pence' => 13800,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.count', 0);
|
||||
});
|
||||
|
||||
it('excludes temporarily closed stations', function () {
|
||||
$closed = Station::factory()->create([
|
||||
'lat' => 52.555064, 'lng' => -0.256119,
|
||||
'temporary_closure' => true,
|
||||
]);
|
||||
StationPriceCurrent::factory()->create([
|
||||
'station_id' => $closed->node_id,
|
||||
'fuel_type' => FuelType::B7Standard,
|
||||
'price_pence' => 14200,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.count', 0);
|
||||
});
|
||||
|
||||
it('excludes stations beyond radius', function () {
|
||||
// Station ~100km north
|
||||
$farStation = Station::factory()->create(['lat' => 53.5, 'lng' => -0.256119]);
|
||||
StationPriceCurrent::factory()->create([
|
||||
'station_id' => $farStation->node_id,
|
||||
'fuel_type' => FuelType::B7Standard,
|
||||
'price_pence' => 14200,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.count', 0);
|
||||
});
|
||||
|
||||
it('sorts by price when sort=price', function () {
|
||||
$sLat = 52.555;
|
||||
$sLng = -0.256;
|
||||
$cheap = Station::factory()->create(['lat' => $sLat, 'lng' => $sLng]);
|
||||
$expensive = Station::factory()->create(['lat' => $sLat + 0.001, 'lng' => $sLng]);
|
||||
|
||||
StationPriceCurrent::factory()->create(['station_id' => $cheap->node_id, 'fuel_type' => FuelType::B7Standard, 'price_pence' => 13900]);
|
||||
StationPriceCurrent::factory()->create(['station_id' => $expensive->node_id, 'fuel_type' => FuelType::B7Standard, 'price_pence' => 14500]);
|
||||
|
||||
$this->getJson("/api/stations?lat={$sLat}&lng={$sLng}&fuel_type=diesel&radius=10&sort=price")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.0.price_pence', 13900);
|
||||
});
|
||||
|
||||
it('logs a search record for each request', function () {
|
||||
$station = Station::factory()->create(['lat' => 52.555064, 'lng' => -0.256119]);
|
||||
StationPriceCurrent::factory()->create(['station_id' => $station->node_id, 'fuel_type' => FuelType::B7Standard, 'price_pence' => 14500]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10');
|
||||
|
||||
$this->assertDatabaseHas('searches', [
|
||||
'lat_bucket' => '52.56',
|
||||
'lng_bucket' => '-0.26',
|
||||
'fuel_type' => 'b7_standard',
|
||||
'results_count' => 1,
|
||||
'lowest_pence' => 14500,
|
||||
'highest_pence' => 14500,
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns 422 when required params are missing', function () {
|
||||
$this->getJson('/api/stations?lat=52.5')
|
||||
->assertUnprocessable();
|
||||
});
|
||||
Reference in New Issue
Block a user