1
0

TaskAssignmentRepositoryTest.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Tests\Repositories;
  4. use App\Domain\TaskAssignment;
  5. use App\Repositories\SprintRepository;
  6. use App\Repositories\SprintWorkerRepository;
  7. use App\Repositories\TaskAssignmentRepository;
  8. use App\Repositories\TaskRepository;
  9. use App\Repositories\WorkerRepository;
  10. use App\Tests\TestCase;
  11. use InvalidArgumentException;
  12. use PDO;
  13. /**
  14. * Phase 18: per-cell status on task_assignments. Verifies upsertStatus
  15. * semantics, the no-op rule, the "row created with days=0 when status is
  16. * set on an empty cell" path, and the new statusGridForSprint reader.
  17. */
  18. final class TaskAssignmentRepositoryTest extends TestCase
  19. {
  20. /** @return array{PDO,TaskAssignmentRepository,int,int,int,int} pdo, repo, sprintId, swId, taskId, workerId */
  21. private function seed(): array
  22. {
  23. $pdo = $this->makeDb();
  24. $sprints = new SprintRepository($pdo);
  25. $workers = new WorkerRepository($pdo);
  26. $sw = new SprintWorkerRepository($pdo);
  27. $tasks = new TaskRepository($pdo);
  28. $repo = new TaskAssignmentRepository($pdo);
  29. $sprint = $sprints->create('S', '2026-01-05', '2026-01-30', 0.2);
  30. $sprints->materializeWeeks($sprint->id, '2026-01-05', 4);
  31. $worker = $workers->create('Alice', true, 0.0);
  32. $sworker = $sw->add($sprint->id, $worker->id, 0.0);
  33. $task = $tasks->create($sprint->id, 'Build thing', null, 1);
  34. return [$pdo, $repo, $sprint->id, $sworker->id, $task->id, $worker->id];
  35. }
  36. public function testUpsertStatusOnEmptyCellWithDefaultIsNoop(): void
  37. {
  38. [, $repo, , $swId, $taskId] = $this->seed();
  39. $r = $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_ZUGEWIESEN);
  40. $this->assertSame('NOOP', $r['action']);
  41. $this->assertNull($r['before']);
  42. $this->assertNull($r['after']);
  43. $this->assertNull($repo->find($taskId, $swId));
  44. }
  45. public function testUpsertStatusOnEmptyCellWithExplicitCreatesZeroDayRow(): void
  46. {
  47. [, $repo, , $swId, $taskId] = $this->seed();
  48. $r = $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_GESTARTET);
  49. $this->assertSame('CREATE', $r['action']);
  50. $this->assertNull($r['before']);
  51. $this->assertNotNull($r['after']);
  52. $this->assertSame(0.0, $r['after']->days);
  53. $this->assertSame(TaskAssignment::STATUS_GESTARTET, $r['after']->status);
  54. // Reload to confirm persistence.
  55. $loaded = $repo->find($taskId, $swId);
  56. $this->assertNotNull($loaded);
  57. $this->assertSame(TaskAssignment::STATUS_GESTARTET, $loaded->status);
  58. }
  59. public function testUpsertStatusUnchangedIsNoop(): void
  60. {
  61. [, $repo, , $swId, $taskId] = $this->seed();
  62. $repo->upsert($taskId, $swId, 1.5);
  63. $first = $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_GESTARTET);
  64. $this->assertSame('UPDATE', $first['action']);
  65. $again = $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_GESTARTET);
  66. $this->assertSame('NOOP', $again['action']);
  67. $this->assertSame(TaskAssignment::STATUS_GESTARTET, $again['after']->status);
  68. }
  69. public function testUpsertStatusUpdatesExistingRowAndKeepsDays(): void
  70. {
  71. [, $repo, , $swId, $taskId] = $this->seed();
  72. $repo->upsert($taskId, $swId, 2.5);
  73. $r = $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_ABGESCHLOSSEN);
  74. $this->assertSame('UPDATE', $r['action']);
  75. $this->assertSame(2.5, $r['before']->days);
  76. $this->assertSame(TaskAssignment::STATUS_ZUGEWIESEN, $r['before']->status);
  77. $this->assertSame(2.5, $r['after']->days);
  78. $this->assertSame(TaskAssignment::STATUS_ABGESCHLOSSEN, $r['after']->status);
  79. }
  80. public function testUpsertDaysOnExistingPreservesStatus(): void
  81. {
  82. [, $repo, , $swId, $taskId] = $this->seed();
  83. $repo->upsert($taskId, $swId, 1.0);
  84. $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_GESTARTET);
  85. $r = $repo->upsert($taskId, $swId, 3.0);
  86. $this->assertSame('UPDATE', $r['action']);
  87. $this->assertSame(TaskAssignment::STATUS_GESTARTET, $r['after']->status);
  88. }
  89. public function testUpsertStatusRejectsUnknownValue(): void
  90. {
  91. [, $repo, , $swId, $taskId] = $this->seed();
  92. $this->expectException(InvalidArgumentException::class);
  93. $repo->upsertStatus($taskId, $swId, 'in-flight');
  94. }
  95. public function testStatusGridForSprintGroupsByTaskAndWorker(): void
  96. {
  97. [, $repo, $sprintId, $swId, $taskId] = $this->seed();
  98. $repo->upsert($taskId, $swId, 1.0);
  99. $repo->upsertStatus($taskId, $swId, TaskAssignment::STATUS_ABGEBROCHEN);
  100. $grid = $repo->statusGridForSprint($sprintId);
  101. $this->assertArrayHasKey($taskId, $grid);
  102. $this->assertArrayHasKey($swId, $grid[$taskId]);
  103. $this->assertSame(TaskAssignment::STATUS_ABGEBROCHEN, $grid[$taskId][$swId]);
  104. }
  105. public function testHydrateOnFreshUpsertCarriesDefaultStatus(): void
  106. {
  107. [, $repo, , $swId, $taskId] = $this->seed();
  108. $r = $repo->upsert($taskId, $swId, 1.0);
  109. $this->assertSame('CREATE', $r['action']);
  110. $this->assertSame(TaskAssignment::STATUS_ZUGEWIESEN, $r['after']->status);
  111. $loaded = $repo->find($taskId, $swId);
  112. $this->assertNotNull($loaded);
  113. $this->assertSame(TaskAssignment::STATUS_ZUGEWIESEN, $loaded->status);
  114. }
  115. }