mdn-backend / tests /Unit /ApplicationReviewReadinessTest.php
internationalscholarsprogram's picture
feat(backend): replace backend with peter branch + Emergency Care integration
6773345
Raw
History Blame Contribute Delete
7.77 kB
<?php
namespace Tests\Unit;
use App\Domain\Applications\ApplicationRepository;
use App\Domain\Applications\ApplicationService;
use App\Domain\Payments\PaymentService;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use PDO;
use ReflectionClass;
use Tests\TestCase;
class ApplicationReviewReadinessTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
if (! in_array('sqlite', PDO::getAvailableDrivers(), true)) {
$this->markTestSkipped('SQLite PDO driver is required for application readiness database regression tests.');
}
Schema::dropIfExists('applications');
Schema::create('applications', function (Blueprint $table): void {
$table->string('id')->primary();
$table->string('application_reference')->nullable();
$table->string('status')->nullable();
$table->string('pathway')->nullable();
$table->string('email')->nullable();
$table->string('phone_number')->nullable();
$table->string('country_of_residence')->nullable();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->text('selected_product_codes')->nullable();
$table->text('metadata')->nullable();
$table->timestamp('submitted_at')->nullable();
$table->timestamp('ready_for_verification_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
public function test_review_search_returns_payment_completed_application_without_submitted_at(): void
{
$this->insertApplication('ready-from-payment', [
'status' => 'APPLICATION_COMPLETED',
'submitted_at' => null,
'ready_for_verification_at' => '2026-06-30 10:00:00',
]);
$this->insertApplication('in-progress', [
'status' => 'APPLICATION_IN_PROGRESS',
'submitted_at' => null,
'ready_for_verification_at' => null,
]);
$result = (new ApplicationRepository())->searchForReview([]);
$this->assertSame(['ready-from-payment'], array_map(fn (object $application): string => $application->id, $result['applications']));
$this->assertSame(1, $result['pagination']['total']);
}
public function test_review_submitted_date_filters_use_review_readiness_date(): void
{
$this->insertApplication('inside-window', [
'status' => 'APPLICATION_COMPLETED',
'submitted_at' => null,
'ready_for_verification_at' => '2026-06-30 10:00:00',
]);
$this->insertApplication('outside-window', [
'status' => 'APPLICATION_COMPLETED',
'submitted_at' => null,
'ready_for_verification_at' => '2026-06-20 10:00:00',
]);
$result = (new ApplicationRepository())->searchForReview([
'submitted_from' => '2026-06-29 00:00:00',
'submitted_to' => '2026-07-01 00:00:00',
]);
$this->assertSame(['inside-window'], array_map(fn (object $application): string => $application->id, $result['applications']));
}
public function test_payment_completion_sets_submitted_at_when_missing(): void
{
$this->insertApplication('payment-ready', [
'status' => 'APPLICATION_IN_PROGRESS',
'submitted_at' => null,
'ready_for_verification_at' => null,
]);
$this->invokeCompleteApplicationFeeIfNeeded((object) [
'application_id' => 'payment-ready',
'fee_type_code' => 'APPLICATION_FEE',
]);
$application = DB::table('applications')->where('id', 'payment-ready')->first();
$this->assertSame('APPLICATION_COMPLETED', $application->status);
$this->assertNotNull($application->ready_for_verification_at);
$this->assertNotNull($application->submitted_at);
}
public function test_payment_completion_preserves_existing_submitted_at(): void
{
$this->insertApplication('already-submitted', [
'status' => 'APPLICATION_IN_PROGRESS',
'submitted_at' => '2026-06-01 08:30:00',
'ready_for_verification_at' => null,
]);
$this->invokeCompleteApplicationFeeIfNeeded((object) [
'application_id' => 'already-submitted',
'fee_type_code' => 'APPLICATION_FEE',
]);
$application = DB::table('applications')->where('id', 'already-submitted')->first();
$this->assertSame('2026-06-01 08:30:00', $application->submitted_at);
}
public function test_approval_payment_url_identifies_membership_payment_type(): void
{
config(['app.approval_payment_form_url' => 'https://example.test/payment']);
$url = $this->invokeApprovalPaymentUrl('approval-token-123');
$parts = parse_url($url);
parse_str($parts['query'] ?? '', $query);
$this->assertSame('https', $parts['scheme']);
$this->assertSame('example.test', $parts['host']);
$this->assertSame('/payment', $parts['path']);
$this->assertSame('membership_fee', $query['payment_type'] ?? null);
$this->assertSame('approval-token-123', $query['approval_token'] ?? null);
}
public function test_approval_payment_url_derives_from_website_url_when_override_is_missing(): void
{
config([
'app.approval_payment_form_url' => null,
'platform.website_url' => 'https://clientportaldev.qhtestingserver.com/diaspora-nexus',
]);
$url = $this->invokeApprovalPaymentUrl('approval-token-456');
$parts = parse_url($url);
parse_str($parts['query'] ?? '', $query);
$this->assertSame('https://clientportaldev.qhtestingserver.com/diaspora-nexus/payment', ($parts['scheme'] ?? '').'://'.($parts['host'] ?? '').($parts['path'] ?? ''));
$this->assertSame('membership_fee', $query['payment_type'] ?? null);
$this->assertSame('approval-token-456', $query['approval_token'] ?? null);
}
private function insertApplication(string $id, array $overrides = []): void
{
DB::table('applications')->insert(array_merge([
'id' => $id,
'application_reference' => 'APP-'.$id,
'status' => 'APPLICATION_IN_PROGRESS',
'pathway' => 'STANDARD',
'email' => $id.'@example.com',
'phone_number' => '+1000000000',
'country_of_residence' => 'Kenya',
'first_name' => 'Test',
'last_name' => 'Applicant',
'selected_product_codes' => null,
'metadata' => null,
'submitted_at' => null,
'ready_for_verification_at' => null,
'created_at' => '2026-06-01 00:00:00',
'updated_at' => '2026-06-01 00:00:00',
'deleted_at' => null,
], $overrides));
}
private function invokeCompleteApplicationFeeIfNeeded(object $payment): void
{
$reflection = new ReflectionClass(PaymentService::class);
$service = $reflection->newInstanceWithoutConstructor();
$method = $reflection->getMethod('completeApplicationFeeIfNeeded');
$method->setAccessible(true);
$method->invoke($service, $payment);
}
private function invokeApprovalPaymentUrl(string $token): string
{
$reflection = new ReflectionClass(ApplicationService::class);
$service = $reflection->newInstanceWithoutConstructor();
$method = $reflection->getMethod('approvalPaymentUrl');
$method->setAccessible(true);
return $method->invoke($service, $token);
}
}