Files
Investidor-V2/tests/Feature/InvestmentMapIndicatorsTest.php
2026-11-04 11:45:42 +02:00

122 lines
7.0 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\InvestmentZone;
use App\Services\InvestmentGeography;
use App\Services\InvestmentMapIndicators;
use App\Services\InvestmentZones;
use App\Support\IsolatedQaDatabase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
class InvestmentMapIndicatorsTest extends TestCase
{
use DatabaseTransactions;
protected function setUp(): void
{
parent::setUp();
$this->assertTrue(IsolatedQaDatabase::allowsSchemaChanges());
}
private function observation(string $indicator = 'employment', ?string $value = '0'): array
{
return ['id' => (string) Str::uuid(), 'label' => 'SYNTHETIC QA source observation', 'indicator' => $indicator,
'value' => $value, 'unit' => $indicator === 'employment' ? 'pessoas' : 'toneladas', 'period_start' => '2025-01-01', 'period_end' => '2025-12-31',
'scope' => 'Synthetic park only', 'population' => 'Synthetic declared establishments', 'methodology' => 'Synthetic documentary register',
'source_reference' => 'SYNTHETIC QA only, not official statistics', 'source_date' => '2026-01-15', 'knowledge' => 'documented'];
}
private function zone(string $prefix, array $rows, ?string $territory = null): InvestmentZone
{
$base = app(InvestmentZones::class)->visible()->where('category', 'industrial_park')->firstOrFail();
$zone = $base->replicate();
$zone->forceFill(['code' => 'QA-'.Str::uuid(), 'name' => $prefix.' synthetic park '.Str::uuid(), 'primary_territory_id' => $territory,
'editorial_draft' => 'PRIVATE-DRAFT-NOT-TO-BE-DECRYPTED',
'operational_details' => ['schema_version' => 1, 'public' => ['integral' => ['observations' => $rows]], 'private_ciphertext' => 'PRIVATE-QUANTITATIVE-SOURCE']])->save();
return $zone;
}
public function test_public_values_keep_zero_decimal_precision_units_sources_and_incomplete_metadata_without_private_fields(): void
{
$prefix = 'QUANT-'.Str::uuid();
$rows = [$this->observation(), $this->observation('production', '12.3400'),
[...$this->observation('investment', '100'), 'unit' => null], [...$this->observation(), 'knowledge' => 'unknown', 'value' => '999'],
[...$this->observation('exports', '25.50'), 'knowledge' => 'disputed', 'private_note' => 'PRIVATE-NOTE']];
$zone = $this->zone($prefix, $rows);
$before = $zone->fresh()->getRawOriginal('operational_details');
$r = $this->getJson('/api/v1/investment/map?q='.$prefix)->assertOk()->assertJsonPath('quantitative_context.state', 'available')
->assertJsonCount(5, 'quantitative_context.records')->assertJsonPath('quantitative_context.records.0.value', '0')
->assertJsonPath('quantitative_context.records.1.value', '12.3400')->assertJsonPath('quantitative_context.records.0.unit', 'pessoas')
->assertJsonPath('quantitative_context.records.2.missing_metadata', ['unit'])->assertJsonPath('quantitative_context.records.3.value', null)
->assertJsonPath('quantitative_context.records.4.knowledge', 'disputed')->assertJsonPath('quantitative_context.records.0.source_date', '2026-01-15');
$this->assertStringNotContainsString('PRIVATE-', $r->getContent());
$this->assertSame($before, $zone->fresh()->getRawOriginal('operational_details'));
$this->assertArrayNotHasKey('total', $r->json('quantitative_context'));
}
public function test_numeric_json_decimals_are_read_exactly_from_the_same_public_snapshot(): void
{
$prefix = 'QUANT-'.Str::uuid();
$zone = $this->zone($prefix, [$this->observation('production', '1')]);
DB::update("UPDATE investment_zones SET operational_details=jsonb_set(operational_details, '{public,integral,observations,0,value}', '1234567890123.456789'::jsonb) WHERE id=?", [$zone->id]);
$this->getJson('/api/v1/investment/map?q='.$prefix)->assertOk()
->assertJsonPath('quantitative_context.records.0.value', '1234567890123.456789');
}
public function test_draft_and_unselected_private_observations_remain_unavailable_and_specific_opportunity_filters_are_explicit(): void
{
$prefix = 'QUANT-'.Str::uuid();
$draft = $this->zone($prefix, [$this->observation()]);
$draft->forceFill(['publication_status' => 'draft'])->save();
$private = $this->zone($prefix, []);
$private->forceFill(['operational_details' => ['private_ciphertext' => 'PRIVATE-SOURCE-WITH-QUANTITY']])->save();
$this->getJson('/api/v1/investment/map?q='.$prefix)->assertOk()->assertJsonCount(0, 'quantitative_context.records');
$this->getJson('/api/v1/investment/map?q='.$prefix.'&sector=agronegocio')->assertOk()
->assertJsonPath('quantitative_context.state', 'not_included')->assertJsonCount(0, 'quantitative_context.records');
}
public function test_quantitative_context_uses_existing_province_and_district_scope_without_moving_any_source(): void
{
$prefix = 'QUANT-'.Str::uuid();
$districts = app(InvestmentGeography::class)->publicReferences()->where('location_type', 'district')->get(['id', 'parent_id']);
$district = $districts->first();
$other = $districts->first(fn ($row) => $row->parent_id !== $district->parent_id);
$zone = $this->zone($prefix, [$this->observation()], $district->id);
$this->zone($prefix, [$this->observation('production', '9')], $other->id);
foreach ([['province_id' => $district->parent_id], ['province_id' => $district->parent_id, 'district_id' => $district->id]] as $filters) {
$this->getJson('/api/v1/investment/map?'.http_build_query(['q' => $prefix, ...$filters]))->assertOk()
->assertJsonCount(1, 'quantitative_context.records')->assertJsonPath('quantitative_context.records.0.zone_id', $zone->id);
}
$this->assertSame($district->id, $zone->fresh()->primary_territory_id);
}
public function test_observation_limit_is_explicit_and_values_from_other_indicators_are_not_reinterpreted(): void
{
$rows = [];
for ($i = 0; $i < 201; $i++) {
$rows[] = $this->observation('production', (string) $i);
}
$rows[] = $this->observation('emissions', '42');
$zone = $this->zone('QUANT-'.Str::uuid(), $rows);
$result = app(InvestmentMapIndicators::class)->forZones(collect([$zone]));
$this->assertCount(200, $result['records']);
$this->assertTrue($result['truncated']);
$this->assertSame('economic_potential', $result['records'][0]['theme']);
}
public function test_zone_limit_does_not_claim_complete_territorial_coverage(): void
{
$prefix = 'QUANT-'.Str::uuid();
for ($i = 0; $i < 31; $i++) {
$this->zone($prefix, []);
}
$this->getJson('/api/v1/investment/map?q='.$prefix)->assertOk()->assertJsonPath('quantitative_context.zones_consulted', 30)
->assertJsonPath('quantitative_context.truncated', true)->assertJsonCount(30, 'territorial_context');
}
}