From 80a8a9f93b1b425cc67c7929b803cf4a90df5b55 Mon Sep 17 00:00:00 2001 From: Ovidiu U Date: Fri, 3 Apr 2026 18:49:34 +0100 Subject: [PATCH] feat: add StationTaggingService with supermarket detection --- app/Services/StationTaggingService.php | 32 +++++++++ database/factories/StationFactory.php | 14 ++-- .../Services/StationTaggingServiceTest.php | 68 +++++++++++++++++++ 3 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 app/Services/StationTaggingService.php create mode 100644 tests/Unit/Services/StationTaggingServiceTest.php diff --git a/app/Services/StationTaggingService.php b/app/Services/StationTaggingService.php new file mode 100644 index 0000000..0bed126 --- /dev/null +++ b/app/Services/StationTaggingService.php @@ -0,0 +1,32 @@ + brand keyword → normalised brand name */ + private const SUPERMARKET_BRANDS = [ + 'tesco' => 'Tesco', + 'asda' => 'Asda', + 'morrisons' => 'Morrisons', + 'sainsbury' => 'Sainsbury\'s', + 'aldi' => 'Aldi', + 'lidl' => 'Lidl', + 'costco' => 'Costco', + ]; + + public function tag(Station $station): void + { + $name = strtolower($station->trading_name); + + foreach (self::SUPERMARKET_BRANDS as $keyword => $normalisedBrand) { + if (str_contains($name, $keyword)) { + $station->is_supermarket = true; + $station->brand_name = $normalisedBrand; + return; + } + } + } +} diff --git a/database/factories/StationFactory.php b/database/factories/StationFactory.php index 9b1f070..e0395ca 100644 --- a/database/factories/StationFactory.php +++ b/database/factories/StationFactory.php @@ -10,10 +10,10 @@ class StationFactory extends Factory { public function definition(): array { - $trading = $this->faker->company(); + $trading = 'Station ' . str()->random(8); return [ - 'node_id' => hash('sha256', $this->faker->unique()->uuid()), + 'node_id' => (string) str()->ulid(), 'trading_name' => $trading, 'brand_name' => $trading, 'is_same_trading_and_brand' => true, @@ -24,14 +24,14 @@ class StationFactory extends Factory 'permanent_closure' => false, 'permanent_closure_date' => null, 'public_phone_number' => null, - 'address_line_1' => $this->faker->streetAddress(), + 'address_line_1' => 'Address ' . str()->random(6), 'address_line_2' => null, - 'city' => $this->faker->city(), + 'city' => 'City ' . str()->random(6), 'county' => null, 'country' => 'England', - 'postcode' => strtoupper($this->faker->postcode()), - 'lat' => $this->faker->latitude(49.9, 60.9), - 'lng' => $this->faker->longitude(-8.2, 1.8), + 'postcode' => strtoupper(str()->random(6)), + 'lat' => random_int(499, 609) / 10.0, + 'lng' => random_int(-82, 18) / 10.0, 'amenities' => [], 'opening_times' => null, 'fuel_types' => ['E10', 'E5'], diff --git a/tests/Unit/Services/StationTaggingServiceTest.php b/tests/Unit/Services/StationTaggingServiceTest.php new file mode 100644 index 0000000..bf42024 --- /dev/null +++ b/tests/Unit/Services/StationTaggingServiceTest.php @@ -0,0 +1,68 @@ +service = new StationTaggingService(); +}); + +it('marks tesco station as supermarket and normalises brand', function (): void { + $station = new Station([ + 'trading_name' => 'TESCO EXTRA', + 'is_supermarket' => false, + ]); + + $this->service->tag($station); + + expect($station->is_supermarket)->toBeTrue() + ->and($station->brand_name)->toBe('Tesco'); +}); + +it('marks asda station as supermarket', function (): void { + $station = new Station([ + 'trading_name' => 'Asda Petrol Station', + 'is_supermarket' => false, + ]); + + $this->service->tag($station); + + expect($station->is_supermarket)->toBeTrue() + ->and($station->brand_name)->toBe('Asda'); +}); + +it('does not mark independent station as supermarket', function (): void { + $station = new Station([ + 'trading_name' => 'Village Garage', + 'is_supermarket' => false, + ]); + + $this->service->tag($station); + + expect($station->is_supermarket)->toBeFalse(); +}); + +it('handles case insensitive matching', function (): void { + $station = new Station([ + 'trading_name' => 'morrisons petrol', + 'is_supermarket' => false, + ]); + + $this->service->tag($station); + + expect($station->is_supermarket)->toBeTrue() + ->and($station->brand_name)->toBe('Morrisons'); +}); + +it('does not overwrite brand_name for non-supermarket stations', function (): void { + $station = new Station([ + 'trading_name' => 'Shell Garage', + 'brand_name' => 'Shell', + 'is_supermarket' => false, + ]); + + $this->service->tag($station); + + expect($station->is_supermarket)->toBeFalse() + ->and($station->brand_name)->toBe('Shell'); +});