feat(project-management) : add timestampable/blamable to Task and Project (additive)
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.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user