test schedule rescheduling
This commit is contained in:
parent
a06bb6f003
commit
258e4ea27a
2 changed files with 338 additions and 0 deletions
218
backend/tests/Unit/Schedule/UseCases/RescheduleScheduleTest.php
Normal file
218
backend/tests/Unit/Schedule/UseCases/RescheduleScheduleTest.php
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Schedule\UseCases;
|
||||
|
||||
use App\Exceptions\BadRequestException;
|
||||
use App\Exceptions\NotFoundException;
|
||||
use App\Schedule\CreateScheduleAssignmentDto;
|
||||
use App\Schedule\CreateScheduleDto;
|
||||
use App\Schedule\EvenDistributionScheduler;
|
||||
use App\Schedule\ScheduleAssignment;
|
||||
use App\Schedule\UseCases\RescheduleSchedule\RescheduleSchedule;
|
||||
use App\Schedule\UseCases\RescheduleSchedule\RescheduleScheduleRequest;
|
||||
use App\Shared\ValueObject\EmailAddress;
|
||||
use App\User\User;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Tests\Fakes\FakeScheduleRepository;
|
||||
|
||||
class RescheduleScheduleTest extends TestCase
|
||||
{
|
||||
public function test_it_redistributes_only_unfinished_assignments(): void
|
||||
{
|
||||
$user = $this->user(1, 'reader@example.com');
|
||||
$repository = new FakeScheduleRepository;
|
||||
$schedule = $repository->create($this->schedule($user));
|
||||
$assignments = $schedule->getAssignments();
|
||||
$firstCompletion = $this->utc('2026-08-15T10:00:00');
|
||||
$secondCompletion = $this->utc('2026-08-16T11:00:00');
|
||||
$assignments[1]->complete($firstCompletion);
|
||||
$assignments[3]->complete($secondCompletion);
|
||||
$repository->updateAssignment($assignments[1]);
|
||||
$repository->updateAssignment($assignments[3]);
|
||||
|
||||
$rescheduled = $this->useCase($repository)->execute(
|
||||
new RescheduleScheduleRequest(
|
||||
scheduleId: $schedule->getId(),
|
||||
user: $user,
|
||||
startDate: '2026-08-20',
|
||||
targetDate: '2026-08-21',
|
||||
workloadPlacement: 'end',
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertSame('2026-08-20', $rescheduled->getStartDate()
|
||||
->format('Y-m-d'));
|
||||
$this->assertSame('2026-08-21', $rescheduled->getTargetDate()
|
||||
->format('Y-m-d'));
|
||||
$this->assertSame(
|
||||
[
|
||||
'2026-08-20',
|
||||
'2026-08-11',
|
||||
'2026-08-21',
|
||||
'2026-08-13',
|
||||
'2026-08-21',
|
||||
],
|
||||
array_map(function (ScheduleAssignment $assignment): string {
|
||||
return $assignment->getScheduledDate()->format('Y-m-d');
|
||||
}, $rescheduled->getAssignments()),
|
||||
);
|
||||
$this->assertSame(
|
||||
[null, $firstCompletion, null, $secondCompletion, null],
|
||||
array_map(function (
|
||||
ScheduleAssignment $assignment,
|
||||
): ?DateTimeImmutable {
|
||||
return $assignment->getCompletedAt();
|
||||
}, $rescheduled->getAssignments()),
|
||||
);
|
||||
$this->assertSame(
|
||||
[1, 2, 3, 4, 5],
|
||||
array_map(function (ScheduleAssignment $assignment): int {
|
||||
return $assignment->getId();
|
||||
}, $rescheduled->getAssignments()),
|
||||
);
|
||||
$this->assertSame(
|
||||
['Lesson 1', 'Lesson 2', 'Lesson 3', 'Lesson 4', 'Lesson 5'],
|
||||
array_map(function (ScheduleAssignment $assignment): string {
|
||||
return $assignment->getName();
|
||||
}, $rescheduled->getAssignments()),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_rejects_a_target_before_the_start(): void
|
||||
{
|
||||
$user = $this->user(1, 'reader@example.com');
|
||||
$repository = new FakeScheduleRepository;
|
||||
$schedule = $repository->create($this->schedule($user));
|
||||
|
||||
$this->expectException(BadRequestException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'targetDate must not be before startDate',
|
||||
);
|
||||
|
||||
$this->useCase($repository)->execute(
|
||||
new RescheduleScheduleRequest(
|
||||
scheduleId: $schedule->getId(),
|
||||
user: $user,
|
||||
startDate: '2026-08-21',
|
||||
targetDate: '2026-08-20',
|
||||
workloadPlacement: 'middle',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_rejects_an_unknown_workload_placement(): void
|
||||
{
|
||||
$user = $this->user(1, 'reader@example.com');
|
||||
$repository = new FakeScheduleRepository;
|
||||
$schedule = $repository->create($this->schedule($user));
|
||||
|
||||
$this->expectException(BadRequestException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'workloadPlacement must be start, middle, or end',
|
||||
);
|
||||
|
||||
$this->useCase($repository)->execute(
|
||||
new RescheduleScheduleRequest(
|
||||
scheduleId: $schedule->getId(),
|
||||
user: $user,
|
||||
startDate: '2026-08-20',
|
||||
targetDate: '2026-08-21',
|
||||
workloadPlacement: 'sideways',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_rejects_a_schedule_without_unfinished_work(): void
|
||||
{
|
||||
$user = $this->user(1, 'reader@example.com');
|
||||
$repository = new FakeScheduleRepository;
|
||||
$schedule = $repository->create($this->schedule($user));
|
||||
foreach ($schedule->getAssignments() as $assignment) {
|
||||
$assignment->complete($this->utc('2026-08-15T10:00:00'));
|
||||
$repository->updateAssignment($assignment);
|
||||
}
|
||||
|
||||
$this->expectException(BadRequestException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'schedule has no unfinished assignments',
|
||||
);
|
||||
|
||||
$this->useCase($repository)->execute(
|
||||
new RescheduleScheduleRequest(
|
||||
scheduleId: $schedule->getId(),
|
||||
user: $user,
|
||||
startDate: '2026-08-20',
|
||||
targetDate: '2026-08-21',
|
||||
workloadPlacement: 'middle',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_hides_another_users_schedule(): void
|
||||
{
|
||||
$repository = new FakeScheduleRepository;
|
||||
$owner = $this->user(1, 'owner@example.com');
|
||||
$schedule = $repository->create($this->schedule($owner));
|
||||
|
||||
$this->expectException(NotFoundException::class);
|
||||
$this->expectExceptionMessage('schedule not found');
|
||||
|
||||
$this->useCase($repository)->execute(
|
||||
new RescheduleScheduleRequest(
|
||||
scheduleId: $schedule->getId(),
|
||||
user: $this->user(2, 'viewer@example.com'),
|
||||
startDate: '2026-08-20',
|
||||
targetDate: '2026-08-21',
|
||||
workloadPlacement: 'middle',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function useCase(
|
||||
FakeScheduleRepository $repository,
|
||||
): RescheduleSchedule {
|
||||
return new RescheduleSchedule(
|
||||
$repository,
|
||||
new EvenDistributionScheduler,
|
||||
);
|
||||
}
|
||||
|
||||
private function schedule(User $user): CreateScheduleDto
|
||||
{
|
||||
$assignments = [];
|
||||
foreach (range(1, 5) as $number) {
|
||||
$assignments[] = new CreateScheduleAssignmentDto(
|
||||
name: "Lesson {$number}",
|
||||
kind: 'lesson',
|
||||
path: ['Course', "Lesson {$number}"],
|
||||
scheduledDate: $this->utc("2026-08-1{$number}"),
|
||||
position: $number,
|
||||
);
|
||||
}
|
||||
|
||||
return new CreateScheduleDto(
|
||||
user: $user,
|
||||
setName: 'Course',
|
||||
elementKind: 'lesson',
|
||||
startDate: $this->utc('2026-08-10'),
|
||||
targetDate: $this->utc('2026-08-15'),
|
||||
assignments: $assignments,
|
||||
);
|
||||
}
|
||||
|
||||
private function user(int $id, string $email): User
|
||||
{
|
||||
return new User(
|
||||
id: $id,
|
||||
email: new EmailAddress($email),
|
||||
passwordHash: 'hashed-password',
|
||||
);
|
||||
}
|
||||
|
||||
private function utc(string $value): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable($value, new DateTimeZone('UTC'));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue