feat(admin): add Filament resources for the forecasting stack
Adds three resources under a new "Forecasting" navigation group: a full-CRUD WatchedEventResource for the Layer 5 volatility detector, plus read-only WeeklyForecastResource and BacktestResource so the ridge model output and its calibration can be inspected without SQL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
59
tests/Feature/Admin/BacktestResourceTest.php
Normal file
59
tests/Feature/Admin/BacktestResourceTest.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Resources\Backtests\BacktestResource;
|
||||
use App\Filament\Resources\Backtests\Pages\ListBacktests;
|
||||
use App\Filament\Resources\Backtests\Pages\ViewBacktest;
|
||||
use App\Models\Backtest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->admin = User::factory()->admin()->create();
|
||||
$this->actingAs($this->admin);
|
||||
});
|
||||
|
||||
it('lists backtests', function () {
|
||||
$backtests = Backtest::factory()->count(3)->create();
|
||||
|
||||
Livewire::test(ListBacktests::class)
|
||||
->assertOk()
|
||||
->assertCanSeeTableRecords($backtests);
|
||||
});
|
||||
|
||||
it('shows the calibration table on the view page', function () {
|
||||
$backtest = Backtest::factory()->create([
|
||||
'calibration_table' => [
|
||||
'0.0-0.5' => 0.55,
|
||||
'0.5-1.0' => 0.65,
|
||||
'1.0+' => 0.72,
|
||||
],
|
||||
]);
|
||||
|
||||
Livewire::test(ViewBacktest::class, ['record' => $backtest->id])
|
||||
->assertOk()
|
||||
->assertSee('0.0-0.5')
|
||||
->assertSee('55%')
|
||||
->assertSee('1.0+')
|
||||
->assertSee('72%');
|
||||
});
|
||||
|
||||
it('disables create and edit', function () {
|
||||
$backtest = Backtest::factory()->create();
|
||||
|
||||
expect(BacktestResource::canCreate())->toBeFalse()
|
||||
->and(BacktestResource::canEdit($backtest))->toBeFalse()
|
||||
->and(BacktestResource::canDelete($backtest))->toBeFalse();
|
||||
});
|
||||
|
||||
it('filters backtests below the ship gate', function () {
|
||||
$shippable = Backtest::factory()->create(['directional_accuracy' => 65.00]);
|
||||
$marginal = Backtest::factory()->create(['directional_accuracy' => 58.00]);
|
||||
|
||||
Livewire::test(ListBacktests::class)
|
||||
->filterTable('below_ship_gate')
|
||||
->assertCanSeeTableRecords([$marginal])
|
||||
->assertCanNotSeeTableRecords([$shippable]);
|
||||
});
|
||||
58
tests/Feature/Admin/WatchedEventResourceTest.php
Normal file
58
tests/Feature/Admin/WatchedEventResourceTest.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Resources\WatchedEvents\Pages\CreateWatchedEvent;
|
||||
use App\Filament\Resources\WatchedEvents\Pages\ListWatchedEvents;
|
||||
use App\Models\User;
|
||||
use App\Models\WatchedEvent;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->admin = User::factory()->admin()->create();
|
||||
$this->actingAs($this->admin);
|
||||
});
|
||||
|
||||
it('lists watched events', function () {
|
||||
$events = WatchedEvent::factory()->count(3)->create();
|
||||
|
||||
Livewire::test(ListWatchedEvents::class)
|
||||
->assertOk()
|
||||
->assertCanSeeTableRecords($events);
|
||||
});
|
||||
|
||||
it('creates a watched event from the form', function () {
|
||||
Livewire::test(CreateWatchedEvent::class)
|
||||
->fillForm([
|
||||
'label' => 'Iran tensions Apr–May 2026',
|
||||
'starts_at' => '2026-04-01 00:00:00',
|
||||
'ends_at' => '2026-05-31 23:59:00',
|
||||
'notes' => 'Geopolitical event affecting Brent crude.',
|
||||
])
|
||||
->call('create')
|
||||
->assertHasNoFormErrors();
|
||||
|
||||
expect(WatchedEvent::where('label', 'Iran tensions Apr–May 2026')->exists())->toBeTrue();
|
||||
});
|
||||
|
||||
it('validates ends_at is after starts_at', function () {
|
||||
Livewire::test(CreateWatchedEvent::class)
|
||||
->fillForm([
|
||||
'label' => 'Bad dates',
|
||||
'starts_at' => '2026-05-01 00:00:00',
|
||||
'ends_at' => '2026-04-01 00:00:00',
|
||||
])
|
||||
->call('create')
|
||||
->assertHasFormErrors(['ends_at']);
|
||||
});
|
||||
|
||||
it('filters to currently active events', function () {
|
||||
$active = WatchedEvent::factory()->active()->create(['label' => 'Active event']);
|
||||
$inactive = WatchedEvent::factory()->inactive()->create(['label' => 'Inactive event']);
|
||||
|
||||
Livewire::test(ListWatchedEvents::class)
|
||||
->filterTable('currently_active')
|
||||
->assertCanSeeTableRecords([$active])
|
||||
->assertCanNotSeeTableRecords([$inactive]);
|
||||
});
|
||||
43
tests/Feature/Admin/WeeklyForecastResourceTest.php
Normal file
43
tests/Feature/Admin/WeeklyForecastResourceTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Resources\WeeklyForecasts\Pages\ListWeeklyForecasts;
|
||||
use App\Filament\Resources\WeeklyForecasts\Pages\ViewWeeklyForecast;
|
||||
use App\Filament\Resources\WeeklyForecasts\WeeklyForecastResource;
|
||||
use App\Models\User;
|
||||
use App\Models\WeeklyForecast;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->admin = User::factory()->admin()->create();
|
||||
$this->actingAs($this->admin);
|
||||
});
|
||||
|
||||
it('lists weekly forecasts sorted by forecast_for desc', function () {
|
||||
$older = WeeklyForecast::factory()->create(['forecast_for' => '2026-04-06']);
|
||||
$newer = WeeklyForecast::factory()->create(['forecast_for' => '2026-05-04']);
|
||||
|
||||
Livewire::test(ListWeeklyForecasts::class)
|
||||
->assertOk()
|
||||
->assertCanSeeTableRecords([$newer, $older], inOrder: true);
|
||||
});
|
||||
|
||||
it('disables create and edit', function () {
|
||||
$forecast = WeeklyForecast::factory()->create();
|
||||
|
||||
expect(WeeklyForecastResource::canCreate())->toBeFalse();
|
||||
expect(WeeklyForecastResource::canEdit($forecast))->toBeFalse();
|
||||
expect(WeeklyForecastResource::canDelete($forecast))->toBeFalse();
|
||||
});
|
||||
|
||||
it('renders the view page for a forecast', function () {
|
||||
$forecast = WeeklyForecast::factory()->create([
|
||||
'reasoning' => 'Brent stabilising; supermarket cycle entering bottom.',
|
||||
]);
|
||||
|
||||
Livewire::test(ViewWeeklyForecast::class, ['record' => $forecast->id])
|
||||
->assertOk()
|
||||
->assertSee('Brent stabilising; supermarket cycle entering bottom.');
|
||||
});
|
||||
Reference in New Issue
Block a user