Files
Investidor-V2/tests/Feature/FinancialInstitutionsSourceTest.php
2026-11-10 21:09:18 +02:00

144 lines
12 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Services\FundingDossierPreparation;
use App\Services\FundingProgrammeReferences;
use App\Services\FundingProgrammes;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Tests\TestCase;
final class FinancialInstitutionsSourceTest extends TestCase
{
use DatabaseTransactions;
public function test_assisted_editor_prepares_without_publishing_and_preserves_deliberate_edits(): void
{
$this->withoutVite();
$actor = User::factory()->create();
config(['portal.managers' => [$actor->email]]);
$this->actingAs($actor);
$this->get('/management/fundos/referencias-assistidas')->assertOk()->assertInertia(fn ($page) => $page->component('funding/assisted')->has('batch.items', 275));
$plan = $this->getJson('/management/fundos/referencias/arko')->assertOk()->json();
$receipt = $this->postJson('/management/fundos/referencias/arko/preparar', ['hash' => $plan['hash']])->assertOk()->assertJsonPath('created', true)->assertJsonPath('matches_reference', true)->json();
$id = $receipt['id'];
$this->assertNull(DB::table('funding_programmes')->where('id', $id)->value('published_version_id'));
$this->postJson('/management/fundos/referencias/arko/preparar', ['hash' => $plan['hash']])->assertOk()->assertJsonPath('created', false)->assertJsonPath('id', $id);
$body = [...$receipt['publication'], 'reason' => 'SYNTHETIC explicit editorial publication confirmation, rolled back.'];
$this->postJson('/management/fundos/'.$id.'/publicar', $body)->assertUnprocessable();
$published = $this->postJson('/management/fundos/'.$id.'/publicar', [...$body, 'confirm' => true])->assertOk()->json('version_id');
$this->postJson('/management/fundos/'.$id.'/publicar', [...$body, 'confirm' => true])->assertOk()->assertJsonPath('version_id', $published);
$row = DB::table('funding_programmes')->where('id', $id)->first();
$draft = json_decode($row->draft, true);
$draft['summary'] .= ' Deliberate QA editorial clarification.';
DB::table('funding_programmes')->where('id', $id)->update(['draft' => json_encode($draft), 'version' => $row->version + 1]);
$this->postJson('/management/fundos/referencias/arko/preparar', ['hash' => $plan['hash']])->assertOk()->assertJsonPath('matches_reference', false)->assertJsonPath('publication', null);
$this->assertSame($draft, json_decode(DB::table('funding_programmes')->where('id', $id)->value('draft'), true));
$other = User::factory()->create();
$this->actingAs($other)->get('/management/fundos/referencias-assistidas')->assertForbidden();
}
public function test_city_or_branch_text_is_searchable_without_treating_wildcards_as_search_commands(): void
{
$this->withoutVite();
Http::preventStrayRequests();
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
$actor = User::factory()->create();
config(['portal.managers' => [$actor->email]]);
$this->actingAs($actor);
$references = app(FundingProgrammeReferences::class);
$plan = $references->preview($actor, 'arko');
$id = $references->prepare($actor, 'arko', $plan['hash'])['id'];
$service = app(FundingProgrammes::class);
$publication = $service->preview($actor, $id);
$service->publish($actor, $id, ['version' => $publication['version'], 'hash' => $publication['hash'], 'confirm' => true, 'reason' => 'SYNTHETIC QA city search through the existing controller, rolled back.']);
$province = DB::table('geo_locations')->where('location_type', 'province')->where('name', 'Sofala')->value('id');
$filters = ['q' => 'Beira', 'territory' => $province, 'location_mode' => 'physical', 'category' => 'insurance', 'institution_type' => 'insurer'];
$this->get('/fundos?'.http_build_query($filters))->assertOk()->assertInertia(fn ($page) => $page->component('funding/index')->where('items.data.0.id', $id));
$this->get('/fundos?'.http_build_query([...$filters, 'q' => '%']))->assertOk()->assertInertia(fn ($page) => $page->component('funding/index')->has('items.data', 0));
$record = $service->published($id);
$location = collect($record['payload']['locations'])->first(fn ($loc) => $loc['territory_id'] === $province && $loc['role'] === 'branch');
$case = $this->postJson('/investimentos', ['title' => 'SYNTHETIC continuity during investor preparation', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
$tracking = app(FundingDossierPreparation::class);
$context = $tracking->context($actor, $case, null);
$saved = $this->postJson('/fundos/'.$id.'/dossier', ['context' => $case, 'version' => $context['version'], 'version_id' => $record['version_id'], 'location_id' => $location['id'], 'location_mode' => 'physical', 'territory' => $province, 'need' => 'insurance', 'operation_key' => (string) Str::uuid(), 'consent' => true])->assertOk()->json();
$this->post('/investimentos/'.$case.'/aplicabilidade', ['version' => $saved['version'], 'land_needed' => 'unknown', 'external_funding' => 'yes', 'viability' => 'unknown', 'reason' => 'SYNTHETIC next preparation step retains selected insurer and provenance.'])->assertRedirect();
$continued = $tracking->context($actor, $case, null);
$this->assertSame($saved['selections'], $continued['selections']);
$this->assertGreaterThan($saved['version'], $continued['version']);
$this->assertSame('insurance', $continued['selections'][0]['need']);
Http::assertNothingSent();
}
public function test_supplied_institutions_can_be_found_by_physical_location_category_and_type_and_remain_traceable_in_one_dossier(): void
{
$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();
Http::preventStrayRequests();
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
$actor = User::factory()->create();
config(['portal.managers' => [$actor->email]]);
$this->actingAs($actor);
$source = json_decode(file_get_contents(database_path('data/funding-institutions-nov2026.json')), true, 512, JSON_THROW_ON_ERROR);
$references = app(FundingProgrammeReferences::class);
$catalogue = app(FundingProgrammes::class);
$records = [];
foreach ($source['references'] as $code => $reference) {
$plan = $references->preview($actor, $code);
$id = $references->prepare($actor, $code, $plan['hash'])['id'];
$this->assertNull(DB::table('funding_programmes')->where('id', $id)->value('published_version_id'), $code);
$ready = $catalogue->preview($actor, $id);
$catalogue->publish($actor, $id, ['version' => $ready['version'], 'hash' => $ready['hash'], 'confirm' => true, 'reason' => 'SYNTHETIC QA publication of supplied documentary facts; rolled back.']);
$records[$code] = $catalogue->published($id);
$this->assertSame($reference['draft']['institution_key'], $records[$code]['payload']['institution_key']);
}
$this->assertCount(270, $records);
$this->assertCount(270, array_unique(array_map(fn ($r) => $r['payload']['institution_key'], $records)));
$province = DB::table('geo_locations')->where('location_type', 'province')->where('name', 'Nampula')->value('id');
$filters = ['territory' => $province, 'location_mode' => 'physical', 'category' => 'insurance', 'institution_type' => 'insurer', 'certainty' => 'documented'];
$found = $catalogue->catalogueQuery($filters)->pluck('p.code')->all();
$this->assertContains('arko', $found);
$this->assertNotContains('bci', $found);
$this->assertNotContains('issm', $found);
$this->assertFalse($catalogue->catalogueQuery(['territory' => $province, 'need' => 'investment'])->where('p.id', $records['arko']['id'])->exists(), 'Insurer is not silently a credit provider');
$this->assertCount(8, $catalogue->catalogueQuery(['institution_type' => 'pension_manager'])->get());
$this->assertFalse($catalogue->catalogueQuery([])->where('p.id', $records['abc-historical']['id'])->exists());
$this->assertTrue($catalogue->catalogueQuery(['include_historical' => true])->where('p.id', $records['abc-historical']['id'])->exists());
$this->assertSame('access-bank', $records['abc-historical']['payload']['successor_key']);
$this->get('/fundos?'.http_build_query($filters))->assertOk()->assertInertia(fn ($p) => $p->component('funding/index')->where('filters.category', 'insurance')->where('filters.institution_type', 'insurer'));
$case = $this->postJson('/investimentos', ['title' => 'SYNTHETIC bank and insurance preparation', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
$tracking = app(FundingDossierPreparation::class);
$before = $tracking->context($actor, $case, null);
$bank = ['context' => $case, 'version' => $before['version'], 'version_id' => $records['bci']['version_id'], 'need' => 'investment', 'territory' => $province, 'product' => 'bci-oferta', 'operation_key' => (string) Str::uuid(), 'consent' => true];
$afterBank = $this->postJson('/fundos/'.$records['bci']['id'].'/dossier', $bank)->assertOk()->json();
$location = collect($records['arko']['payload']['locations'])->first(fn ($loc) => $loc['territory_id'] === $province && $loc['role'] === 'branch');
$this->assertNotNull($location);
$insurance = ['context' => $case, 'version' => $afterBank['version'], 'version_id' => $records['arko']['version_id'], 'need' => 'insurance', 'territory' => $province, 'location_mode' => 'physical', 'location_id' => $location['id'], 'operation_key' => (string) Str::uuid(), 'consent' => true];
$saved = $this->postJson('/fundos/'.$records['arko']['id'].'/dossier', $insurance)->assertOk()->assertJsonCount(2, 'selections')->json();
$this->postJson('/fundos/'.$records['arko']['id'].'/dossier', $insurance)->assertOk()->assertJsonCount(2, 'selections');
$this->postJson('/fundos/'.$records['arko']['id'].'/dossier', [...$insurance, 'version' => $saved['version'], 'operation_key' => (string) Str::uuid(), 'location_id' => 'foreign-location'])->assertUnprocessable();
$catalogue->withdraw($actor, $records['arko']['id'], $records['arko']['version'], 'SYNTHETIC QA withdrawal without rewriting investor history.');
$recovered = $tracking->context($actor, $case, null);
$this->assertSame($saved['selections'], $recovered['selections']);
$chosen = $recovered['selections'][1];
$this->assertSame('arko', $chosen['institution_key']);
$this->assertSame(['insurance'], $chosen['categories']);
$this->assertContains('insurer', $chosen['institution_types']);
$this->assertSame($location['id'], $chosen['location']['id']);
$this->assertSame($source['source']['sha256'], $chosen['location']['source_document_sha256']);
$this->getJson('/investimentos/'.$case)->assertOk()->assertJsonCount(2, 'funding_selections');
$directory = base_path('outputs/funding-directory-20261110/institutions');
if (! is_dir($directory)) {
mkdir($directory, 0700, true);
}
file_put_contents($directory.'/content.json', json_encode(['synthetic_editorial_publication_rolled_back' => true, 'source' => $source['source'], 'records' => $records, 'nampula_insurers' => $found, 'dossier_recovered' => $recovered, 'no_external_submission' => true], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
Http::assertNothingSent();
}
}