Files
compras/tests/Feature/AiPreparationTest.php
T
nala c6373365bf
tests / ci (push) Canceled after 0s
commit9
2026-10-30 20:02:30 +02:00

57 lines
3.5 KiB
PHP

<?php
use App\Enums\PurchasingRole;
use App\Models\User;
use App\Services\AiPreparationPlan;
use Illuminate\Support\Facades\Http;
use Inertia\Testing\AssertableInertia as Assert;
require_once __DIR__.'/../Support/ReceiptFixture.php';
require_once __DIR__.'/../Support/AiPlanFixture.php';
beforeEach(function () {
$this->f = receiptFixture();
$this->actingAs($this->f['issuer']);
Http::fake();
});
function prepareAiPlan(object $test, array $changes = []): void
{
$plan = app(AiPreparationPlan::class)->checked([...aiPlanInput(), ...$changes]);
$test->f['tenant']->update(['intelligence_policy' => ['revision' => 1, 'configured' => true, 'procurement_brief' => false, 'people_brief' => true, 'ai_plan' => $plan]]);
}
test('AI preparation filters facts by scope preserves the report and exposes no company budget or source hashes', function () {
prepareAiPlan($this, ['data_scopes' => ['process_state']]);
$this->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertOk()->assertInertia(fn (Assert $page) => $page
->where('brief', null)->where('report.requisition.id', $this->f['r']->id)->where('aiPreparation.status', 'draft')
->where('aiPreparation.external_execution_enabled', false)->where('aiPreparation.usage_checked', false)
->has('aiPreparation.facts', 1)->where('aiPreparation.facts.0.id', 'request')
->missing('aiPreparation.monthly_budget_usd')->missing('aiPreparation.facts.0.basis_sha256'));
Http::assertNotSent(fn ($request) => ! in_array(parse_url($request->url(), PHP_URL_HOST), ['127.0.0.1', 'localhost'], true));
});
test('AI preparation respects financial access even when finance is allowed in the organisation plan', function () {
prepareAiPlan($this, ['data_scopes' => ['financial_counts']]);
$this->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertInertia(fn (Assert $page) => $page->where('aiPreparation.status', 'no_permitted_facts')->where('aiPreparation.facts', []));
$finance = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'purchasing_role' => PurchasingRole::Finance]);
$this->actingAs($finance)->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertInertia(fn (Assert $page) => $page->where('aiPreparation.facts.0.id', 'finance')->has('aiPreparation.facts', 1));
Http::assertNotSent(fn ($request) => ! in_array(parse_url($request->url(), PHP_URL_HOST), ['127.0.0.1', 'localhost'], true));
});
test('AI preparation rejects an excluded domain and never truncates a payload to make it fit', function () {
prepareAiPlan($this, ['domains' => ['people']]);
$this->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertInertia(fn (Assert $page) => $page->where('aiPreparation.status', 'outside_scope')->where('aiPreparation.facts', []));
prepareAiPlan($this, ['data_scopes' => array_keys(AiPreparationPlan::SCOPES), 'max_input_bytes' => 512]);
$this->get('/requisicoes/'.$this->f['r']->id.'/relatorio')->assertInertia(fn (Assert $page) => $page->where('aiPreparation.status', 'input_exceeds_limit')->where('aiPreparation.facts', [])
->where('aiPreparation.input_bytes', fn ($bytes) => $bytes > 512));
Http::assertNotSent(fn ($request) => ! in_array(parse_url($request->url(), PHP_URL_HOST), ['127.0.0.1', 'localhost'], true));
});
test('AI preparation cannot disclose another company report or query an external provider', function () {
$other = receiptFixture();
prepareAiPlan($this);
$this->actingAs($this->f['issuer'])->get('/requisicoes/'.$other['r']->id.'/relatorio')->assertNotFound();
Http::assertNothingSent();
});