206 lines
12 KiB
PHP
206 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\InterestRequest;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
use Tests\Support\AuthenticatesQaActors;
|
|
use Tests\TestCase;
|
|
|
|
class BusinessMarketplacePublicTest extends TestCase
|
|
{
|
|
use AuthenticatesQaActors;
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
config(['existing-business.enabled' => true]);
|
|
}
|
|
|
|
public function test_public_catalogue_and_detail_exclude_reserved_fields_and_locations(): void
|
|
{
|
|
$business = $this->publishedBusiness();
|
|
|
|
$this->getJson('/api/v1/business-opportunities')->assertOk()
|
|
->assertJsonPath('data.0.title', 'Public synthetic opportunity')
|
|
->assertJsonMissingPath('data.0.owner_user_id')
|
|
->assertJsonMissingPath('data.0.payload')
|
|
->assertJsonMissingPath('data.0.geom');
|
|
$this->getJson('/api/v1/business-opportunities/'.$business['public_slug'])->assertOk()
|
|
->assertJsonPath('data.title', 'Public synthetic opportunity')
|
|
->assertJsonMissingPath('data.payload')
|
|
->assertJsonMissingPath('data.owner_user_id');
|
|
}
|
|
|
|
public function test_interest_is_limited_to_published_business_and_deduplicated_idempotently(): void
|
|
{
|
|
$this->withoutVite();
|
|
config(['portal.notifications_enabled' => false]);
|
|
$business = $this->publishedBusiness();
|
|
$payload = [
|
|
'existingBusinessId' => $business['id'],
|
|
'modality' => 'company_sale',
|
|
'fullName' => 'Synthetic investor',
|
|
'email' => 'portal-integration-test@gmail.com',
|
|
'phone' => '+258 840000000',
|
|
'investorType' => 'empresa',
|
|
'country' => 'PT',
|
|
'declared_origin_country_code' => 'ZA',
|
|
'sectorInterest' => 'Energia',
|
|
'message' => 'Synthetic interest for the published business listing.',
|
|
'privacyConsent' => true,
|
|
];
|
|
$key = (string) Str::uuid();
|
|
$this->get('/negocios/'.$business['public_slug'].'/manifestar-interesse')->assertInertia(fn (Assert $p) => $p
|
|
->component('investment/business-interest')->has('countries', 249)->where('record.id', $business['id']));
|
|
$this->postJson('/api/v1/interest-requests', $payload, ['Idempotency-Key' => $key])->assertUnauthorized();
|
|
$this->assertDatabaseCount('interest_requests', 0);
|
|
$this->actingAs(User::factory()->create());
|
|
$this->postJson('/api/v1/interest-requests', [...$payload, 'country' => 'ZZ'], ['Idempotency-Key' => (string) Str::uuid()])->assertUnprocessable();
|
|
$this->assertDatabaseCount('interest_requests', 0);
|
|
|
|
$first = $this->postJson('/api/v1/interest-requests', $payload, ['Idempotency-Key' => $key])->assertCreated();
|
|
$second = $this->postJson('/api/v1/interest-requests', $payload, ['Idempotency-Key' => $key])->assertCreated();
|
|
$this->assertSame($first->json(), $second->json());
|
|
$this->assertDatabaseHas('interest_requests', [
|
|
'existing_business_id' => $business['id'],
|
|
'modality' => 'company_sale',
|
|
'status' => 'received',
|
|
]);
|
|
$this->assertDatabaseCount('interest_requests', 1);
|
|
$this->assertDatabaseCount('outbox_events', 1);
|
|
|
|
$this->postJson('/api/v1/interest-requests', [...$payload, 'message' => 'Another valid message for this active interest.'], ['Idempotency-Key' => (string) Str::uuid()])
|
|
->assertStatus(409);
|
|
$this->assertDatabaseCount('interest_requests', 1);
|
|
$interest = InterestRequest::firstOrFail();
|
|
$this->assertSame('PT', $interest->country);
|
|
$this->assertSame('ZA', $interest->metadata['declared_origin_country_code']);
|
|
$manager = User::factory()->create();
|
|
config(['portal.managers' => [$manager->email]]);
|
|
$this->actingAsWithMfa($manager)->get('/management/interests/'.$interest->id)->assertInertia(fn (Assert $p) => $p
|
|
->where('interest.country', 'PT')->where('declaredOrigin.code', 'ZA'));
|
|
$this->get('/management/interests?country=PT&origin=ZA')->assertInertia(fn (Assert $p) => $p
|
|
->where('requests.total', 1)->where('originStatistics.interests.0.code', 'ZA')->where('originStatistics.interests.0.count', 1)
|
|
->has('originStatistics.businesses', 0));
|
|
$this->get('/management/interests?origin=PT')->assertInertia(fn (Assert $p) => $p->where('requests.total', 0)->has('originStatistics.interests', 0));
|
|
$this->actingAs(User::factory()->create())->get('/management/interests/'.$interest->id)->assertForbidden();
|
|
}
|
|
|
|
public function test_public_catalogue_filters_by_existing_opportunity_modality_and_opens_response_flow(): void
|
|
{
|
|
$business = $this->publishedBusiness('real_estate');
|
|
$companySale = $this->publishedBusiness('company_sale');
|
|
$landRight = $this->publishedBusiness('land_right');
|
|
|
|
$this->getJson('/api/v1/business-opportunities?modality=real_estate')->assertOk()
|
|
->assertJsonCount(1, 'data')->assertJsonPath('data.0.modality', 'real_estate');
|
|
$this->getJson('/api/v1/business-opportunities?modality=company_sale')->assertOk()
|
|
->assertJsonCount(1, 'data')->assertJsonPath('data.0.modality', 'company_sale');
|
|
$this->getJson('/api/v1/business-opportunities?modality=land_right')->assertOk()
|
|
->assertJsonCount(1, 'data')->assertJsonPath('data.0.modality', 'land_right');
|
|
|
|
$this->get('/negocios?modality=real_estate')->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-marketplace')
|
|
->where('filters.modality', 'real_estate')->has('results.data', 1));
|
|
$this->get('/negocios/'.$business['public_slug'])->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-opportunity')
|
|
->where('record.modality', 'real_estate')
|
|
->where('similarCatalogueUrl', '/negocios?modality=real_estate'));
|
|
$this->get('/negocios/'.$business['public_slug'].'/manifestar-interesse')->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-interest')
|
|
->where('record.modality', 'real_estate'));
|
|
$this->get('/negocios?modality=company_sale')->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-marketplace')
|
|
->where('filters.modality', 'company_sale')->has('results.data', 1));
|
|
$this->get('/negocios/'.$companySale['public_slug'])->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-opportunity')
|
|
->where('record.modality', 'company_sale'));
|
|
$this->get('/negocios?modality=land_right')->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-marketplace')
|
|
->where('filters.modality', 'land_right')->has('results.data', 1));
|
|
$this->get('/negocios/'.$landRight['public_slug'])->assertOk()
|
|
->assertInertia(fn ($page) => $page->component('investment/business-opportunity')
|
|
->where('record.modality', 'land_right'));
|
|
|
|
$this->actingAs(User::factory()->create())->postJson('/api/v1/interest-requests', [
|
|
'existingBusinessId' => $business['id'], 'modality' => 'real_estate',
|
|
'fullName' => 'Synthetic investor', 'email' => 'portal-integration-test@gmail.com', 'phone' => '+258 840000000',
|
|
'investorType' => 'empresa', 'country' => 'MZ', 'sectorInterest' => 'Imobiliário',
|
|
'message' => 'Synthetic interest in the published real estate opportunity.',
|
|
'privacyConsent' => true,
|
|
], ['Idempotency-Key' => (string) Str::uuid()])->assertCreated();
|
|
$this->assertDatabaseHas('interest_requests', ['existing_business_id' => $business['id'], 'modality' => 'real_estate', 'status' => 'received']);
|
|
}
|
|
|
|
public function test_similar_catalogue_uses_only_disclosed_primary_province_and_preserves_publication_limits(): void
|
|
{
|
|
$this->withoutVite();
|
|
$country = (string) Str::uuid();
|
|
$province = (string) Str::uuid();
|
|
DB::table('geo_locations')->insert([
|
|
['id' => $country, 'name' => 'Synthetic country', 'code' => $country, 'location_type' => 'country', 'parent_id' => null],
|
|
['id' => $province, 'name' => 'Synthetic province', 'code' => $province, 'location_type' => 'province', 'parent_id' => $country],
|
|
]);
|
|
foreach ([$country, $province] as $location) {
|
|
DB::table('geo_locations')->where('id', $location)->update([
|
|
'data_classification' => 'public', 'source_system' => 'cartografia-workbook',
|
|
'external_id' => '964a187ad32ea553b9e112cd159caa90b900050c42b7329594a75bcd798a96ff:'.$location,
|
|
]);
|
|
}
|
|
$origin = $this->publishedBusiness();
|
|
$match = $this->publishedBusiness();
|
|
$reserved = $this->publishedBusiness();
|
|
$expired = $this->publishedBusiness();
|
|
foreach ([$origin, $match, $reserved, $expired] as $business) {
|
|
DB::table('existing_business_locations')->insert([
|
|
'id' => (string) Str::uuid(), 'existing_business_id' => $business['id'],
|
|
'province_id' => $province, 'precision_level' => 'province',
|
|
'is_primary' => true, 'is_public' => $business['id'] !== $reserved['id'],
|
|
]);
|
|
}
|
|
DB::table('existing_businesses')->where('id', $expired['id'])->update(['expires_at' => now()->subDay()]);
|
|
$url = '/negocios?modality=company_sale&province_id='.$province;
|
|
$this->get('/negocios/'.$origin['public_slug'])->assertInertia(fn (Assert $page) => $page
|
|
->where('similarCatalogueUrl', $url)->where('record.province', 'Synthetic province'));
|
|
$this->get('/negocios/'.$reserved['public_slug'])->assertInertia(fn (Assert $page) => $page
|
|
->where('similarCatalogueUrl', '/negocios?modality=company_sale')->where('record.province', null));
|
|
$this->getJson('/api/v1/business-opportunities?modality=company_sale&province_id='.$province)->assertOk()
|
|
->assertJsonCount(2, 'data')->assertJsonMissing(['id' => $reserved['id']])->assertJsonMissing(['id' => $expired['id']]);
|
|
DB::table('geo_locations')->where('id', $province)->update(['source_system' => 'unpublished-synthetic']);
|
|
$this->get('/negocios/'.$origin['public_slug'])->assertInertia(fn (Assert $page) => $page
|
|
->where('similarCatalogueUrl', '/negocios?modality=company_sale'));
|
|
}
|
|
|
|
/** @return array{id:string,public_slug:string} */
|
|
private function publishedBusiness(string $modality = 'company_sale'): array
|
|
{
|
|
$id = (string) Str::uuid();
|
|
$slug = 'public-synthetic-'.$id;
|
|
$now = now();
|
|
DB::table('existing_businesses')->insert([
|
|
'id' => $id,
|
|
'code' => 'SYN-'.strtoupper(substr(str_replace('-', '', $id), 0, 12)),
|
|
'owner_user_id' => User::factory()->create()->id,
|
|
'modality' => $modality,
|
|
'status' => 'published',
|
|
'classification' => 'confidential',
|
|
'version_no' => 1,
|
|
'payload' => Crypt::encryptString(json_encode(['title' => 'Private synthetic title'], JSON_THROW_ON_ERROR)),
|
|
'public_title' => 'Public synthetic opportunity',
|
|
'public_slug' => $slug,
|
|
'published_at' => $now,
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
]);
|
|
|
|
return ['id' => $id, 'public_slug' => $slug];
|
|
}
|
|
}
|