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.'§or=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'); } }