106 lines
3.6 KiB
PHP
106 lines
3.6 KiB
PHP
<?php
|
|
|
|
use App\Ai\Agents\ReportSummaryAgent;
|
|
use App\Services\Ai\ReportSummarizer;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Exceptions;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Laravel\Ai\Exceptions\ProviderOverloadedException;
|
|
|
|
function summarize(bool $remember = true): ?string
|
|
{
|
|
return app(ReportSummarizer::class)->summarize('test-report', 'Test report.', ['weeks' => [['week' => '2026-W26']]], $remember);
|
|
}
|
|
|
|
it('feeds the model the previous run figures and when they were captured', function () {
|
|
ReportSummaryAgent::fake(['Los registros bajan.']);
|
|
|
|
Cache::put('report_summary_baseline:test-report', [
|
|
'captured_at' => now()->subWeek()->toIso8601String(),
|
|
'payload' => ['weeks' => [['week' => '2026-W25']]],
|
|
], now()->addDay());
|
|
|
|
expect(summarize())->toBe('Los registros bajan.');
|
|
|
|
ReportSummaryAgent::assertPrompted(function ($prompt): bool {
|
|
$payload = json_decode($prompt->prompt, true);
|
|
|
|
return $payload['previous']['weeks'][0]['week'] === '2026-W25'
|
|
&& $payload['current']['weeks'][0]['week'] === '2026-W26'
|
|
&& str_starts_with($payload['previous_captured_at'], now()->subWeek()->toDateString());
|
|
});
|
|
});
|
|
|
|
it('leaves the current figures as the baseline for the next run', function () {
|
|
ReportSummaryAgent::fake(['Resumen.']);
|
|
|
|
summarize();
|
|
|
|
expect(Cache::get('report_summary_baseline:test-report'))
|
|
->toMatchArray(['payload' => ['weeks' => [['week' => '2026-W26']]]]);
|
|
});
|
|
|
|
it('keeps the stored baseline when the report is re-run the same day', function () {
|
|
ReportSummaryAgent::fake(['Resumen.', 'Resumen.']);
|
|
|
|
Cache::put('report_summary_baseline:test-report', [
|
|
'captured_at' => now()->toIso8601String(),
|
|
'payload' => ['weeks' => [['week' => 'last-week']]],
|
|
], now()->addDay());
|
|
|
|
summarize();
|
|
|
|
// A manual re-run must not overwrite the period the next scheduled run
|
|
// compares against, otherwise the summary reports "no change".
|
|
expect(Cache::get('report_summary_baseline:test-report')['payload'])
|
|
->toBe(['weeks' => [['week' => 'last-week']]]);
|
|
});
|
|
|
|
it('does not touch the baseline on a dry run', function () {
|
|
ReportSummaryAgent::fake(['Resumen.']);
|
|
|
|
summarize(remember: false);
|
|
|
|
expect(Cache::get('report_summary_baseline:test-report'))->toBeNull();
|
|
});
|
|
|
|
it('strips backticks so the summary cannot swallow the table below it', function () {
|
|
ReportSummaryAgent::fake(['Los pagos suben ```mucho```.']);
|
|
|
|
expect(summarize())->toBe('Los pagos suben mucho.');
|
|
});
|
|
|
|
it('trims a runaway answer', function () {
|
|
ReportSummaryAgent::fake([str_repeat('a', 2000)]);
|
|
|
|
expect(mb_strlen((string) summarize()))->toBeLessThanOrEqual(900);
|
|
});
|
|
|
|
it('returns null for an empty answer', function () {
|
|
ReportSummaryAgent::fake([' ']);
|
|
|
|
expect(summarize())->toBeNull();
|
|
});
|
|
|
|
it('returns null and reports the failure when the provider fails', function () {
|
|
Exceptions::fake();
|
|
ReportSummaryAgent::fake(fn () => throw new RuntimeException('provider down'));
|
|
|
|
expect(summarize())->toBeNull();
|
|
|
|
Exceptions::assertReported(fn (RuntimeException $exception): bool => $exception->getMessage() === 'provider down');
|
|
});
|
|
|
|
it('logs a transient provider failure without reporting it', function () {
|
|
Exceptions::fake();
|
|
Log::spy();
|
|
ReportSummaryAgent::fake(fn () => throw ProviderOverloadedException::forProvider('gemini'));
|
|
|
|
expect(summarize())->toBeNull();
|
|
|
|
Exceptions::assertNothingReported();
|
|
Log::shouldHaveReceived('warning')
|
|
->withArgs(fn (string $message): bool => str_contains($message, 'provider transient failure'))
|
|
->once();
|
|
});
|