assertSame('simulator_fiscal_delta_20261021_test', $target->name); $this->assertSame(55441, (int) $target->port); config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]); $this->actingAs(User::factory()->create(['email_verified_at' => now()])); $draft = json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-fx-selection.json')), true, flags: JSON_THROW_ON_ERROR); $id = $this->postJson('/api/v1/simulation-drafts', ['personal_snapshot' => [], 'economic_snapshot' => $draft], ['Idempotency-Key' => (string) Str::uuid()])->assertCreated()->json('data.id'); $recovered = $this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->json('data.economic_snapshot'); $this->assertSame($draft['modelo_a_snapshot'], $recovered['modelo_a_snapshot']); $s = json_decode($recovered['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR); $b = $s['currencyEvents']['opportunity:365']; $this->assertCount(1, $b['history']); $this->assertSame('6406.9775', $b['history'][0]['rows'][0]['scenarioAmount']); $this->assertSame('12782.00', $b['result']['rows'][0]['scenarioAmount']); $this->assertSame('mean', $b['rows'][0]['fxSelection']['applied']['column']); $this->assertNull($b['result']['rows'][0]['baseAmount']); $this->assertNull($b['result']['rows'][0]['vat']); $prior = json_decode(json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-offer-workflow.json')), true)['modelo_a_snapshot'], true); $this->assertSame(array_diff_key($prior, ['currencyEvents' => true]), array_diff_key($s, ['currencyEvents' => true])); foreach ($prior['currencyEvents'] as $k => $v) { $this->assertSame($v, $s['currencyEvents'][$k]); } file_put_contents(base_path('outputs/iva-l1-returnees-20261025/fx-recovered.json'), json_encode($recovered, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)); } public function test_private_decision_attachment_and_recovery_preserve_prior_books(): void { $target = DB::selectOne('select current_database() as name,inet_server_port() as port'); $this->assertSame('simulator_fiscal_delta_20261021_test', $target->name); $this->assertSame(55441, (int) $target->port); config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]); Queue::fake(); Storage::fake('local'); $owner = User::factory()->create(['email_verified_at' => now()]); $this->actingAs($owner); $draft = json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-offer-workflow.json')), true, flags: JSON_THROW_ON_ERROR); $id = $this->postJson('/api/v1/simulation-drafts', ['personal_snapshot' => [], 'economic_snapshot' => $draft], ['Idempotency-Key' => (string) Str::uuid()])->assertCreated()->json('data.id'); $file = UploadedFile::fake()->createWithContent('SYNTHETIC-QA-despacho.txt', 'SYNTHETIC QA despacho privado para teste de continuidade.'); $url = '/api/v1/simulation-drafts/'.$id.'/offer-documents'; $doc = $this->post($url, ['file' => $file], ['Accept' => 'application/json'])->assertCreated()->json('data'); $this->assertSame(hash('sha256', 'SYNTHETIC QA despacho privado para teste de continuidade.'), $doc['sha256']); Queue::assertPushed(ScanDocumentForMalware::class); $this->post($url, ['file' => UploadedFile::fake()->createWithContent('copy.txt', 'SYNTHETIC QA despacho privado para teste de continuidade.')], ['Accept' => 'application/json'])->assertOk()->assertJsonPath('data.id', $doc['id']); $this->getJson($doc['url'])->assertStatus(409); $this->getJson($url)->assertOk()->assertJsonCount(1, 'data'); $this->actingAs(User::factory()->create(['email_verified_at' => now()])); $this->getJson($doc['url'])->assertNotFound(); $this->getJson($url)->assertNotFound(); $this->post($url, ['file' => UploadedFile::fake()->createWithContent('other.txt', 'SYNTHETIC QA other')], ['Accept' => 'application/json'])->assertNotFound(); $this->actingAs($owner); DB::table('documents')->where('id', $doc['id'])->update(['status' => 'active', 'malware_scan_status' => 'clean', 'malware_scanned_sha256' => $doc['sha256']]); $this->get($doc['url'])->assertOk()->assertHeader('X-Content-Type-Options', 'nosniff'); $recovered = $this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->json('data.economic_snapshot'); $this->assertSame($draft['modelo_a_snapshot'], $recovered['modelo_a_snapshot']); $snapshot = json_decode($recovered['modelo_a_snapshot'], true, flags: JSON_THROW_ON_ERROR); $this->assertCount(2, $snapshot['offerPreparations']['opportunity:364']['rows']); $prior = json_decode(json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-returnees.json')), true)['modelo_a_snapshot'], true); $this->assertSame(array_diff_key($prior, ['offerPreparations' => true]), array_diff_key($snapshot, ['offerPreparations' => true])); foreach ($prior['offerPreparations'] as $k => $v) { $this->assertSame($v, $snapshot['offerPreparations'][$k]); } // The UI seed contains the real uploaded private decision identity, not a synthetic URL passed as an upload. foreach ($snapshot['offerPreparations']['opportunity:364']['rows'] as &$row) { $row['workflow']['decisionAttachment'] = $doc; } unset($row); $recovered['modelo_a_snapshot'] = json_encode($snapshot, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); file_put_contents(base_path('outputs/iva-l1-returnees-20261025/recovered.json'), json_encode($recovered, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)); $stored = DB::table('documents')->where('id', $doc['id'])->first(); Storage::disk('local')->put($stored->storage_key, 'changed'); $this->getJson($doc['url'])->assertStatus(409); } }