135 lines
12 KiB
PHP
135 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Opportunity;
|
|
use App\Models\User;
|
|
use App\Services\{CorridorAgribusinessOpportunities, InvestmentCorridorContext, InvestmentConsumerScope, InvestorContext, InvestmentZones, VirtualDesk};
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Tests\TestCase;
|
|
|
|
final class InvestmentConsumerScopeTest extends TestCase
|
|
{
|
|
use DatabaseTransactions;
|
|
private User $actor;
|
|
private string $id;
|
|
private array $choices;
|
|
private array $parks;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();$this->withoutVite();
|
|
$this->withoutMiddleware(\Illuminate\Routing\Middleware\ThrottleRequests::class);
|
|
$db=DB::selectOne('select current_database() as name,inet_server_port() as port');
|
|
$this->assertSame('simulator_fiscal_delta_20261021_test',$db->name);$this->assertSame(55441,(int)$db->port);
|
|
config(['investor-path.enabled'=>true,'investor-path.private_preparation'=>true,'suitecrm.enabled'=>false,'investmoz_ai.enabled'=>false]);
|
|
$this->actor=User::factory()->create();$this->actingAs($this->actor);
|
|
$this->id=$this->postJson('/investimentos',['title'=>'SYNTHETIC QA — âmbito específico','client_key'=>(string)Str::uuid(),'reuse_consent'=>true])->assertCreated()->json('id');
|
|
$corridors=app(InvestmentCorridorContext::class);
|
|
$records=[];foreach(['beira','limpopo'] as $corridor){
|
|
$entry=collect(app(CorridorAgribusinessOpportunities::class)->entries())->firstWhere('corridor',config('investment_corridors.'.$corridor.'.name'));
|
|
$records[]=Opportunity::where('management_creation_key',$entry['key'])->firstOrFail();
|
|
}
|
|
$sofala=collect($corridors->provinces('beira'))->firstWhere('source_code','07')->id;
|
|
$gaza=$corridors->provinces('limpopo')[0]->id;
|
|
$manica=collect($corridors->provinces('beira'))->firstWhere('name','Manica')->id;
|
|
$template=app(InvestmentZones::class)->visible()->whereIn('category',['industrial_park','zee','zfi'])->firstOrFail();
|
|
foreach([$sofala,$gaza,$manica] as $territory){$p=$template->replicate();$p->code='SYNTHETIC-QA-'.Str::uuid();$p->name='SYNTHETIC QA — parque '.$territory;$p->primary_territory_id=$territory;$p->save();$this->parks[]=$p;}
|
|
$this->choices=[['source'=>'opportunity:'.$records[0]->id,'corridor_key'=>'beira','territory'=>'territory:'.$sofala,'park'=>'park:'.$this->parks[0]->id,'activities'=>['production']],['source'=>'opportunity:'.$records[1]->id,'corridor_key'=>'limpopo','territory'=>'territory:'.$gaza,'park'=>'park:'.$this->parks[1]->id,'activities'=>['trade']]];
|
|
$this->confirm();
|
|
}
|
|
|
|
private function confirm(): array
|
|
{
|
|
$v=$this->getJson('/investimentos/'.$this->id.'/contexto')->assertOk()->json();
|
|
$body=['version'=>$v['case']['version'],'selection'=>[],'opportunity_choices'=>$this->choices];
|
|
$plan=$this->postJson('/investimentos/'.$this->id.'/contexto/previa',$body)->assertOk()->json();
|
|
$this->postJson('/investimentos/'.$this->id.'/contexto/confirmar',[...$body,'preview_hash'=>$plan['preview_hash'],'operation_key'=>(string)Str::uuid(),'confirm'=>true])->assertOk();
|
|
return $this->getJson('/investimentos/'.$this->id.'/contexto')->assertOk()->json();
|
|
}
|
|
|
|
public function test_second_only_change_updates_its_parks_and_all_consumer_proposals_without_replacing_first(): void
|
|
{
|
|
$before=$this->getJson('/investimentos/'.$this->id.'/contexto')->json('context');
|
|
$key=$this->choices[1]['source'];
|
|
$this->getJson('/investimentos/'.$this->id.'/contexto?opportunity='.urlencode($key))->assertOk()->assertJsonPath('prefill.land.parcels.0.province_id',$this->parks[1]->primary_territory_id);
|
|
$pending=$this->choices;$pending[1]=[...$pending[1],'corridor_key'=>'beira','territory'=>null,'park'=>null];
|
|
$this->postJson('/investimentos/'.$this->id.'/contexto/rascunho',['version'=>$this->getJson('/investimentos/'.$this->id.'/contexto')->json('case.version'),'selection'=>[],'opportunity_choices'=>$pending,'draft_sequence'=>1000000])->assertOk();
|
|
$this->get('/investimentos/'.$this->id.'/contexto/editar')->assertOk()->assertInertia(fn($p)=>$p->where('requestedChoices.1.territory',null));
|
|
$this->choices[1]=[...$this->choices[1],'corridor_key'=>'beira','territory'=>'territory:'.$this->parks[2]->primary_territory_id,'park'=>'park:'.$this->parks[2]->id,'confirm_location_change'=>true,'location_reason'=>'Alteração expressa apenas da segunda oportunidade para comparação QA.'];
|
|
$view=$this->confirm();
|
|
$this->assertSame($before['opportunity_choices'][0],$view['context']['opportunity_choices'][0]);
|
|
$this->assertSame($this->parks[2]->id,$view['context']['opportunity_choices'][1]['park']['id']);
|
|
$scope=$this->getJson('/investimentos/'.$this->id.'/contexto?opportunity='.urlencode($key))->assertOk();
|
|
$scope->assertJsonPath('prefill.land.parcels.0.province_id',$this->parks[2]->primary_territory_id)->assertJsonPath('prefill.desk.territory_id',$this->parks[2]->primary_territory_id)->assertJsonPath('prefill.submission.location','Manica');
|
|
$snapshot=json_decode($scope->json('prefill.simulator.context_applicability_snapshot'),true);
|
|
$this->assertSame($this->parks[2]->id,$snapshot['zone']['id']);
|
|
foreach($snapshot['requirements_assessment']['requirements'] as $r)$this->assertSame($key,$r['opportunity_key']);
|
|
$this->assertCount(1,$snapshot['opportunity_choices']);
|
|
$this->choices[1]['park']='park:'.$this->parks[1]->id;
|
|
$this->postJson('/investimentos/'.$this->id.'/contexto/previa',['version'=>$view['case']['version'],'selection'=>[],'opportunity_choices'=>$this->choices])->assertUnprocessable();
|
|
}
|
|
|
|
public function test_ambiguous_scope_proposes_only_common_data_and_rejects_foreign_identity(): void
|
|
{
|
|
$view=$this->getJson('/investimentos/'.$this->id.'/contexto')->assertOk()->json();
|
|
$this->assertSame('SYNTHETIC QA — âmbito específico',$view['prefill']['land']['project_reference']);
|
|
$this->assertEmpty($view['prefill']['land']['parcels'][0]);
|
|
$this->assertArrayNotHasKey('territory_id',$view['prefill']['desk']);
|
|
$this->assertArrayNotHasKey('location',$view['prefill']['submission']);
|
|
$this->getJson('/investimentos/'.$this->id.'/contexto?opportunity=opportunity:99999999')->assertUnprocessable();
|
|
$this->actingAs(User::factory()->create())->getJson('/investimentos/'.$this->id.'/contexto?opportunity='.urlencode($this->choices[1]['source']))->assertForbidden();
|
|
}
|
|
|
|
public function test_land_desk_and_manifestation_save_and_recover_canonical_second_identity_and_preserve_deliberate_fields(): void
|
|
{
|
|
$first=$this->getJson('/investimentos/'.$this->id.'/contexto')->json('context.opportunity_choices.0');
|
|
$this->choices[1]=[...$this->choices[1],'corridor_key'=>'beira','territory'=>'territory:'.$this->parks[2]->primary_territory_id,'park'=>'park:'.$this->parks[2]->id,'confirm_location_change'=>true,'location_reason'=>'Alteração expressa apenas da segunda oportunidade para comparação QA.'];
|
|
$this->confirm();
|
|
$key=$this->choices[1]['source'];
|
|
$scope=app(InvestorContext::class)->prefill($this->getJson('/investimentos/'.$this->id.'/contexto')->json('context'),$key);
|
|
$land=$this->postJson('/investidor/terra-duat',['payload'=>[...$scope['land'],'investor_path_case_id'=>$this->id,'context_opportunity'=>$key,'context_opportunity_snapshot'=>'forged','purpose'=>'Texto deliberado do requerente']])->assertCreated()->json('id');
|
|
$landPayload=$this->getJson('/investidor/terra-duat/'.$land)->assertOk()->json('case.payload');
|
|
$this->assertSame('Texto deliberado do requerente',$landPayload['purpose']);
|
|
$this->assertSame($key,json_decode($landPayload['context_opportunity_snapshot'],true)['choice']['opportunity']['key']);
|
|
$this->get('/investidor/terra-duat/'.$land.'/imprimir')->assertOk()->assertInertia(fn($p)=>$p->where('case.payload.context_opportunity',$key));
|
|
$desk=app(VirtualDesk::class);$service=DB::table('desk_service_versions')->where('status','published')->firstOrFail();
|
|
$request=$desk->createRequest($this->actor,$this->id,$service->id,(string)Str::uuid(),$key);
|
|
$payload=$desk->decode($request->payload);$payload['context_opportunity_snapshot']='forged';
|
|
if(isset($desk->definition($request)['fields']['description']))$payload['description']='Descrição deliberada de QA';
|
|
$saved=$desk->save($this->actor,$request->id,['version'=>$request->version,'payload'=>$payload]);
|
|
$recovered=$desk->decode($desk->request($this->actor,$saved->id)->payload);
|
|
$this->assertSame($key,$recovered['context_opportunity']);
|
|
$this->assertSame($this->parks[2]->id,json_decode($recovered['context_opportunity_snapshot'],true)['choice']['park']['id']);
|
|
$rendered=view('desk.receipt',['record'=>(object)[...(array)$saved,'protocol'=>'SYNTHETIC-QA-SCOPE','submitted_at'=>now()->toDateTimeString()],'snapshot'=>['payload'=>$recovered],'definition'=>$desk->definition($saved)])->render();
|
|
$this->assertStringContainsString('Manica',$rendered);$this->assertStringContainsString($key,$rendered);
|
|
$submission=$this->postJson('/investidor/manifestacoes',['opportunity_id'=>(int)substr($key,12)])->assertCreated()->json('id');
|
|
$v=$this->getJson('/investimentos/'.$this->id.'/contexto')->json('case.version');
|
|
$bound=$this->postJson('/investidor/manifestacoes/'.$submission.'/contexto',['case_id'=>$this->id,'case_version'=>$v,'version'=>1])->assertOk()->json('version');
|
|
$this->putJson('/investidor/manifestacoes/'.$submission,['version'=>$bound,'payload'=>[...$scope['submission'],'context_opportunity'=>$key,'context_opportunity_snapshot'=>'forged','objective'=>'Objectivo deliberado do investidor']])->assertOk();
|
|
$p=$this->getJson('/investidor/manifestacoes/'.$submission)->assertOk()->json('submission.payload');
|
|
$this->assertSame($key,$p['context_opportunity']);$this->assertSame('Objectivo deliberado do investidor',$p['objective']);
|
|
$this->assertSame($key,json_decode($p['context_opportunity_snapshot'],true)['choice']['opportunity']['key']);
|
|
$this->assertSame('Manica',json_decode($p['context_opportunity_snapshot'],true)['choice']['territory']['title']);
|
|
$this->assertSame((int)substr($key,12),$this->getJson('/investidor/manifestacoes/'.$submission)->json('submission.opportunity_id'));
|
|
$revision=$this->getJson('/investidor/manifestacoes/'.$submission.'/revisions/3')->assertOk()->json('snapshot.payload');
|
|
$this->assertSame($key,$revision['context_opportunity']);
|
|
$this->assertSame($first,$this->getJson('/investimentos/'.$this->id.'/contexto')->json('context.opportunity_choices.0'));
|
|
}
|
|
|
|
public function test_returned_request_keeps_its_locked_opportunity_snapshot_when_only_description_is_editable(): void
|
|
{
|
|
$desk=app(VirtualDesk::class);$service=DB::table('desk_service_versions')->where('status','published')->firstOrFail();
|
|
$row=$desk->createRequest($this->actor,$this->id,$service->id,(string)Str::uuid(),$this->choices[1]['source']);
|
|
$payload=$desk->decode($row->payload);$snapshot=$payload['context_opportunity_snapshot'];
|
|
DB::table('desk_requests')->where('id',$row->id)->update(['status'=>'returned','editable_fields'=>json_encode(['description']),'protocol'=>'SYNTHETIC-QA-'.Str::uuid(),'submitted_at'=>now(),'submitted_snapshot'=>$desk->encode(['payload'=>$payload,'investment_context'=>['revision'=>1]])]);
|
|
$this->choices[1]=[...$this->choices[1],'corridor_key'=>'beira','territory'=>'territory:'.$this->parks[2]->primary_territory_id,'park'=>'park:'.$this->parks[2]->id,'confirm_location_change'=>true,'location_reason'=>'Mudança apenas da segunda oportunidade após devolução do pedido QA.'];$this->confirm();
|
|
$saved=$desk->save($this->actor,$row->id,['version'=>$row->version,'payload'=>[...$payload,'description'=>'Correcção deliberada apenas da descrição']]);
|
|
$after=$desk->decode($saved->payload);
|
|
$this->assertSame($snapshot,$after['context_opportunity_snapshot']);
|
|
$this->assertSame($payload['context_opportunity'],$after['context_opportunity']);
|
|
}
|
|
}
|