Files
Investidor-V2/tools/portal-audit-territory-coverage.php
nala 01405cbf8a
tests / ci (push) Canceled after 0s
oportunidades 5
2026-10-10 14:15:28 +02:00

75 lines
3.9 KiB
PHP

<?php
// Exercise the real public API over the restored active catalogue. No migration or active connection.
use App\Services\InvestmentGeography;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;
$root = dirname(__DIR__);
require_once $root.'/tools/portal-qa-environment.php';
$target = portalQaEnvironment();
require $root.'/vendor/autoload.php';
$app = require $root.'/bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$kernel->bootstrap();
$identity = portalQaAssertTarget($target);
DB::beginTransaction();
DB::statement('SET TRANSACTION READ ONLY');
// Enumerating the whole catalogue is a QA coverage run, independent of rate-limit acceptance.
RateLimiter::for('api', fn () => Limit::none());
$expected = app(InvestmentGeography::class)->publicReferences()->whereIn('location_type', ['province', 'district', 'admin_post', 'locality'])
->get(['id', 'parent_id', 'location_type']);
$counts = [];
$results = [];
$seen = [];
foreach (['province' => null, 'district' => 'province', 'admin_post' => 'district', 'locality' => 'admin_post'] as $type => $parentType) {
$parents = $parentType === null ? [null] : $expected->where('location_type', $parentType)->pluck('id')->all();
foreach ($parents as $parent) {
$page = 1;
$actual = [];
do {
$uri = '/api/v1/investment/territory-options?'.http_build_query(['type' => $type, 'parent_id' => $parent, 'page' => $page]);
$request = Request::create($uri, 'GET', [], [], [], ['HTTP_ACCEPT' => 'application/json']);
$response = $kernel->handle($request);
if ($response->getStatusCode() !== 200) {
throw new RuntimeException('Catalogue API failed: '.$uri.' HTTP '.$response->getStatusCode());
}
$body = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR);
foreach ($body['data'] as $item) {
if ($item['location_type'] !== $type || ($parent !== null && $item['parent_id'] !== $parent)) {
throw new RuntimeException('Hierarchy mismatch');
}
$actual[] = $item['id'];
$seen[$item['id']] = true;
}
$kernel->terminate($request, $response);
$results[] = ['type' => $type, 'parent_id' => $parent, 'page' => $page, 'http' => 200, 'rows' => count($body['data'])];
$page++;
} while ($body['next_page_url'] !== null);
$wanted = $expected->where('location_type', $type);
if ($parent !== null) {
$wanted = $wanted->where('parent_id', $parent);
}
$wanted = $wanted->pluck('id')->sort()->values()->all();
sort($actual);
if ($wanted !== $actual) {
throw new RuntimeException('API omissions/duplicates at '.$type.' '.$parent);
}
}
$counts[$type] = $expected->where('location_type', $type)->count();
}
if (count($seen) !== $expected->count()) {
throw new RuntimeException('Unreachable catalogue references');
}
DB::rollBack();
$result = ['captured_at' => gmdate('c'), 'target' => $identity, 'source' => 'Verified active backup restored in QA', 'counts' => $counts,
'references_expected' => $expected->count(), 'references_recovered' => count($seen), 'api_requests' => count($results), 'checks' => $results,
'visual_verified' => false, 'all_consumers_accepted' => false, 'geometry_rows' => DB::table('geo_geometries')->count(),
'rate_limit_scope' => 'Excluded only in QA bulk catalogue enumeration; access/rate behavior covered separately', 'active_writes' => 0];
file_put_contents($root.'/storage/app/private/investmoz-v2/portal-audit/territory-catalogue-coverage.json', json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
unset($result['checks']);
echo json_encode($result, JSON_THROW_ON_ERROR).PHP_EOL;