assertSame('simulator_fiscal_delta_20261021_test', $db->name); $this->assertSame(55441, (int) $db->port); $this->withoutVite(); $identities = json_decode(file_get_contents(config_path('legacy_editorial_resources.json')), true); $this->resources = DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->whereIn('d.external_id', array_column($identities, 'external_id'))->orderBy('r.id')->get(['r.*']); $this->assertCount(28, $this->resources); $originalsRoot = getenv('INVESTMOZ_QA_ORIGINALS_ROOT'); if (is_string($originalsRoot) && $originalsRoot !== '') { Storage::fake('local'); foreach (DB::table('documents')->whereIn('id', $this->resources->pluck('document_id'))->get() as $document) { $source = $originalsRoot.'/'.$document->storage_key; $this->assertFileExists($source); $this->assertSame(trim($document->sha256), hash_file('sha256', $source)); Storage::disk('local')->put($document->storage_key, file_get_contents($source)); } } $this->editor = User::factory()->create(); config(['portal.managers' => [$this->editor->email]]); $this->actingAs($this->editor); } private function payload(object $r, string $body): array { return ['metadata_version' => (int) $r->metadata_version, 'body' => $body, 'image_mode' => 'original', 'image_resource_id' => null]; } public function test_all_28_open_from_existing_originals_and_private_previews_without_reimport_or_save(): void { $before = DB::table('portal_resource_revisions')->count(); $service = app(EditorialReading::class); foreach ($this->resources as $r) { $original = $service->original($r); $this->assertNotEmpty($original['content_html']); $text = fn (string $html) => preg_replace('/\s+/u', '', html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8')); $this->assertSame($text($original['content_html']), $text(app(LegacyContent::class)->sanitize($original['content_html'], true)), 'Reader must preserve the original text, including inline spans and table cells.'); $this->assertStringStartsWith('https://investemoz.tempo.co.mz/', $service->reading($r, null, true)['image_source_url']); $this->get('/management/portal-services/resources/'.$r->id.'/article')->assertOk()->assertHeader('Cache-Control', 'no-store, private')->assertInertia(fn (Assert $p) => $p->where('source.legacy_id', $original['legacy_id'])->where('content.body', $service->content($r, $original)['body'])); $this->get('/management/portal-services/resources/'.$r->id.'/article/preview')->assertOk()->assertHeader('X-Robots-Tag', 'noindex, nofollow')->assertInertia(fn (Assert $p) => $p->where('article.private_preview', true)->where('article.source_url', $original['source_url'])->where('article.legacy_id', $original['legacy_id'])); $this->get('/artigos/'.$r->id)->assertNotFound(); } $this->assertSame($before, DB::table('portal_resource_revisions')->count()); } public function test_review_changes_save_recover_versions_and_stale_previews_preserve_original(): void { $r = $this->resources->first(fn ($item) => ! $item->editorial_content); $original = app(EditorialReading::class)->original($r); $sha = DB::table('documents')->where('id', $r->document_id)->value('sha256'); $payload = $this->payload($r, 'Correcção editorial sintética QA; preservar o original e não publicar.'); $this->postJson('/management/portal-services/resources/'.$r->id.'/article/preview', $payload)->assertOk()->assertJsonPath('article.body', $payload['body']); $this->assertNull(DB::table('portal_resources')->where('id', $r->id)->value('editorial_content')); $this->put('/management/portal-services/resources/'.$r->id.'/article', $payload)->assertSessionHasNoErrors()->assertRedirect(); $this->get('/management/portal-services/resources/'.$r->id.'/article')->assertInertia(fn (Assert $p) => $p->where('resource.metadata_version', (int) $r->metadata_version + 1)->where('content.body', $payload['body'])); $this->postJson('/management/portal-services/resources/'.$r->id.'/article/preview', $payload)->assertConflict(); $this->put('/management/portal-services/resources/'.$r->id.'/article', $payload)->assertConflict(); $payload['metadata_version']++; $payload['body'] .= ' Segunda revisão.'; $this->put('/management/portal-services/resources/'.$r->id.'/article', $payload)->assertSessionHasNoErrors()->assertRedirect(); $this->get('/management/portal-services/resources/'.$r->id.'/article/preview')->assertInertia(fn (Assert $p) => $p->where('article.body', $payload['body'])->where('article.source_url', $original['source_url'])); $this->assertSame($sha, DB::table('documents')->where('id', $r->document_id)->value('sha256')); $this->assertSame($original, app(EditorialReading::class)->original($r)); $this->assertFalse((bool) DB::table('portal_resources')->where('id', $r->id)->value('published')); $this->assertGreaterThanOrEqual(2, DB::table('portal_resource_revisions')->where('resource_id', $r->id)->count()); } public function test_publication_remains_explicit_and_reader_works_before_first_editorial_save(): void { $r = $this->resources->first(fn ($item) => ! $item->editorial_content); $original = app(EditorialReading::class)->original($r); $this->post('/management/portal-services/resources/'.$r->id, ['published' => true])->assertSessionHasErrors(); $document = DB::table('documents')->find($r->document_id); $metadata = ['document_id' => $r->document_id, 'title' => $r->title, 'category' => $r->category, 'topic' => $r->topic, 'issuer' => $document->issuer, 'document_type' => $r->document_type, 'language' => 'pt', 'description' => 'Arquivo editorial QA. Sem oportunidade concreta ou regra activa.', 'source_url' => $r->source_url, 'source_reference' => $r->source_reference, 'source_confirmation' => 'identified', 'legal_status' => 'historical', 'rights_status' => 'authorized', 'data_classification' => 'public', 'metadata_version' => $r->metadata_version, 'associations' => [], 'territorial_scope' => 'unknown', 'independent_review' => false, 'legislation' => false, 'essential' => false]; $this->put('/management/portal-services/resources/'.$r->id, $metadata)->assertSessionHasNoErrors()->assertRedirect(); $this->get('/artigos/'.$r->id)->assertNotFound(); $this->post('/management/portal-services/resources/'.$r->id, ['published' => true])->assertSessionHasNoErrors()->assertRedirect(); Auth::logout(); $this->app['auth']->forgetGuards(); $this->get('/artigos/'.$r->id)->assertOk()->assertInertia(fn (Assert $p) => $p->where('article.legacy_id', $original['legacy_id'])->where('article.private_preview', false)); $this->get('/documentos/'.$r->id)->assertOk()->assertInertia(fn (Assert $p) => $p->where('articleUrl', '/artigos/'.$r->id)); $this->actingAs($this->editor); $current = DB::table('portal_resources')->find($r->id); $this->put('/management/portal-services/resources/'.$r->id.'/article', $this->payload($current, 'Edição sintética depois da publicação; revisão expressa necessária.'))->assertRedirect(); $this->get('/artigos/'.$r->id)->assertNotFound(); } public function test_named_editorial_scope_reuses_28_exact_original_identities_and_mfa_without_global_grant(): void { $actor = $this->editor; $other = User::factory()->create(); $actor->forceFill(['two_factor_secret' => Crypt::encryptString('JBSWY3DPEHPK3PXP'), 'two_factor_confirmed_at' => now()])->save(); config(['portal.profile_access_enabled' => true, 'portal.managers' => [], 'homologation_editorial.accounts' => [$actor->email]]); DB::table('portal_superuser_pair')->updateOrInsert(['id' => 1], ['first_user_id' => $actor->id, 'second_user_id' => $other->id, 'approved_by' => $other->id]); $this->withSession(['portal.mfa_actor' => $actor->id, 'portal.mfa_at' => time(), 'portal.access_revision' => (int) $actor->portal_access_revision]); $this->get('/management/portal-services/resources/'.$this->resources->first()->id.'/article/preview')->assertOk(); $this->assertFalse($actor->can('manage-portal')); DB::table('documents')->where('id', $this->resources->first()->document_id)->update(['external_id' => 'destination-upload-qa-identity', 'source_system' => 'portal-services']); $this->get('/management/portal-services/resources/'.$this->resources->first()->id.'/article/preview')->assertOk(); $this->get('/management/portal-services/resources/38/article/preview')->assertForbidden(); $this->withSession(['portal.mfa_at' => time() - 901])->get('/management/portal-services/resources/'.$this->resources->first()->id.'/article/preview')->assertForbidden(); $this->actingAs($other)->get('/management/portal-services/resources/'.$this->resources->first()->id.'/article/preview')->assertForbidden(); } public function test_structured_original_is_sanitized_and_unavailable_images_are_explicit(): void { $html = app(LegacyContent::class)->sanitize('
| Conteúdo |