107 lines
5.4 KiB
PHP
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);
|
|
}
|
|
}
|