335 lines
22 KiB
PHP
335 lines
22 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Opportunity;
|
|
use App\Models\User;
|
|
use App\Services\InvestmentCatalogue;
|
|
use App\Services\InvestorContext;
|
|
use App\Services\OpportunityClassification;
|
|
use App\Services\OpportunityManagement;
|
|
use App\Services\ProactiveInvestment;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Tests\Support\ProactiveInvestmentFixture as F;
|
|
use Tests\TestCase;
|
|
|
|
final class ProactiveInvestmentTest extends TestCase
|
|
{
|
|
use DatabaseTransactions;
|
|
|
|
private User $actor;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
F::guard();
|
|
$this->withoutVite();
|
|
config(['portal.profile_access_enabled' => true, 'portal.opportunity_specialized_drafts_enabled' => true, 'portal.managers' => [], 'investor-path.enabled' => true, 'suitecrm.enabled' => false, 'investmoz_ai.enabled' => false]);
|
|
$this->actor = F::operator();
|
|
$this->login($this->actor);
|
|
}
|
|
|
|
private function login(User $u): void
|
|
{
|
|
$this->actingAs($u)->withSession(['portal.mfa_actor' => $u->id, 'portal.mfa_at' => time(), 'portal.access_revision' => (int) $u->portal_access_revision]);
|
|
}
|
|
|
|
private function create(string $pilot = 'Cahora Bassa'): Opportunity
|
|
{
|
|
$response = $this->postJson('/minhas-oportunidades/drafts', F::input($pilot));
|
|
if ($response->status() !== 201) {
|
|
fwrite(STDERR, substr($response->getContent(), 0, 400)."\n");
|
|
}$id = $response->assertCreated()->json('id');
|
|
|
|
return Opportunity::findOrFail($id);
|
|
}
|
|
|
|
private function publication(Opportunity $o, array $patch = []): array
|
|
{
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
|
|
return [...['etag' => app(OpportunityManagement::class)->etag($o), 'action' => 'disclose', 'title' => $o->title, 'summary' => 'SYNTHETIC QA ideia em preparação; não é oportunidade com participação disponível.', 'description' => 'Conteúdo público expressamente redigido.', 'limitations' => 'Conclusões locais por confirmar.', 'source_reference' => 'SYNTHETIC QA exemplo público', 'edition' => 'QA1', 'source_date' => '2026-10-27', 'sector_id' => $o->sector_id, 'territory_ids' => [$o->primary_territory_id]], ...F::publication($a), ...$patch];
|
|
}
|
|
|
|
private function publish(Opportunity $o): array
|
|
{
|
|
$p = $this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/previa', $this->publication($o))->assertOk()->json();
|
|
$input = ['token' => $p['token'], 'operation_key' => (string) Str::uuid(), 'confirm' => true, 'reason' => 'SYNTHETIC QA explicit public projection reviewed without private information'];
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/confirmar', $input)->assertOk();
|
|
|
|
return $input;
|
|
}
|
|
|
|
public function test_legacy_records_without_analysis_add_no_publication_lookup(): void
|
|
{
|
|
$o = new Opportunity;
|
|
$o->forceFill(['workflow_context' => []]);
|
|
DB::enableQueryLog();
|
|
DB::flushQueryLog();
|
|
$this->assertNull(app(ProactiveInvestment::class)->publicData($o));
|
|
$this->assertSame([], DB::getQueryLog());
|
|
DB::disableQueryLog();
|
|
}
|
|
|
|
public function test_create_recover_edit_with_etag_and_historical_foundation_without_financial_or_promoter_requirements(): void
|
|
{
|
|
$o = $this->create();
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$tag = app(OpportunityManagement::class)->etag($o);
|
|
$this->assertNull($o->estimated_investment_amount);
|
|
$this->assertNull($o->promoter_name);
|
|
$this->assertNotNull($o->primary_territory_id);
|
|
$this->get('/minhas-oportunidades/'.$o->id.'/edit')->assertOk()->assertInertia(fn ($page) => $page->where('values.proactive.need.state', 'hypothesis')->has('proactiveHistory', 1));
|
|
$a['need']['state'] = 'confirmed';
|
|
$a['need']['confirmation'] = 'SYNTHETIC QA confirmation of fixture conditions only, no real market validation.';
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => $tag, 'proactive' => $a, 'reason' => 'SYNTHETIC QA updated documented foundation for this fixture only'])->assertOk();
|
|
$this->assertSame('confirmed', app(OpportunityManagement::class)->values($o->fresh())['proactive']['need']['state']);
|
|
$history = app(ProactiveInvestment::class)->history($o);
|
|
$this->assertCount(2, $history);
|
|
$this->assertSame('hypothesis', $history[1]['state']);
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => $tag, 'proactive' => $a, 'reason' => 'SYNTHETIC QA stale version cannot overwrite current analysis'])->assertStatus(409);
|
|
}
|
|
|
|
public function test_hypothesis_cannot_be_confirmed_without_documented_conditions_and_missing_is_not_zero(): void
|
|
{
|
|
$o = $this->create();
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$a['need']['state'] = 'confirmed';
|
|
$a['need']['observed_demand'] = '';
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA incomplete confirmation must preserve prior hypothesis'])->assertUnprocessable();
|
|
$this->assertSame('hypothesis', app(OpportunityManagement::class)->values($o->fresh())['proactive']['need']['state']);
|
|
$a['need']['state'] = 'hypothesis';
|
|
$a['idea']['resources'] = '';
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA unknown resource remains blank rather than zero'])->assertOk();
|
|
$this->assertNull(app(OpportunityManagement::class)->values($o->fresh())['proactive']['idea']['resources']);
|
|
}
|
|
|
|
public function test_methodology_alone_cannot_confirm_local_need(): void
|
|
{
|
|
$o = $this->create();
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$a['need']['state'] = 'confirmed';
|
|
$a['evidence'][0]['purpose'] = 'methodology';
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA international methodology is not evidence of local demand'])->assertUnprocessable();
|
|
$this->assertSame('hypothesis', app(OpportunityManagement::class)->values($o->fresh())['proactive']['need']['state']);
|
|
}
|
|
|
|
public function test_creation_replay_and_duplicate_evidence_and_relations_do_not_duplicate_analysis(): void
|
|
{
|
|
$input = F::input();
|
|
$id = $this->postJson('/minhas-oportunidades/drafts', $input)->assertCreated()->json('id');
|
|
$this->postJson('/minhas-oportunidades/drafts', $input)->assertCreated()->assertJsonPath('id', $id);
|
|
$o = Opportunity::findOrFail($id);
|
|
$a = $input['proactive'];
|
|
$a['evidence'][] = [...$a['evidence'][0], 'id' => (string) Str::uuid()];
|
|
$this->putJson('/minhas-oportunidades/'.$id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA repeated evidence must be reconciled, not counted twice'])->assertUnprocessable();
|
|
$this->assertSame(1, Opportunity::where('management_creation_key', $input['creation_key'])->count());
|
|
}
|
|
|
|
public function test_document_from_another_scope_cannot_be_attached_or_reclassified_as_public(): void
|
|
{
|
|
$o = $this->create();
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$a['evidence'][0]['document_id'] = (string) Str::uuid();
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA document association must use existing access rules'])->assertUnprocessable();
|
|
}
|
|
|
|
public function test_existing_permissions_reject_unassigned_and_unrelated_operators(): void
|
|
{
|
|
$o = $this->create();
|
|
$this->login(User::factory()->create());
|
|
$this->postJson('/minhas-oportunidades/drafts', F::input())->assertForbidden();
|
|
$this->get('/minhas-oportunidades/'.$o->id.'/edit')->assertForbidden();
|
|
$this->login(F::operator());
|
|
$this->get('/minhas-oportunidades/'.$o->id.'/edit')->assertForbidden();
|
|
$this->login($this->actor);
|
|
$input = F::input();
|
|
unset($input['proactive']);
|
|
$this->postJson('/minhas-oportunidades/drafts', $input)->assertForbidden();
|
|
$this->assertNull($o->organization_id);
|
|
}
|
|
|
|
public function test_internal_attached_document_cannot_be_changed_to_public_through_analysis(): void
|
|
{
|
|
$o = $this->create();
|
|
$doc = (string) Str::uuid();
|
|
$hash = hash('sha256', 'SYNTHETIC QA private source');
|
|
DB::table('documents')->insert(['id' => $doc, 'file_name' => 'SYNTHETIC-QA-INTERNAL-LIST.txt', 'storage_key' => 'synthetic-qa/'.$doc, 'media_type' => 'text/plain', 'sha256' => $hash, 'size_bytes' => strlen('SYNTHETIC QA private source'), 'uploaded_by' => $this->actor->id, 'status' => 'active', 'data_classification' => 'internal', 'malware_scan_status' => 'clean', 'malware_scanned_sha256' => $hash]);
|
|
DB::table('opportunity_documents')->insert(['id' => (string) Str::uuid(), 'opportunity_id' => $o->id, 'document_id' => $doc, 'version' => 1, 'created_by' => $this->actor->id, 'payload' => json_encode(['title' => 'SYNTHETIC QA internal list', 'visibility' => 'internal'])]);
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$a['evidence'][0]['document_id'] = $doc;
|
|
$a['evidence'][0]['access'] = 'public';
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA original document restrictions cannot be overridden here'])->assertUnprocessable();
|
|
$this->assertSame('internal', DB::table('documents')->where('id', $doc)->value('data_classification'));
|
|
}
|
|
|
|
public function test_selected_public_relationship_and_assumptions_travel_without_private_reference_or_duplicate(): void
|
|
{
|
|
$o = $this->create();
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$target = app(OpportunityClassification::class)->targets('project')->first(['id', 'name']);
|
|
$this->assertNotNull($target);
|
|
$r = ['id' => (string) Str::uuid(), 'kind' => 'project', 'target_id' => $target->id, 'relation' => 'hypothesis', 'reference' => 'PRIVATE-QA-RELATION-REFERENCE-NOT-FOR-PUBLICATION', 'evidence_ids' => [$a['evidence'][0]['id']]];
|
|
$a['relations'] = [$r];
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA reference is a hypothesis, not access or contract'])->assertOk();
|
|
$o->refresh();
|
|
$bad = $a;
|
|
$bad['relations'][] = [...$r, 'id' => (string) Str::uuid()];
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $bad, 'reason' => 'SYNTHETIC QA same reference cannot appear twice in the same analysis'])->assertUnprocessable();
|
|
$d = $this->publication($o);
|
|
$d['proactive_public'] = [...$d['proactive_public'], 'relation_ids' => [$r['id']], 'resources' => 'SYNTHETIC QA public resource, no guaranteed capacity.', 'assumptions' => 'No contract, land or power guaranteed.', 'conditions' => 'Confirm actual local evidence.'];
|
|
$p = $this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/previa', $d)->assertOk()->json();
|
|
$this->assertSame($target->name, $p['projection']['proactive']['relations'][0]['label']);
|
|
$this->assertStringNotContainsString('PRIVATE-QA-RELATION', json_encode($p['projection']));
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/confirmar', ['token' => $p['token'], 'operation_key' => (string) Str::uuid(), 'confirm' => true, 'reason' => 'SYNTHETIC QA explicitly reviewed public relationship and assumptions'])->assertOk();
|
|
$ref = app(InvestorContext::class)->reference('opportunity:'.$o->id);
|
|
$this->assertSame('hypothesis', $ref['analysis']['proactive']['relations'][0]['relationship']);
|
|
$this->assertSame('No contract, land or power guaranteed.', $ref['analysis']['proactive']['assumptions']);
|
|
}
|
|
|
|
public function test_publication_needs_explicit_review_and_rejects_internal_source_and_literal_leakage(): void
|
|
{
|
|
$o = $this->create();
|
|
$data = $this->publication($o);
|
|
unset($data['proactive_reviewed']);
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/previa', $data)->assertUnprocessable();
|
|
$data = $this->publication($o);
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$data['proactive_public']['evidence_ids'] = [$a['evidence'][1]['id']];
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/previa', $data)->assertUnprocessable();
|
|
$data = $this->publication($o, ['description' => $a['evidence'][1]['statement']]);
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/previa', $data)->assertUnprocessable();
|
|
$data = $this->publication($o);
|
|
$data['proactive_public']['state'] = 'confirmed';
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/previa', $data)->assertUnprocessable();
|
|
}
|
|
|
|
public function test_public_snapshot_separate_from_private_edits_and_anonymous_access_no_coordinates_required(): void
|
|
{
|
|
$o = $this->create();
|
|
$private = $o->management_draft;
|
|
$confirm = $this->publish($o);
|
|
$this->postJson('/minhas-oportunidades/'.$o->id.'/divulgacao/confirmar', $confirm)->assertOk()->assertJsonPath('replayed', true);
|
|
$o->refresh();
|
|
$this->assertSame($private, $o->management_draft);
|
|
$this->assertFalse(app(ProactiveInvestment::class)->publicData($o)['economic_validation']);
|
|
$a = app(OpportunityManagement::class)->values($o)['proactive'];
|
|
$a['idea']['solution'] = 'PRIVATE-QA-NEW-UNPUBLISHED-SOLUTION';
|
|
$this->putJson('/minhas-oportunidades/'.$o->id.'/draft', ['etag' => app(OpportunityManagement::class)->etag($o), 'proactive' => $a, 'reason' => 'SYNTHETIC QA private update must not change published projection'])->assertOk();
|
|
$this->app['auth']->forgetGuards();
|
|
$this->get('/oportunidades/'.$o->slug)->assertOk()->assertDontSee('PRIVATE-QA-');
|
|
$this->get('/minhas-oportunidades/'.$o->id.'/edit')->assertRedirect('/login');
|
|
$this->assertNotNull(app(ProactiveInvestment::class)->publicData($o->fresh()));
|
|
}
|
|
|
|
public function test_three_synthetic_pilots_reuse_existing_territories_and_catalogue_groups(): void
|
|
{
|
|
foreach (['Cahora Bassa', 'Montepuez', 'Gurué'] as $pilot) {
|
|
$o = $this->create($pilot);
|
|
$this->publish($o);
|
|
$this->assertNotNull($o->fresh()->primary_territory_id);
|
|
$this->assertTrue(app(InvestmentCatalogue::class)->query(['perspective' => 'ideas'])->whereKey($o->id)->exists());
|
|
$this->assertFalse(app(InvestmentCatalogue::class)->query(['perspective' => 'needs'])->whereKey($o->id)->exists());
|
|
}
|
|
$this->get('/oportunidades?perspective=ideas')->assertOk();
|
|
}
|
|
|
|
public function test_existing_facts_and_needs_do_not_require_irrelevant_idea_fields(): void
|
|
{
|
|
foreach (['exists', 'needs'] as $kind) {
|
|
$input = F::input();
|
|
$input['proactive']['kind'] = $kind;
|
|
$id = $this->postJson('/minhas-oportunidades/drafts', $input)->assertCreated()->json('id');
|
|
$o = Opportunity::findOrFail($id);
|
|
$d = $this->publication($o);
|
|
unset($d['proactive_public']['proposal']);
|
|
if ($kind === 'exists') {
|
|
unset($d['proactive_public']['need']);
|
|
}
|
|
$p = $this->postJson('/minhas-oportunidades/'.$id.'/divulgacao/previa', $d)->assertOk()->json();
|
|
$this->assertSame($kind, $p['projection']['proactive']['group']);
|
|
$this->assertSame('', $p['projection']['proactive']['proposal']);
|
|
$this->postJson('/minhas-oportunidades/'.$id.'/divulgacao/confirmar', ['token' => $p['token'], 'operation_key' => (string) Str::uuid(), 'confirm' => true, 'reason' => 'SYNTHETIC QA only applicable questions for this content group'])->assertOk();
|
|
$this->assertTrue(app(InvestmentCatalogue::class)->query(['perspective' => $kind])->whereKey($id)->exists());
|
|
}
|
|
}
|
|
|
|
public function test_private_analysis_complements_keep_preparation_valid_without_inventing_public_offers(): void
|
|
{
|
|
$private = $this->create('Gurué');
|
|
$public = $this->create();
|
|
$input = $this->publication($public, ['summary' => 'SYNTHETIC QA: silos e armazenagem como componente documentada.']);
|
|
$plan = $this->postJson('/minhas-oportunidades/'.$public->id.'/divulgacao/previa', $input)->assertOk()->json();
|
|
$this->postJson('/minhas-oportunidades/'.$public->id.'/divulgacao/confirmar', ['token' => $plan['token'], 'operation_key' => (string) Str::uuid(), 'confirm' => true, 'reason' => 'SYNTHETIC QA public component in transaction only'])->assertOk();
|
|
$result = $this->postJson('/complementos-investimento', ['contexts' => [['origin' => 'opportunity:'.$private->id], ['origin' => 'opportunity:'.$public->id]]])->assertOk()->json();
|
|
$this->assertSame([], $result['issues']);
|
|
$this->assertNotEmpty($result['items']);
|
|
foreach ($result['items'] as $item) {
|
|
$this->assertSame('opportunity:'.$public->id, $item['origin']['key']);
|
|
}
|
|
$this->assertStringNotContainsString('PRIVATE-QA-', json_encode($result));
|
|
$this->assertFalse(Opportunity::published()->whereKey($private->id)->exists());
|
|
$this->assertSame('draft', $private->fresh()->status);
|
|
}
|
|
|
|
public function test_private_analysis_complements_still_hide_unauthorised_sources(): void
|
|
{
|
|
$private = $this->create('Gurué');
|
|
$this->login(User::factory()->create());
|
|
$this->postJson('/complementos-investimento', ['origin' => 'opportunity:'.$private->id])->assertNotFound();
|
|
$result = $this->postJson('/complementos-investimento', ['contexts' => [['origin' => 'opportunity:'.$private->id]]])->assertOk()->json();
|
|
$this->assertSame([], $result['items']);
|
|
$this->assertCount(1, $result['issues']);
|
|
$this->assertStringNotContainsString($private->title, json_encode($result));
|
|
$this->assertStringNotContainsString('PRIVATE-QA-', json_encode($result));
|
|
}
|
|
|
|
public function test_authenticated_multi_idea_preparation_keeps_each_origin_and_deliberate_edits_in_existing_dossier(): void
|
|
{
|
|
$first = $this->create();
|
|
$second = $this->create('Gurué');
|
|
$this->publish($first);
|
|
$this->publish($second);
|
|
$investor = User::factory()->create();
|
|
$this->login($investor);
|
|
$key = (string) Str::uuid();
|
|
$create = ['title' => 'SYNTHETIC QA deliberately chosen project title', 'client_key' => $key, 'reuse_consent' => true];
|
|
$id = $this->postJson('/investimentos', $create)->assertCreated()->json('id');
|
|
$this->postJson('/investimentos', $create)->assertCreated()->assertJsonPath('id', $id);
|
|
$before = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
|
|
$input = ['version' => $before['case']['version'], 'selection' => [], 'opportunity_choices' => array_map(fn ($o) => ['source' => 'opportunity:'.$o->id, 'territory' => 'territory:'.$o->primary_territory_id], [$first, $second])];
|
|
$preview = $this->postJson('/investimentos/'.$id.'/contexto/previa', $input)->assertOk()->json();
|
|
$this->assertCount(2, $preview['opportunity_choices']);
|
|
$this->assertSame('SYNTHETIC QA deliberately chosen project title', $preview['fields']['project_title']);
|
|
foreach ($preview['opportunity_choices'] as $c) {
|
|
$this->assertSame('ideas', $c['opportunity']['analysis']['proactive']['group']);
|
|
$this->assertStringNotContainsString('PRIVATE-QA-', json_encode($c));
|
|
}
|
|
$confirm = [...$input, 'preview_hash' => $preview['preview_hash'], 'operation_key' => (string) Str::uuid(), 'confirm' => true];
|
|
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', $confirm)->assertOk();
|
|
$this->postJson('/investimentos/'.$id.'/contexto/confirmar', $confirm)->assertOk()->assertJsonPath('replayed', true);
|
|
$saved = $this->getJson('/investimentos/'.$id.'/contexto')->assertOk()->json();
|
|
$this->assertCount(2, $saved['context']['opportunity_choices']);
|
|
$consumers = [];
|
|
foreach ([$first, $second] as $record) {
|
|
$consumer = $this->getJson('/investimentos/'.$id.'/contexto?opportunity=opportunity:'.$record->id)->assertOk()->json();
|
|
$snapshot = json_decode($consumer['prefill']['simulator']['context_applicability_snapshot'], true);
|
|
$this->assertCount(1, $snapshot['opportunity_choices']);
|
|
$this->assertSame('opportunity:'.$record->id, $snapshot['opportunity_choices'][0]['opportunity']['key']);
|
|
$this->assertSame('ideas', $snapshot['opportunity_choices'][0]['opportunity']['analysis']['proactive']['group']);
|
|
$this->assertArrayNotHasKey('tax_benefit', $consumer['prefill']['simulator']);
|
|
$consumers[] = $consumer;
|
|
}$saved['consumer_contexts'] = $consumers;
|
|
$evidenceDirectory = base_path('outputs/proactive-investment-20261027');
|
|
if (! is_dir($evidenceDirectory)) {
|
|
mkdir($evidenceDirectory, 0777, true);
|
|
}
|
|
file_put_contents($evidenceDirectory.'/recovered-context.json', json_encode($saved, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
|
|
$this->get('/investimentos/'.$id)->assertOk();
|
|
$this->login($this->actor);
|
|
$this->getJson('/investimentos/'.$id.'/contexto')->assertForbidden();
|
|
}
|
|
}
|