Remove obsolete Livewire fuel search components and consolidate pricing tiers
- Delete unused Livewire Search test and fuel type select Blade component - Move subscription webhook listener from EventServiceProvider to AppServiceProvider - Add FUEL_TYPES global config to app layout for client-side use - Add Billable trait to User model and include email_verified_at in fillable - Implement monthly/annual cadence toggle with pricing display and smart CTA routing on homepage - Update VerifyApiKeyMiddlewareTest to use e10 instead of petrol - Refactor PollFuelPrices to auto-refresh stale stations based on last_seen_at - Add incremental polling with cached timestamp and effective-start-timestamp param to FuelPriceService - Normalize amenities/fuel_types from API objects to flat arrays, skip stations missing required fields - Log response body on API failures in ApiLogger - Default homepage sort to 'reliable' instead of 'price'
This commit is contained in:
@@ -20,7 +20,7 @@ it('returns stations near coordinates filtered by fuel type', function () {
|
||||
'price_pence' => 14500,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10&sort=price')
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=b7_standard&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']],
|
||||
@@ -38,7 +38,7 @@ it('excludes stations with no matching fuel type', function () {
|
||||
'price_pence' => 13800,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=b7_standard&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.count', 0);
|
||||
});
|
||||
@@ -54,7 +54,7 @@ it('excludes temporarily closed stations', function () {
|
||||
'price_pence' => 14200,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=b7_standard&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.count', 0);
|
||||
});
|
||||
@@ -68,7 +68,7 @@ it('excludes stations beyond radius', function () {
|
||||
'price_pence' => 14200,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=b7_standard&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.count', 0);
|
||||
});
|
||||
@@ -82,7 +82,7 @@ it('sorts by price when sort=price', function () {
|
||||
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")
|
||||
$this->getJson("/api/stations?lat={$sLat}&lng={$sLng}&fuel_type=b7_standard&radius=10&sort=price")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.0.price_pence', 13900);
|
||||
});
|
||||
@@ -91,7 +91,7 @@ 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->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=b7_standard&radius=10');
|
||||
|
||||
$this->assertDatabaseHas('searches', [
|
||||
'lat_bucket' => '52.56',
|
||||
@@ -166,7 +166,7 @@ it('includes resolved lat and lng in meta', function () {
|
||||
'price_pence' => 14500,
|
||||
]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=diesel&radius=10')
|
||||
$this->getJson('/api/stations?lat=52.555064&lng=-0.256119&fuel_type=b7_standard&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('meta.lat', 52.555064)
|
||||
->assertJsonPath('meta.lng', -0.256119);
|
||||
|
||||
@@ -5,23 +5,23 @@ use Illuminate\Support\Facades\Hash;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
it('returns user preferences for authenticated user', function (): void {
|
||||
$user = User::factory()->create(['preferred_fuel_type' => 'diesel']);
|
||||
$user = User::factory()->create(['preferred_fuel_type' => 'b7_standard']);
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$this->getJson('/api/user/preferences')
|
||||
->assertOk()
|
||||
->assertJsonFragment(['preferred_fuel_type' => 'diesel']);
|
||||
->assertJsonFragment(['preferred_fuel_type' => 'b7_standard']);
|
||||
});
|
||||
|
||||
it('updates user preferences', function (): void {
|
||||
$user = User::factory()->create(['preferred_fuel_type' => 'petrol']);
|
||||
$user = User::factory()->create(['preferred_fuel_type' => 'e10']);
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$this->putJson('/api/user/preferences', ['preferred_fuel_type' => 'diesel'])
|
||||
$this->putJson('/api/user/preferences', ['preferred_fuel_type' => 'b7_standard'])
|
||||
->assertOk()
|
||||
->assertJsonFragment(['preferred_fuel_type' => 'diesel']);
|
||||
->assertJsonFragment(['preferred_fuel_type' => 'b7_standard']);
|
||||
|
||||
expect($user->fresh()->preferred_fuel_type)->toBe('diesel');
|
||||
expect($user->fresh()->preferred_fuel_type)->toBe('b7_standard');
|
||||
});
|
||||
|
||||
it('rejects invalid fuel type in preferences update', function (): void {
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Public\Fuel\Search;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('renders the search component', function () {
|
||||
Livewire::test(Search::class)
|
||||
->assertStatus(200)
|
||||
->assertSeeHtml('name="search"');
|
||||
});
|
||||
|
||||
it('has default property values', function () {
|
||||
Livewire::test(Search::class)
|
||||
->assertSet('search', '')
|
||||
->assertSet('fuelType', 'petrol')
|
||||
->assertSet('radius', 5)
|
||||
->assertSet('sort', 'reliable')
|
||||
->assertSet('apiError', null)
|
||||
->assertSet('hasSearched', false);
|
||||
});
|
||||
|
||||
it('validates search is required', function () {
|
||||
Livewire::test(Search::class)
|
||||
->call('findStations')
|
||||
->assertHasErrors(['search' => 'required']);
|
||||
});
|
||||
|
||||
it('validates fuelType is required', function () {
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', '')
|
||||
->call('findStations')
|
||||
->assertHasErrors(['fuelType' => 'required']);
|
||||
});
|
||||
|
||||
it('dispatches stations-found with results, meta, prediction and radius on successful search', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response([
|
||||
'data' => [
|
||||
[
|
||||
'station_id' => 'abc123',
|
||||
'name' => 'BP Garage',
|
||||
'brand' => 'BP',
|
||||
'is_supermarket' => false,
|
||||
'address' => '1 High Street',
|
||||
'postcode' => 'SW1A 1AA',
|
||||
'lat' => 51.5074,
|
||||
'lng' => -0.1278,
|
||||
'distance_km' => 1.5,
|
||||
'fuel_type' => 'e10',
|
||||
'price_pence' => 14390,
|
||||
'price' => 143.9,
|
||||
'price_updated_at' => '2026-04-05T08:00:00.000Z',
|
||||
'price_classification' => 'current',
|
||||
'price_classification_label' => 'Current',
|
||||
],
|
||||
],
|
||||
'meta' => ['count' => 1, 'lowest_pence' => 14390, 'avg_pence' => 14390.0],
|
||||
], 200),
|
||||
'*/api/prediction*' => Http::response([
|
||||
'action' => 'fill_now',
|
||||
'confidence_score' => 80.0,
|
||||
'confidence_label' => 'high',
|
||||
'reasoning' => 'Prices rising.',
|
||||
'predicted_direction' => 'up',
|
||||
'predicted_change_pence' => 3.5,
|
||||
], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('hasSearched', true)
|
||||
->assertSet('apiError', null)
|
||||
->assertDispatched('stations-found', fn ($event, $params) =>
|
||||
count($params['results']) === 1
|
||||
&& $params['results'][0]['name'] === 'BP Garage'
|
||||
&& $params['meta']['count'] === 1
|
||||
&& $params['prediction']['action'] === 'fill_now'
|
||||
&& $params['radius'] === 5
|
||||
);
|
||||
});
|
||||
|
||||
it('sets apiError from 422 station response and does not dispatch stations-found', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response([
|
||||
'errors' => ['postcode' => ['Postcode not found.']],
|
||||
], 422),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'ZZ99 9ZZ')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('hasSearched', false)
|
||||
->assertSet('apiError', 'Postcode not found.')
|
||||
->assertNotDispatched('stations-found');
|
||||
});
|
||||
|
||||
it('sets generic apiError on server error', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response([], 500),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('apiError', 'Unable to fetch stations. Please try again.');
|
||||
});
|
||||
|
||||
it('converts radius from miles to km in the outgoing stations request', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->set('radius', 5)
|
||||
->call('findStations');
|
||||
|
||||
Http::assertSent(function ($request) {
|
||||
if (! str_contains($request->url(), 'api/stations')) {
|
||||
return false;
|
||||
}
|
||||
$data = $request->data();
|
||||
return isset($data['radius']) && abs((float) $data['radius'] - 8.05) < 0.01;
|
||||
});
|
||||
});
|
||||
|
||||
it('resets apiError before each new search', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->set('apiError', 'Old error')
|
||||
->call('findStations')
|
||||
->assertSet('apiError', null);
|
||||
});
|
||||
|
||||
it('does not call findStations on updatedFuelType if not yet searched', function () {
|
||||
Http::fake();
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('fuelType', 'diesel');
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('re-runs findStations on updatedFuelType when already searched', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('hasSearched', true)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'diesel');
|
||||
|
||||
Http::assertSentCount(2);
|
||||
});
|
||||
|
||||
it('re-runs findStations on updatedRadius when already searched', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('hasSearched', true)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('radius', 10);
|
||||
|
||||
Http::assertSentCount(2);
|
||||
});
|
||||
|
||||
it('re-runs findStations on updatedSort when already searched', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('hasSearched', true)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('sort', 'price');
|
||||
|
||||
Http::assertSentCount(2);
|
||||
});
|
||||
|
||||
it('prediction is null in stations-found payload when prediction api fails', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response([], 500),
|
||||
]);
|
||||
|
||||
Livewire::test(Search::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('hasSearched', true)
|
||||
->assertDispatched('stations-found', fn ($event, $params) =>
|
||||
$params['prediction'] === null
|
||||
);
|
||||
});
|
||||
@@ -4,7 +4,7 @@ use App\Models\User;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
it('rejects requests without api key or sanctum session', function (): void {
|
||||
$response = $this->getJson('/api/stations?postcode=SW1A1AA&fuel_type=petrol');
|
||||
$response = $this->getJson('/api/stations?postcode=SW1A1AA&fuel_type=e10');
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
@@ -13,7 +13,7 @@ it('accepts requests with valid api key', function (): void {
|
||||
config(['app.api_secret_key' => 'test-secret']);
|
||||
|
||||
$response = $this->withHeader('X-Api-Key', 'test-secret')
|
||||
->getJson('/api/stations?postcode=SW1A1AA&fuel_type=petrol');
|
||||
->getJson('/api/stations?postcode=SW1A1AA&fuel_type=e10');
|
||||
|
||||
// 403 would mean middleware rejected — any other status means it passed through
|
||||
expect($response->status())->not->toBe(403);
|
||||
@@ -23,7 +23,7 @@ it('accepts requests from sanctum authenticated users', function (): void {
|
||||
$user = User::factory()->create();
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->getJson('/api/stations?postcode=SW1A1AA&fuel_type=petrol');
|
||||
$response = $this->getJson('/api/stations?postcode=SW1A1AA&fuel_type=e10');
|
||||
|
||||
expect($response->status())->not->toBe(403);
|
||||
});
|
||||
|
||||
@@ -40,6 +40,16 @@ it('logs a failed request and re-throws the exception', function (): void {
|
||||
->and($log->error)->toBe('connection refused');
|
||||
});
|
||||
|
||||
it('captures response body as error when status is 4xx/5xx', function (): void {
|
||||
Http::fake(['https://example.com/missing' => Http::response('Not Found', 404)]);
|
||||
|
||||
$this->apiLogger->send('test_service', 'GET', 'https://example.com/missing', fn () => Http::get('https://example.com/missing'));
|
||||
|
||||
$log = ApiLog::first();
|
||||
expect($log->status_code)->toBe(404)
|
||||
->and($log->error)->toBe('Not Found');
|
||||
});
|
||||
|
||||
it('logs a POST request with correct method', function (): void {
|
||||
Http::fake(['https://example.com/token' => Http::response(['token' => 'abc'], 201)]);
|
||||
|
||||
|
||||
@@ -2,27 +2,22 @@
|
||||
|
||||
use App\Enums\FuelType;
|
||||
|
||||
it('resolves diesel alias to B7Standard', function () {
|
||||
expect(FuelType::fromAlias('diesel'))->toBe(FuelType::B7Standard);
|
||||
it('maps UK API uppercase values to the canonical lowercase enum', function () {
|
||||
expect(FuelType::fromApiValue('E10'))->toBe(FuelType::E10)
|
||||
->and(FuelType::fromApiValue('B7_STANDARD'))->toBe(FuelType::B7Standard)
|
||||
->and(FuelType::fromApiValue('HVO'))->toBe(FuelType::Hvo);
|
||||
});
|
||||
|
||||
it('resolves petrol alias to E10', function () {
|
||||
expect(FuelType::fromAlias('petrol'))->toBe(FuelType::E10);
|
||||
it('accepts already-lowercase values', function () {
|
||||
expect(FuelType::fromApiValue('e5'))->toBe(FuelType::E5);
|
||||
});
|
||||
|
||||
it('resolves unleaded alias to E10', function () {
|
||||
expect(FuelType::fromAlias('unleaded'))->toBe(FuelType::E10);
|
||||
it('exposes a human label for each case', function () {
|
||||
expect(FuelType::E10->label())->toBe('Petrol (E10)')
|
||||
->and(FuelType::B7Standard->label())->toBe('Diesel (B7)')
|
||||
->and(FuelType::Hvo->label())->toBe('HVO');
|
||||
});
|
||||
|
||||
it('resolves premium_unleaded alias to E5', function () {
|
||||
expect(FuelType::fromAlias('premium_unleaded'))->toBe(FuelType::E5);
|
||||
});
|
||||
|
||||
it('accepts canonical enum values as aliases', function () {
|
||||
expect(FuelType::fromAlias('e10'))->toBe(FuelType::E10);
|
||||
expect(FuelType::fromAlias('b7_standard'))->toBe(FuelType::B7Standard);
|
||||
});
|
||||
|
||||
it('throws ValueError for unknown alias', function () {
|
||||
FuelType::fromAlias('avgas');
|
||||
it('throws ValueError for unknown fuel types', function () {
|
||||
FuelType::fromApiValue('avgas');
|
||||
})->throws(ValueError::class);
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\StationPriceCurrent;
|
||||
use App\Services\ApiLogger;
|
||||
use App\Services\FuelPriceService;
|
||||
use App\Services\StationTaggingService;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
@@ -310,3 +311,110 @@ it('stops pagination when an empty batch is returned', function (): void {
|
||||
|
||||
Http::assertSentCount(1);
|
||||
});
|
||||
|
||||
it('caches the poll timestamp and sends it on subsequent polls', function (): void {
|
||||
Cache::put('fuel_finder_access_token', 'tok', 3540);
|
||||
Cache::forget('fuel_finder_last_price_poll_at');
|
||||
|
||||
Http::fake([
|
||||
'*/pfs/fuel-prices*' => Http::response([]),
|
||||
]);
|
||||
|
||||
$this->service->pollPrices();
|
||||
|
||||
expect(Cache::get('fuel_finder_last_price_poll_at'))->toBeInstanceOf(CarbonInterface::class);
|
||||
|
||||
$this->service->pollPrices();
|
||||
|
||||
Http::assertSent(fn ($request) => str_contains($request->url(), 'effective-start-timestamp='));
|
||||
});
|
||||
|
||||
it('does not cache the poll timestamp when a batch errors', function (): void {
|
||||
Cache::put('fuel_finder_access_token', 'tok', 3540);
|
||||
Cache::forget('fuel_finder_last_price_poll_at');
|
||||
|
||||
Http::fake([
|
||||
'*/pfs/fuel-prices*' => Http::response([], 500),
|
||||
]);
|
||||
|
||||
$this->service->pollPrices();
|
||||
|
||||
expect(Cache::has('fuel_finder_last_price_poll_at'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('skips price rows for stations not present in the stations table', function (): void {
|
||||
Cache::put('fuel_finder_access_token', 'tok', 3540);
|
||||
|
||||
Http::fake([
|
||||
'*/pfs/fuel-prices*' => Http::sequence()
|
||||
->push([[
|
||||
'node_id' => 'unknown-station',
|
||||
'fuel_prices' => [[
|
||||
'fuel_type' => 'E10',
|
||||
'price' => 142.9,
|
||||
'price_last_updated' => '2026-04-04T10:00:00.000Z',
|
||||
'price_change_effective_timestamp' => '2026-04-04T10:00:00.000Z',
|
||||
]],
|
||||
]])
|
||||
->push([]),
|
||||
]);
|
||||
|
||||
$inserted = $this->service->pollPrices();
|
||||
|
||||
expect($inserted)->toBe(0)
|
||||
->and(StationPrice::count())->toBe(0)
|
||||
->and(StationPriceCurrent::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('normalises amenities and fuel_types object payloads to flat arrays', function (): void {
|
||||
$apiStations = [[
|
||||
'node_id' => 'abc999',
|
||||
'trading_name' => 'Shell Somewhere',
|
||||
'brand_name' => 'Shell',
|
||||
'is_same_trading_and_brand_name' => false,
|
||||
'is_motorway_service_station' => false,
|
||||
'is_supermarket_service_station' => false,
|
||||
'temporary_closure' => false,
|
||||
'permanent_closure' => false,
|
||||
'location' => [
|
||||
'postcode' => 'AB1 2CD',
|
||||
'latitude' => 52.1,
|
||||
'longitude' => -1.2,
|
||||
],
|
||||
'amenities' => [
|
||||
'adblue_pumps' => true,
|
||||
'car_wash' => false,
|
||||
'customer_toilets' => true,
|
||||
],
|
||||
'fuel_types' => [
|
||||
'E10' => true,
|
||||
'E5' => true,
|
||||
'B7_Standard' => true,
|
||||
'B7_Premium' => false,
|
||||
'B10' => false,
|
||||
'HVO' => false,
|
||||
],
|
||||
]];
|
||||
|
||||
$this->service->upsertStations($apiStations);
|
||||
|
||||
$station = Station::find('abc999');
|
||||
expect($station->amenities)->toBe(['adblue_pumps', 'customer_toilets'])
|
||||
->and($station->fuel_types)->toBe(['E10', 'E5', 'B7_Standard']);
|
||||
});
|
||||
|
||||
it('skips stations missing required fields', function (): void {
|
||||
$apiStations = [
|
||||
['node_id' => 'missing-loc', 'trading_name' => 'Bad Data'],
|
||||
[
|
||||
'node_id' => 'good',
|
||||
'trading_name' => 'Good Station',
|
||||
'location' => ['postcode' => 'AB1 2CD', 'latitude' => 52.0, 'longitude' => -1.0],
|
||||
],
|
||||
];
|
||||
|
||||
$this->service->upsertStations($apiStations);
|
||||
|
||||
expect(Station::find('missing-loc'))->toBeNull()
|
||||
->and(Station::find('good'))->not->toBeNull();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user