Files
Investidor-V2/tests/Feature/ClusterValueChainTest.php
T
nala 250ac35fd1
tests / ci (push) Canceled after 0s
first commit
2026-09-28 10:01:09 +02:00

107 lines
5.4 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\EconomicCluster;
use App\Models\InvestmentGap;
use App\Models\User;
use App\Models\ValueChain;
use App\Models\ValueChainLink;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Inertia\Testing\AssertableInertia;
use Tests\TestCase;
class ClusterValueChainTest extends TestCase
{
use RefreshDatabase;
private function manager(): void
{
$user = User::factory()->create();
config(['portal.managers' => [$user->email]]);
$this->actingAs($user);
}
private function cluster(string $code): EconomicCluster
{
return EconomicCluster::create(['code' => $code, 'name' => $code, 'sector' => 'Energia', 'status' => 'planned']);
}
private function chain(EconomicCluster $cluster, string $code): ValueChain
{
return ValueChain::create(['cluster_id' => $cluster->id, 'code' => $code, 'name' => $code, 'stage' => 'input']);
}
public function test_links_are_idempotent_and_reject_self_foreign_and_deleted_stages(): void
{
$this->manager();
$cluster = $this->cluster('C1');
$origin = $this->chain($cluster, 'Input');
$destination = $this->chain($cluster, 'Production');
$foreign = $this->chain($this->cluster('C2'), 'Foreign');
$url = '/management/clusters/'.$cluster->id.'/chain-links';
$data = ['from_value_chain_id' => $origin->id, 'to_value_chain_id' => $destination->id, 'link_type' => 'supplies'];
$this->post($url, $data)->assertSessionHasNoErrors();
$this->post($url, $data)->assertSessionHasNoErrors();
$this->assertDatabaseCount('value_chain_links', 1);
$this->assertSame(1, DB::table('audit_events')->where('entity_type', 'value_chain_links')->count());
$this->post($url, [...$data, 'to_value_chain_id' => $origin->id])->assertSessionHasErrors('to_value_chain_id');
$this->post($url, [...$data, 'to_value_chain_id' => $foreign->id])->assertSessionHasErrors('to_value_chain_id');
$destination->delete();
$this->post($url, $data)->assertSessionHasErrors('to_value_chain_id');
}
public function test_removal_preserves_history_and_cannot_target_another_cluster(): void
{
$this->manager();
$cluster = $this->cluster('C1');
$other = $this->cluster('C2');
$data = ['from_value_chain_id' => $this->chain($cluster, 'A')->id, 'to_value_chain_id' => $this->chain($cluster, 'B')->id, 'link_type' => 'supports'];
$url = '/management/clusters/'.$cluster->id.'/chain-links';
$this->post($url, $data)->assertSessionHasNoErrors();
$link = ValueChainLink::firstOrFail();
$this->delete('/management/clusters/'.$other->id.'/chain-links/'.$link->id)->assertNotFound();
$this->assertNotSoftDeleted($link);
$this->delete($url.'/'.$link->id)->assertSessionHasNoErrors();
$this->assertSoftDeleted($link);
$this->assertSame(2, DB::table('audit_events')->where('entity_id', $link->id)->count());
$this->post($url, $data)->assertSessionHasNoErrors();
$this->assertSame(1, ValueChainLink::count());
$this->assertDatabaseCount('value_chain_links', 2);
}
public function test_gap_requires_details_and_stays_within_cluster(): void
{
$this->manager();
$cluster = $this->cluster('C1');
$other = $this->cluster('C2');
$stage = $this->chain($cluster, 'Logistics');
$foreign = $this->chain($other, 'Foreign');
$data = ['value_chain_id' => $stage->id, 'title' => 'Armazenagem', 'location' => 'Distrito de teste',
'required_capacity' => '500 toneladas', 'rationale' => 'Falta capacidade de armazenamento.', 'status' => 'identified', 'call_to_action' => 'Apresentar proposta de investimento.'];
$url = '/management/clusters/'.$cluster->id.'/gaps';
$this->post($url, [...$data, 'required_capacity' => ''])->assertSessionHasErrors('required_capacity');
$this->post($url, [...$data, 'value_chain_id' => $foreign->id])->assertSessionHasErrors('value_chain_id');
$this->post($url, $data)->assertSessionHasNoErrors();
$gap = InvestmentGap::firstOrFail();
$this->put('/management/clusters/'.$other->id.'/gaps/'.$gap->id, [...$data, 'value_chain_id' => $foreign->id])->assertNotFound();
$this->put($url.'/'.$gap->id, [...$data, 'status' => 'resolved'])->assertSessionHasNoErrors();
$this->assertSame('resolved', $gap->fresh()->status);
$event = DB::table('audit_events')->where('entity_id', $gap->id)->where('action', 'created')->first();
$this->assertStringNotContainsString($data['rationale'], $event->after_data);
$this->get('/management/clusters/'.$cluster->id.'/edit')->assertInertia(fn (AssertableInertia $page) => $page
->component('structures/editor')->has('gaps', 1)->where('gaps.0.status', 'resolved')->has('chainLinks', 0));
}
public function test_anonymous_and_unauthorised_users_cannot_write_network_or_gaps(): void
{
$cluster = $this->cluster('C1');
$this->post('/management/clusters/'.$cluster->id.'/gaps', [])->assertRedirect('/login');
$this->actingAs(User::factory()->create());
$this->post('/management/clusters/'.$cluster->id.'/gaps', [])->assertForbidden();
$this->post('/management/clusters/'.$cluster->id.'/chain-links', [])->assertForbidden();
$this->assertDatabaseCount('investment_gaps', 0);
}
}