75 lines
3.9 KiB
PHP
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;
|