Files
Investidor-V2/tests/Feature/LegacyContentTest.php
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

151 lines
10 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Services\LegacyContent;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Inertia\Testing\AssertableInertia as Assert;
use Tests\TestCase;
class LegacyContentTest extends TestCase
{
use RefreshDatabase;
private int $resource;
protected function setUp(): void
{
parent::setUp();
$this->withoutVite();
Storage::fake('local');
config(['portal.legacy_content_qa_enabled' => true, 'investor-path.enabled' => true]);
$bytes = json_encode(['format' => 'investmoz-legacy-article-v1', 'source_url' => 'https://investemoz.tempo.co.mz/agronegocio',
'content_html' => '<p>Conhecer corredores</p><script>secret()</script><a href="javascript:alert(1)" onclick="bad()">Link</a>',
'title' => 'Agronegocio', 'author' => 'Autor sintético', 'legacy_id' => 171985, 'legacy_category' => 'Oportunidades',
'published_at' => '2026-04-06T09:19:07+00:00', 'modified_at' => '2026-04-20T12:41:43+00:00'], JSON_THROW_ON_ERROR);
$id = (string) Str::uuid();
Storage::disk('local')->put('legacy/'.$id, $bytes);
DB::table('documents')->insert(['id' => $id, 'source_system' => 'synthetic-test', 'external_id' => 'legacy-tempo-post-171985-20260420',
'file_name' => 'source.json', 'media_type' => 'application/json', 'storage_key' => 'legacy/'.$id,
'sha256' => hash('sha256', $bytes), 'malware_scanned_sha256' => hash('sha256', $bytes), 'malware_scan_status' => 'clean',
'size_bytes' => strlen($bytes), 'status' => 'active', 'rights_status' => 'authorized', 'data_classification' => 'public']);
$this->resource = DB::table('portal_resources')->insertGetId(['document_id' => $id, 'title' => 'Agronegocio',
'category' => 'education', 'topic' => 'agronegocio', 'published' => true, 'rights_status' => 'authorized',
'source_url' => 'https://investemoz.tempo.co.mz/agronegocio', 'source_reference' => 'Exemplar sintético; não oficial.', 'metadata_version' => 1]);
}
public function test_public_read_search_and_sanitization_do_not_create_opportunities_and_private_sources_are_hidden(): void
{
$count = DB::table('opportunities')->count();
$this->get('/agronegocio')->assertOk()->assertInertia(fn (Assert $page) => $page->component('portal/legacy')
->where('article.id', $this->resource)->where('article.calculation_use', false));
$html = app(LegacyContent::class)->article()['html'];
$this->assertStringNotContainsString('script', $html);
$this->assertStringNotContainsString('javascript', $html);
$this->assertStringNotContainsString('onclick', $html);
$this->get('/explorar/agronegocio?q=corredores')->assertOk()->assertInertia(fn (Assert $page) => $page->where('match', true));
$this->get('/explorar/agronegocio?q=absent')->assertOk()->assertInertia(fn (Assert $page) => $page->where('match', false));
$this->getJson('/investimentos/a-partir-de/agronegocio')->assertUnauthorized();
$this->assertSame($count, DB::table('opportunities')->count());
DB::table('portal_resources')->where('id', $this->resource)->update(['published' => false]);
$this->get('/agronegocio')->assertNotFound();
config(['portal.legacy_content_qa_enabled' => false]);
$this->get('/explorar/agronegocio')->assertNotFound();
}
public function test_original_heading_text_is_preserved_with_an_accessible_article_hierarchy(): void
{
$source = '<h3>Turismo</h3><h3>Territórios</h3><h4>Localizações</h4><script>unsafe()</script>';
$html = app(LegacyContent::class)->sanitize($source);
$this->assertSame('<h2>Turismo</h2><h2>Territórios</h2><h3>Localizações</h3>', $html);
$this->assertSame('<h2>Secção</h2><h3>Subsecção</h3>', app(LegacyContent::class)->sanitize('<h1>Secção</h1><h3>Subsecção</h3>'));
$this->assertStringContainsString('<h3>Turismo</h3>', $source);
}
public function test_source_is_saved_once_in_existing_dossier_recovered_and_isolated_from_other_accounts_and_projects(): void
{
$owner = User::factory()->create();
$other = User::factory()->create();
$data = ['title' => 'Ideia sintética', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true, 'content_resource_id' => $this->resource];
$case = $this->actingAs($owner)->postJson('/investimentos', $data)->assertCreated()->json('id');
$first = $this->getJson('/investimentos/'.$case)->assertOk()->json();
$this->assertCount(19, $first['steps']);
$this->assertCount(5, $first['phases']);
$this->assertSame($this->resource, $first['content_sources'][0]['resource_id']);
$this->postJson('/investimentos/'.$case.'/conteudos', ['version' => $first['case']['version'], 'resource_id' => $this->resource])->assertOk();
$again = $this->getJson('/investimentos/'.$case)->assertOk()->json();
$this->assertSame($first['case']['version'], $again['case']['version']);
$this->assertSame($first['content_sources'], $again['content_sources']);
$this->assertSame($case, $this->postJson('/investimentos', $data)->assertCreated()->json('id'));
$this->actingAs($other)->getJson('/investimentos/'.$case)->assertForbidden();
$this->postJson('/investimentos/'.$case.'/conteudos', ['version' => $first['case']['version'], 'resource_id' => $this->resource])->assertForbidden();
$this->actingAs($owner);
unset($data['content_resource_id']);
$data['client_key'] = (string) Str::uuid();
$independent = $this->postJson('/investimentos', $data)->assertCreated()->json('id');
$this->assertSame([], $this->getJson('/investimentos/'.$independent)->assertOk()->json('content_sources'));
$before = DB::table('desk_cases')->count();
$data['client_key'] = (string) Str::uuid();
$data['content_resource_id'] = $this->resource + 100;
$this->postJson('/investimentos', $data)->assertUnprocessable();
$this->assertSame($before, DB::table('desk_cases')->count());
}
private function sectorArticle(string $slug, string $title, string $body): int
{
$identity = config('legacy_content.articles.'.$slug);
$document = (string) Str::uuid();
$bytes = json_encode(['format' => 'investmoz-legacy-article-v1', 'source_url' => $identity['source_url'],
'content_html' => $body, 'title' => $title, 'author' => 'Autor sintético', 'legacy_id' => 123,
'legacy_category' => 'Turismo', 'published_at' => '2026-04-01T00:00:00+00:00', 'modified_at' => '2026-04-02T00:00:00+00:00'], JSON_THROW_ON_ERROR);
Storage::disk('local')->put('legacy/'.$document, $bytes);
DB::table('documents')->insert(['id' => $document, 'source_system' => 'synthetic-test', 'external_id' => $identity['external_id'],
'file_name' => 'source.json', 'media_type' => 'application/json', 'storage_key' => 'legacy/'.$document,
'sha256' => hash('sha256', $bytes), 'malware_scanned_sha256' => hash('sha256', $bytes), 'malware_scan_status' => 'clean',
'size_bytes' => strlen($bytes), 'status' => 'active', 'rights_status' => 'authorized', 'data_classification' => 'public']);
return DB::table('portal_resources')->insertGetId(['document_id' => $document, 'title' => $title, 'category' => 'education',
'topic' => $identity['sector_slug'], 'published' => true, 'rights_status' => 'authorized',
'source_url' => $identity['source_url'], 'source_reference' => 'Exemplar sintético.', 'metadata_version' => 1]);
}
public function test_energy_and_mining_remain_distinct_and_each_reference_returns_to_its_article(): void
{
$this->sectorArticle('energia-mineracao', 'Energia e Mineração', '<a href="https://investemoz.tempo.co.mz/energia/">Energia</a><a href="https://investemoz.tempo.co.mz/mineracao">Mineração</a>');
$energy = $this->sectorArticle('energia', 'Energia', '<p>Rede e geração.</p>');
$mining = $this->sectorArticle('mineracao', 'Mineração', '<p>Recursos minerais.</p>');
$this->get('/explorar/energia-mineracao?q=geração')->assertOk()->assertInertia(fn (Assert $page) => $page->has('articles', 1)->where('articles.0.id', $energy));
$index = app(LegacyContent::class)->article('energia-mineracao');
$this->assertStringContainsString('href="/energia"', $index['html']);
$this->assertStringContainsString('href="/mineracao"', $index['html']);
$owner = User::factory()->create();
$case = $this->actingAs($owner)->postJson('/investimentos', ['title' => 'Energia sintética', 'client_key' => (string) Str::uuid(),
'reuse_consent' => true, 'content_resource_id' => $energy])->assertCreated()->json('id');
$first = $this->getJson('/investimentos/'.$case)->assertOk()->json();
$this->postJson('/investimentos/'.$case.'/conteudos', ['version' => $first['case']['version'], 'resource_id' => $mining])->assertOk();
$sources = $this->getJson('/investimentos/'.$case)->assertOk()->json('content_sources');
$this->assertSame(['/energia', '/mineracao'], array_column($sources, 'return_href'));
$this->assertSame([$energy, $mining], array_column($sources, 'resource_id'));
DB::table('portal_resources')->where('id', $mining)->update(['published' => false]);
$this->get('/mineracao')->assertNotFound();
$this->assertStringContainsString('https://investemoz.tempo.co.mz/mineracao', app(LegacyContent::class)->article('energia-mineracao')['html']);
}
public function test_tourism_is_textual_and_empty_financial_source_is_not_a_published_article(): void
{
$this->sectorArticle('turismo', 'Turismo', '<p>Província da Zambézia e turismo.</p>');
$tourism = app(LegacyContent::class)->article('turismo');
$this->assertSame([], $tourism['territorial_references']);
$this->assertFalse(DB::table('portal_resource_links')->where('target_type', 'territory')->exists());
$finance = $this->sectorArticle('mercado-financeiro', 'Mercado Financeiro', '');
$this->get('/mercado-financeiro')->assertNotFound();
$this->get('/explorar/mercado-financeiro')->assertNotFound();
$this->actingAs(User::factory()->create())->postJson('/investimentos', ['title' => 'Ideia', 'client_key' => (string) Str::uuid(),
'reuse_consent' => true, 'content_resource_id' => $finance])->assertUnprocessable();
}
}