200 lines
14 KiB
PHP
200 lines
14 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use App\Services\CompanyComparison;
|
|
use App\Services\VirtualDesk;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Str;
|
|
use Tests\TestCase;
|
|
|
|
final class CompanyComparisonTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private User $owner;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->withoutVite();
|
|
config(['investor-path.enabled' => true, 'investmoz_ai.enabled' => false, 'suitecrm.enabled' => false, 'portal.notifications_enabled' => false]);
|
|
Http::preventStrayRequests();
|
|
$this->owner = User::factory()->create();
|
|
}
|
|
|
|
private function dossier(): string
|
|
{
|
|
return $this->actingAs($this->owner)->postJson('/investimentos', ['title' => 'SYNTHETIC company preparation', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
|
|
}
|
|
|
|
public function test_public_incomplete_comparison_uses_exact_mozambican_source_without_writing_or_requiring_ia(): void
|
|
{
|
|
$this->getJson('/formas-juridicas')->assertOk()->assertHeader('Cache-Control', 'no-store, private')
|
|
->assertJsonPath('result.source.sha256', '8009ef95a352dc02283e671cd717449ec8ad747a8995153cd3122ea16aa22a0f')
|
|
->assertJsonCount(5, 'result.forms')->assertJsonPath('case', null)->assertJsonCount(8, 'result.pending');
|
|
$this->get('/formas-juridicas')->assertOk()->assertInertia(fn ($page) => $page->component('investment/company-comparison'));
|
|
$this->assertDatabaseCount('desk_cases', 0);
|
|
$this->assertDatabaseCount('organizations', 0);
|
|
}
|
|
|
|
public function test_answers_explain_single_participant_and_listing_conflicts_without_an_automatic_winner(): void
|
|
{
|
|
$r = $this->getJson('/formas-juridicas?'.http_build_query(['answers' => ['purpose' => 'new', 'participants' => 'one', 'listing' => 'yes', 'participation' => 'shares', 'preferred' => 'simplified']]))->assertOk();
|
|
$forms = collect($r->json('result.forms'))->keyBy('code');
|
|
$this->assertNotEmpty($forms['collective']['constraints']);
|
|
$this->assertNotEmpty($forms['simplified']['constraints']);
|
|
$this->assertTrue($forms['simplified']['selected']);
|
|
foreach (['quotas', 'anonymous', 'simplified'] as $code) {
|
|
$this->assertStringContainsString('unipessoal', implode(' ', $forms[$code]['notes']));
|
|
}
|
|
$this->assertArrayNotHasKey('score', $forms['simplified']);
|
|
$this->assertStringContainsString('preferência', implode(' ', $forms['quotas']['notes']));
|
|
$this->getJson('/formas-juridicas?'.http_build_query(['answers' => ['participants' => 'several']]))->assertOk()->assertJsonCount(1, 'result.forms.0.constraints');
|
|
$this->getJson('/formas-juridicas?'.http_build_query(['answers' => ['unexpected' => 'field']]))->assertUnprocessable();
|
|
$this->getJson('/formas-juridicas?'.http_build_query(['answers' => ['preferred' => 'invented']]))->assertUnprocessable();
|
|
}
|
|
|
|
public function test_representation_and_cooperation_keep_inactive_answers_without_treating_them_as_new_societies(): void
|
|
{
|
|
foreach (['representation', 'cooperation'] as $purpose) {
|
|
$this->getJson('/formas-juridicas?'.http_build_query(['answers' => ['purpose' => $purpose, 'participants' => 'one', 'preferred' => 'quotas']]))->assertOk()
|
|
->assertJsonCount(0, 'result.forms')->assertJsonPath('result.active', ['purpose', 'regulated'])
|
|
->assertJsonPath('answers.participants', 'one')->assertJsonPath('answers.preferred', 'quotas');
|
|
}
|
|
}
|
|
|
|
public function test_questions_produce_applicable_preparation_without_a_false_choice_between_partners_and_administration(): void
|
|
{
|
|
$answers = ['purpose' => 'new', 'participants' => 'several', 'participation' => 'work', 'capital' => 'defined', 'management' => 'both', 'responsibility' => 'limited', 'listing' => 'yes', 'regulated' => 'yes'];
|
|
$response = $this->getJson('/formas-juridicas?'.http_build_query(['answers' => $answers]))->assertOk()->assertJsonCount(0, 'result.pending');
|
|
$preparation = implode(' ', $response->json('result.preparation'));
|
|
foreach (['contribuições definidas', 'poderes de gestão', 'nomeação', 'regime de responsabilidade', 'mercado pretendido', 'habilitações específicas'] as $expected) {
|
|
$this->assertStringContainsString($expected, $preparation);
|
|
}
|
|
$this->assertStringContainsString('trabalho', implode(' ', $response->json('result.forms.1.notes')));
|
|
}
|
|
|
|
public function test_private_comparison_saves_recovers_and_replays_without_changing_master_identity_or_steps(): void
|
|
{
|
|
$id = $this->dossier();
|
|
$url = '/investimentos/'.$id.'/forma-juridica';
|
|
$before = DB::table('journey_steps')->orderBy('id')->get()->toJson();
|
|
$case = DB::table('desk_cases')->where('id', $id)->first();
|
|
$payloadBefore = app(VirtualDesk::class)->decode($case->payload);
|
|
$input = ['version' => $case->version, 'operation_key' => (string) Str::uuid(), 'answers' => ['participants' => 'one', 'preferred' => 'quotas']];
|
|
$this->postJson($url, $input)->assertOk()->assertJsonPath('replayed', false)->assertJsonPath('revision', 1);
|
|
$this->postJson($url, $input)->assertOk()->assertJsonPath('replayed', true);
|
|
$this->postJson($url, [...$input, 'answers' => ['participants' => 'several']])->assertConflict();
|
|
$this->getJson($url)->assertOk()->assertHeader('Cache-Control', 'no-store, private')->assertJsonPath('saved.revision', 1)->assertJsonPath('answers.participants', 'one');
|
|
$this->assertSame(1, DB::table('desk_events')->where('case_id', $id)->where('action', CompanyComparison::EVENT)->count());
|
|
$stored = DB::table('desk_cases')->where('id', $id)->first();
|
|
$payload = app(VirtualDesk::class)->decode($stored->payload);
|
|
unset($payload['company_comparison']);
|
|
$this->assertSame($payloadBefore, $payload);
|
|
$this->assertStringNotContainsString('company_comparison', $stored->payload);
|
|
$this->assertSame($before, DB::table('journey_steps')->orderBy('id')->get()->toJson());
|
|
$this->assertDatabaseCount('organizations', 0);
|
|
$this->assertDatabaseCount('investment_company_declarations', 0);
|
|
$this->assertDatabaseCount('desk_requests', 0);
|
|
$currentVersion = $stored->version;
|
|
$this->postJson($url, [...$input, 'version' => $currentVersion, 'operation_key' => (string) Str::uuid()])->assertOk()->assertJsonPath('unchanged', true);
|
|
$this->assertSame($currentVersion, DB::table('desk_cases')->where('id', $id)->value('version'));
|
|
$this->assertSame(1, DB::table('desk_events')->where('case_id', $id)->where('action', CompanyComparison::EVENT)->count());
|
|
$this->getJson('/investimentos/'.$id)->assertOk()->assertJsonPath('tools.company', $url);
|
|
$this->postJson($url, [...$input, 'operation_key' => (string) Str::uuid()])->assertConflict();
|
|
}
|
|
|
|
public function test_historical_answers_sources_and_results_do_not_follow_future_engine_changes(): void
|
|
{
|
|
$id = $this->dossier();
|
|
$url = '/investimentos/'.$id.'/forma-juridica';
|
|
$version = DB::table('desk_cases')->where('id', $id)->value('version');
|
|
$original = ['version' => $version, 'operation_key' => (string) Str::uuid(), 'answers' => ['participants' => 'one']];
|
|
$this->postJson($url, $original)->assertOk();
|
|
$first = $this->getJson($url)->assertOk()->json();
|
|
config(['company-comparison.version' => 'SYNTHETIC-future-version', 'company-comparison.source.edition' => 'SYNTHETIC-future-edition']);
|
|
config(['company-comparison.questions.synthetic_future' => ['label' => 'SYNTHETIC future question', 'options' => ['unknown' => 'Unknown']]]);
|
|
$this->postJson($url, [...$original, 'version' => (string) $original['version']])->assertOk()->assertJsonPath('replayed', true)->assertJsonPath('revision', 1);
|
|
$this->getJson($url)->assertOk()->assertJsonPath('result', $first['result']);
|
|
$this->postJson($url, ['version' => $first['case']['version'], 'operation_key' => (string) Str::uuid(), 'answers' => ['participants' => 'several']])->assertOk()->assertJsonPath('revision', 2);
|
|
$revision = $first['history'][0]['id'];
|
|
$this->getJson($url.'?revision='.$revision)->assertOk()->assertJsonPath('historical', true)->assertJsonPath('result', $first['result'])->assertJsonPath('answers.participants', 'one');
|
|
$this->getJson($url.'?'.http_build_query(['revision' => $revision, 'answers' => ['participants' => 'several']]))->assertUnprocessable();
|
|
$this->getJson($url)->assertOk()->assertJsonPath('result.engine_version', 'SYNTHETIC-future-version')->assertJsonPath('answers.participants', 'several');
|
|
}
|
|
|
|
public function test_scope_is_rechecked_before_validation_and_revocation_or_closure_does_not_allow_writes(): void
|
|
{
|
|
$id = $this->dossier();
|
|
$url = '/investimentos/'.$id.'/forma-juridica';
|
|
$input = ['version' => DB::table('desk_cases')->where('id', $id)->value('version'), 'operation_key' => (string) Str::uuid(), 'answers' => []];
|
|
$this->actingAs(User::factory()->create())->getJson($url)->assertForbidden();
|
|
$this->postJson($url, [])->assertForbidden();
|
|
$this->actingAs($this->owner);
|
|
$this->postJson($url, $input)->assertOk();
|
|
DB::table('desk_cases')->where('id', $id)->update(['closed_at' => now()]);
|
|
$this->getJson($url)->assertOk()->assertJsonPath('canWrite', false);
|
|
$this->postJson($url, $input)->assertOk()->assertJsonPath('replayed', true);
|
|
$this->postJson($url, [...$input, 'operation_key' => (string) Str::uuid()])->assertConflict();
|
|
}
|
|
|
|
public function test_public_answers_continue_through_dossier_choice_and_explicit_creation_without_automatic_save(): void
|
|
{
|
|
$answers = ['participants' => 'one', 'preferred' => 'anonymous'];
|
|
$target = '/investimentos?'.http_build_query(['company_comparison' => $answers]);
|
|
$this->get($target)->assertRedirect('/login');
|
|
$this->assertSame(url($target), session('url.intended'));
|
|
$id = $this->dossier();
|
|
$this->getJson($target)->assertOk()->assertJsonPath('companyComparisonAnswers.participants', 'one');
|
|
$record = $this->postJson('/investimentos', ['title' => 'SYNTHETIC comparison destination', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true, 'company_comparison' => $answers])->assertCreated()->json();
|
|
$this->assertStringContainsString('/forma-juridica?', $record['comparison_href']);
|
|
$this->getJson($record['comparison_href'])->assertOk()->assertJsonPath('workingCopy', true)->assertJsonPath('answers.preferred', 'anonymous')->assertJsonPath('saved', null);
|
|
$this->assertSame(0, DB::table('desk_events')->where('action', CompanyComparison::EVENT)->count());
|
|
$this->getJson('/investimentos/'.$id.'/forma-juridica?'.http_build_query(['answers' => $answers]))->assertOk()->assertJsonPath('saved', null);
|
|
}
|
|
|
|
public function test_organization_coeditor_revocation_is_effective_before_replay_or_validation(): void
|
|
{
|
|
$id = $this->dossier();
|
|
$org = (string) Str::uuid();
|
|
DB::table('organizations')->insert(['id' => $org, 'name' => 'SYNTHETIC comparison organization', 'organization_type' => 'private']);
|
|
DB::table('desk_cases')->where('id', $id)->update(['organization_id' => $org]);
|
|
$coeditor = User::factory()->create();
|
|
$viewer = User::factory()->create();
|
|
foreach ([[$this->owner, 'officer'], [$coeditor, 'officer'], [$viewer, 'viewer']] as [$actor, $role]) {
|
|
DB::table('memberships')->insert(['id' => (string) Str::uuid(), 'organization_id' => $org, 'user_id' => $actor->id, 'role' => $role]);
|
|
}
|
|
$url = '/investimentos/'.$id.'/forma-juridica';
|
|
$input = ['version' => DB::table('desk_cases')->where('id', $id)->value('version'), 'operation_key' => (string) Str::uuid(), 'answers' => ['participants' => 'several']];
|
|
$this->actingAs($coeditor)->postJson($url, $input)->assertOk();
|
|
$this->actingAs($viewer)->getJson($url)->assertOk()->assertJsonPath('canWrite', false);
|
|
$this->postJson($url, $input)->assertForbidden();
|
|
$this->actingAs($coeditor);
|
|
DB::table('memberships')->where('organization_id', $org)->where('user_id', $coeditor->id)->update(['deleted_at' => now()]);
|
|
$this->getJson($url)->assertForbidden();
|
|
$this->postJson($url, $input)->assertForbidden();
|
|
$this->postJson($url, [])->assertForbidden();
|
|
$this->actingAs($this->owner)->getJson($url)->assertOk()->assertJsonPath('answers.participants', 'several');
|
|
$this->assertSame(1, DB::table('desk_events')->where('case_id', $id)->where('action', CompanyComparison::EVENT)->count());
|
|
}
|
|
|
|
public function test_all_history_pages_recover_old_editions_without_overwriting_the_latest_snapshot(): void
|
|
{
|
|
$id = $this->dossier();
|
|
$url = '/investimentos/'.$id.'/forma-juridica';
|
|
$version = DB::table('desk_cases')->where('id', $id)->value('version');
|
|
for ($revision = 1; $revision <= 26; $revision++) {
|
|
$this->postJson($url, ['version' => $version++, 'operation_key' => (string) Str::uuid(), 'answers' => ['participants' => $revision % 2 === 1 ? 'one' : 'several']])->assertOk()->assertJsonPath('revision', $revision);
|
|
}
|
|
$page = $this->getJson($url)->assertOk()->assertJsonCount(25, 'history')->assertJsonPath('saved.revision', 26)->json();
|
|
$this->assertNotNull($page['historyNext']);
|
|
$older = $this->getJson($url.'?history_before='.$page['historyNext'])->assertOk()->assertJsonCount(1, 'history')->assertJsonPath('history.0.revision', 1)->assertJsonPath('saved.revision', 26)->json();
|
|
$this->getJson($url.'?revision='.$older['history'][0]['id'])->assertOk()->assertJsonPath('saved.revision', 1);
|
|
$this->getJson($url)->assertOk()->assertJsonPath('saved.revision', 26);
|
|
}
|
|
}
|