Remove prediction API endpoint and integrate into stations search
Consolidate prediction functionality by merging /api/prediction endpoint into /api/stations response. Move prediction logic from PredictionController into StationController, returning prediction data alongside station results. Replace usePrediction composable with unified useStations that returns {stations, meta, prediction}. Remove PredictionRequest, related tests, and unused Vue components (FuelFinderTest, MapTest, RecommendationTest, StationListTest). Add PredictionFull component and UpsellBanner. Extend NationalFuelPredictionService to include weekly_summary (7-day series, yesterday/today averages, cheapest/priciest days) and oil signal from price_predictions table. Update Home.vue to consume prediction from stations response. Add Plan::resolveCadenceForUser helper and configure Cashier to use custom Subscription model.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\FuelType;
|
||||
use App\Filament\Resources\UserResource;
|
||||
use App\Models\Station;
|
||||
use App\Models\StationPriceCurrent;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
@@ -12,6 +14,15 @@ beforeEach(function () {
|
||||
$this->withHeaders(['X-Api-Key' => config('app.api_secret_key')]);
|
||||
});
|
||||
|
||||
function asPaidUserOnStations(string $tier = 'plus'): User
|
||||
{
|
||||
test()->artisan('db:seed', ['--class' => 'PlanSeeder']);
|
||||
$user = User::factory()->create();
|
||||
UserResource::applyTier($user, $tier, 'monthly');
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
it('returns stations near coordinates filtered by fuel type', function () {
|
||||
$station = Station::factory()->create(['lat' => 52.555064, 'lng' => -0.256119]);
|
||||
StationPriceCurrent::factory()->create([
|
||||
@@ -192,3 +203,37 @@ it('includes resolved lat and lng in meta when postcode is provided', function (
|
||||
->assertJsonPath('meta.lat', 51.5010)
|
||||
->assertJsonPath('meta.lng', -0.1415);
|
||||
});
|
||||
|
||||
it('embeds a tier-locked prediction teaser for guest requests', function () {
|
||||
Station::factory()->create(['lat' => 52.555, 'lng' => -0.256]);
|
||||
|
||||
$this->getJson('/api/stations?lat=52.555&lng=-0.256&fuel_type=e10&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('prediction.tier_locked', true)
|
||||
->assertJsonStructure(['prediction' => ['fuel_type', 'predicted_direction', 'tier_locked']])
|
||||
->assertJsonMissingPath('prediction.signals')
|
||||
->assertJsonMissingPath('prediction.weekly_summary');
|
||||
});
|
||||
|
||||
it('embeds a tier-locked teaser for free-tier authenticated users', function () {
|
||||
asPaidUserOnStations('free');
|
||||
$user = User::query()->latest('id')->first();
|
||||
Station::factory()->create(['lat' => 52.555, 'lng' => -0.256]);
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->getJson('/api/stations?lat=52.555&lng=-0.256&fuel_type=e10&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonPath('prediction.tier_locked', true)
|
||||
->assertJsonMissingPath('prediction.signals');
|
||||
});
|
||||
|
||||
it('embeds the full prediction payload for plus users', function () {
|
||||
$user = asPaidUserOnStations('plus');
|
||||
Station::factory()->create(['lat' => 52.555, 'lng' => -0.256]);
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->getJson('/api/stations?lat=52.555&lng=-0.256&fuel_type=e10&radius=10')
|
||||
->assertOk()
|
||||
->assertJsonStructure(['prediction' => ['fuel_type', 'predicted_direction', 'confidence_score', 'reasoning', 'weekly_summary', 'signals']])
|
||||
->assertJsonMissingPath('prediction.tier_locked');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user