Files
clinicpro/tests/Database/UniqueConstraintsTest.php
T
hamedandClaude Opus 4.8 96a86dbfed fix(db): business-key unique constraints (M16-M19)
Add unique constraints (one migration, no dup data in either DB):
- users.email, users.national_code (M16) — NULLs still allowed.
- payments.gateway_token (M17).
- date_overrides (doctor_id, date) (M18) — was a non-unique index.
- financial_breakdowns (payment_id, source) (M19) — anti double-accounting.

Regression: tests/Database/UniqueConstraintsTest (4 duplicate-insert cases).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 21:03:54 +03:30

75 lines
2.5 KiB
PHP

<?php
namespace App\Tests\Database;
use App\Appointment\Entity\DateOverride;
use App\Doctor\Entity\Doctor;
use App\Payment\Entity\Payment;
use App\Settlement\Entity\FinancialBreakdown;
use App\Tests\ApiTestCase;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
/**
* Business-key uniqueness: duplicate email / national_code / gateway_token,
* a second date-override for the same (doctor, date), and a second financial
* breakdown for the same (payment, source) must all be rejected at the DB.
*/
class UniqueConstraintsTest extends ApiTestCase
{
public function testDuplicateEmailRejected(): void
{
$email = 'dup' . bin2hex(random_bytes(5)) . '@test.local';
$this->createUser()->setEmail($email);
$this->createUser()->setEmail($email);
$this->expectException(UniqueConstraintViolationException::class);
$this->em->flush();
}
public function testDuplicateGatewayTokenRejected(): void
{
$token = 'tok-' . bin2hex(random_bytes(6));
$a = new Payment($this->createUser(), 1000, 'mellat', Payment::TYPE_SUBSCRIPTION);
$a->setGatewayToken($token);
$b = new Payment($this->createUser(), 1000, 'mellat', Payment::TYPE_SUBSCRIPTION);
$b->setGatewayToken($token);
$this->em->persist($a);
$this->em->persist($b);
$this->expectException(UniqueConstraintViolationException::class);
$this->em->flush();
}
public function testDuplicateDateOverrideRejected(): void
{
$doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر');
$this->em->persist($doctor);
$this->em->flush();
$date = time();
$this->em->persist(new DateOverride($doctor, $date));
$this->em->persist(new DateOverride($doctor, $date));
$this->expectException(UniqueConstraintViolationException::class);
$this->em->flush();
}
public function testDuplicateBreakdownPaymentSourceRejected(): void
{
$user = $this->createUser();
$payment = new Payment($user, 100_000, 'mellat', Payment::TYPE_APPOINTMENT);
$this->em->persist($payment);
$this->em->flush();
$make = fn () => new FinancialBreakdown(
$payment, FinancialBreakdown::SOURCE_APPOINTMENT, $user,
100_000, 0, '0.00', 0, 100_000, '10.00', 10_000, 90_000, null, null, null,
);
$this->em->persist($make());
$this->em->persist($make());
$this->expectException(UniqueConstraintViolationException::class);
$this->em->flush();
}
}