Files
Investidor-V2/tests/Feature/TerritoryGeometryConsumerAccessTest.php
T

119 lines
7.6 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Services\ApprovedAdministrativeBoundaries;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
final class TerritoryGeometryConsumerAccessTest extends TestCase
{
use DatabaseTransactions;
private User $actor;
private string $territory;
protected function setUp(): void
{
parent::setUp();
$identity = DB::selectOne('select current_database() as name,inet_server_port() as port');
$this->assertSame('simulator_fiscal_delta_20261021_test', $identity->name);
$this->assertSame(55441, (int) $identity->port);
$this->withoutVite();
$this->withoutMiddleware(ThrottleRequests::class);
config(['portal.profile_access_enabled' => true, 'portal.managers' => [], 'investor-path.enabled' => true, 'suitecrm.enabled' => false, 'investmoz_ai.enabled' => false]);
$this->actor = User::factory()->create(['two_factor_secret' => Crypt::encryptString('JBSWY3DPEHPK3PXP'), 'two_factor_confirmed_at' => now()]);
$other = User::factory()->create();
DB::table('portal_superuser_pair')->updateOrInsert(['id' => 1], ['first_user_id' => $this->actor->id, 'second_user_id' => $other->id, 'approved_by' => $other->id]);
$this->territory = DB::table('geo_geometries as g')->join('geo_locations as l', 'l.id', '=', 'g.geo_location_id')
->where('g.source_system', 'ine-cod-ab')->where('g.data_classification', 'internal')->where('l.location_type', 'province')->value('l.id');
$this->assertNotEmpty($this->territory);
}
public function test_general_consumer_uses_existing_authenticated_read_access_without_the_legacy_gate(): void
{
$this->assertFalse($this->actor->can('manage-portal'));
$before = $this->fingerprint();
$this->actingAs($this->actor)->getJson('/api/v1/investment/territory-geometries?territory_id='.$this->territory)
->assertOk()->assertJsonPath('selected_geometry_available', true)->assertJsonPath('geometry_scope', 'authorized_internal')
->assertJsonPath('areas.0.territory_id', $this->territory)->assertHeader('Cache-Control', 'no-store, private');
$this->assertSame($before, $this->fingerprint());
}
public function test_existing_administration_reaches_the_same_geometry_with_its_completed_mfa_session(): void
{
$this->actingAs($this->actor)->withSession(['portal.mfa_actor' => $this->actor->id, 'portal.mfa_at' => time(), 'portal.access_revision' => (int) $this->actor->portal_access_revision]);
$this->getJson('/management/territory-geometries?territory_id='.$this->territory)->assertOk()->assertJsonPath('selected_geometry_available', true);
session()->forget(['portal.mfa_actor', 'portal.mfa_at']);
$this->getJson('/management/territory-geometries?territory_id='.$this->territory)->assertForbidden();
}
public function test_anonymous_and_ordinary_accounts_do_not_receive_or_publish_internal_layers(): void
{
$before = $this->fingerprint();
$url = '/api/v1/investment/territory-geometries?territory_id='.$this->territory;
$this->getJson($url)->assertOk()->assertJsonPath('geometry_scope', 'public')->assertJsonPath('selected_geometry_available', false)->assertJsonCount(0, 'areas');
$this->actingAs(User::factory()->create())->getJson($url)->assertOk()->assertJsonPath('geometry_scope', 'public')->assertJsonCount(0, 'areas');
$this->getJson('/management/territory-geometries?territory_id='.$this->territory)->assertForbidden();
$this->assertSame($before, $this->fingerprint());
}
public function test_saved_territory_is_recovered_in_dossier_and_simulator_and_the_map_uses_that_identity(): void
{
$this->actingAs($this->actor);
$id = $this->postJson('/investimentos', ['title' => 'SYNTHETIC QA - mapa recuperado', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
$body = ['version' => $this->getJson('/investimentos/'.$id.'/contexto')->json('case.version'), 'selection' => ['territory' => 'territory:'.$this->territory]];
$preview = $this->postJson('/investimentos/'.$id.'/contexto/previa', $body)->assertOk()->json();
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', [...$body, 'preview_hash' => $preview['preview_hash'], 'operation_key' => (string) Str::uuid(), 'confirm' => true])->assertOk();
$view = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
$this->assertSame($this->territory, $view['context']['selection']['territory']['id']);
$this->assertSame($this->territory, $view['prefill']['simulator']['province_id']);
$this->getJson('/api/v1/investment/territory-geometries?territory_id='.$view['prefill']['simulator']['province_id'])
->assertOk()->assertJsonPath('selected_geometry_available', true)->assertJsonPath('areas.0.selected_territory', true);
$this->assertSame($this->territory, $this->getJson('/investimentos/'.$id.'/contexto')->json('context.selection.territory.id'));
}
public function test_only_the_approved_unchanged_snapshot_is_public_without_reclassifying_the_original(): void
{
$before = $this->fingerprint();
$row = DB::table('geo_geometries as g')->where('geo_location_id', $this->territory)->first([
'g.id', 'g.geo_location_id', DB::raw("encode(sha256(ST_AsEWKB(g.geom)),'hex') as geometry_sha256"),
DB::raw("encode(sha256(convert_to(g.properties::text,'UTF8')),'hex') as properties_sha256"),
]);
// The QA import has its own identities; never copy or replace production rows.
$approved = $this->partialMock(ApprovedAdministrativeBoundaries::class);
$approved->shouldReceive('snapshots')->andReturn([$row->id => [
'territory_id' => $row->geo_location_id, 'geometry_sha256' => $row->geometry_sha256,
'properties_sha256' => $row->properties_sha256,
]]);
$url = '/api/v1/investment/territory-geometries?territory_id='.$this->territory;
$this->getJson($url)->assertOk()->assertJsonPath('selected_geometry_available', true)
->assertJsonPath('areas.0.data_classification', 'public')->assertJsonPath('investment_site', false);
$this->actingAs(User::factory()->create())->getJson($url)->assertOk()->assertJsonPath('selected_geometry_available', true);
$this->assertSame($before, $this->fingerprint());
$this->assertSame('internal', DB::table('geo_geometries')->where('id', $row->id)->value('data_classification'));
DB::table('geo_geometries')->where('id', $row->id)->update(['properties' => DB::raw("properties || '{\"qa_changed\":true}'::jsonb")]);
$this->getJson($url)->assertOk()->assertJsonPath('selected_geometry_available', false)->assertJsonCount(0, 'areas');
$this->actingAs($this->actor)->getJson($url)->assertOk()->assertJsonPath('selected_geometry_available', true);
}
public function test_public_manifest_has_the_exact_approved_scope_and_immutable_hash(): void
{
$items = app(ApprovedAdministrativeBoundaries::class)->snapshots();
$this->assertCount(408, $items);
$this->assertCount(408, array_unique(array_column($items, 'territory_id')));
$this->assertSame(['2019-06-07'], array_values(array_unique(array_column($items, 'source_version'))));
}
private function fingerprint(): string
{
return DB::selectOne("select md5(string_agg(row_to_json(g)::text,E'\\n' order by id)) as hash from geo_geometries g")->hash;
}
}