c90d91d6c4
Tranche 3 of LST-65. Task and Project adopt TimestampableBlamableTrait. - Additive migration on task and project: created_at/updated_at (nullable), created_by/updated_by (nullable INT, FK to "user" ON DELETE SET NULL) + indexes + COMMENT ON COLUMN. down() drops only the added objects. - Trait fields stay out of the existing API groups (trait carries its own). - Functional test (TaskTimestampableTest) confirms created_at on persist and updated_at refresh on update. 161 tests green, no destructive migration.
103 lines
3.4 KiB
PHP
103 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Functional\Module\ProjectManagement;
|
|
|
|
use App\Module\ProjectManagement\Domain\Entity\Project;
|
|
use App\Module\ProjectManagement\Domain\Entity\Task;
|
|
use DateTimeImmutable;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
/**
|
|
* Task adopts TimestampableBlamableTrait (LST-65 slice 3).
|
|
* Verifies the TimestampableBlamableSubscriber populates created_at on
|
|
* persist and refreshes updated_at on update.
|
|
*
|
|
* @internal
|
|
*/
|
|
final class TaskTimestampableTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
$this->em->getConnection()->executeStatement("DELETE FROM task WHERE title = 'ts-test-task'");
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
$this->em->getConnection()->executeStatement("DELETE FROM task WHERE title = 'ts-test-task'");
|
|
parent::tearDown();
|
|
unset($this->em);
|
|
}
|
|
|
|
public function testCreatedAtIsSetOnPersist(): void
|
|
{
|
|
$task = $this->makeTask();
|
|
$this->em->persist($task);
|
|
$this->em->flush();
|
|
|
|
self::assertInstanceOf(DateTimeImmutable::class, $task->getCreatedAt(), 'createdAt must be set after flush');
|
|
self::assertInstanceOf(DateTimeImmutable::class, $task->getUpdatedAt(), 'updatedAt must be set after flush');
|
|
|
|
$taskId = $task->getId();
|
|
$this->em->clear();
|
|
$reloaded = $this->em->getRepository(Task::class)->find($taskId);
|
|
self::assertNotNull($reloaded);
|
|
self::assertNotNull($reloaded->getCreatedAt(), 'created_at must be persisted in DB');
|
|
}
|
|
|
|
public function testUpdatedAtIsRefreshedOnUpdate(): void
|
|
{
|
|
$task = $this->makeTask();
|
|
$this->em->persist($task);
|
|
$this->em->flush();
|
|
|
|
$taskId = $task->getId();
|
|
$initialUpdatedAt = $task->getUpdatedAt();
|
|
self::assertNotNull($initialUpdatedAt);
|
|
|
|
// Backdate the persisted value so the preUpdate refresh is observable
|
|
// even within the same second.
|
|
$this->em->getConnection()->executeStatement(
|
|
"UPDATE task SET updated_at = '2000-01-01 00:00:00' WHERE id = :id",
|
|
['id' => $taskId],
|
|
);
|
|
$this->em->clear();
|
|
|
|
/** @var Task $managed */
|
|
$managed = $this->em->getRepository(Task::class)->find($taskId);
|
|
self::assertSame('2000-01-01 00:00:00', $managed->getUpdatedAt()?->format('Y-m-d H:i:s'));
|
|
|
|
$managed->setDescription('changed description');
|
|
$this->em->flush();
|
|
$this->em->clear();
|
|
|
|
/** @var Task $reloaded */
|
|
$reloaded = $this->em->getRepository(Task::class)->find($taskId);
|
|
self::assertNotNull($reloaded->getUpdatedAt());
|
|
self::assertNotSame(
|
|
'2000-01-01 00:00:00',
|
|
$reloaded->getUpdatedAt()->format('Y-m-d H:i:s'),
|
|
'updated_at must be refreshed and persisted on UPDATE',
|
|
);
|
|
}
|
|
|
|
private function makeTask(): Task
|
|
{
|
|
$project = $this->em->getRepository(Project::class)->findOneBy([]);
|
|
self::assertNotNull($project, 'Les fixtures doivent fournir au moins un projet.');
|
|
|
|
$task = new Task();
|
|
$task->setNumber(random_int(100000, 999999));
|
|
$task->setTitle('ts-test-task');
|
|
$task->setProject($project);
|
|
|
|
return $task;
|
|
}
|
|
}
|