test level based scheduling

This commit is contained in:
Yisroel Baum 2026-08-11 22:54:55 +03:00
parent 99e1ba051c
commit a64df64b76
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
7 changed files with 415 additions and 94 deletions

View file

@ -8,92 +8,110 @@ use App\Exceptions\NotFoundException;
use App\Schedule\UseCases\CreateSchedule\CreateSchedule;
use App\Schedule\UseCases\CreateSchedule\CreateScheduleRequest;
use App\Set\CreateSetDto;
use App\Set\CreateSetLevelDto;
use App\Set\Set;
use App\Set\SetLevel;
use App\Shared\ValueObject\EmailAddress;
use App\User\User;
use PHPUnit\Framework\TestCase;
use Tests\Fakes\FakeElementRepository;
use Tests\Fakes\FakeScheduleRepository;
use Tests\Fakes\FakeSetRepository;
use Tests\Fakes\FakeSetLevelRepository;
class CreateScheduleTest extends TestCase
{
public function test_it_schedules_every_matching_element_in_tree_order(): void
public function test_it_schedules_every_element_on_the_selected_level(): void
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$elementRepository = new FakeElementRepository;
$scheduleRepository = new FakeScheduleRepository;
$set = $setRepository->create(new CreateSetDto(
name: 'Bible',
creator: $user,
));
$bookLevel = $this->createLevel(
$setLevelRepository,
$set,
'book',
);
$portionLevel = $this->createLevel(
$setLevelRepository,
$set,
'portion',
);
$chapterLevel = $this->createLevel(
$setLevelRepository,
$set,
'chapter',
);
$genesis = $elementRepository->create(new CreateElementDto(
set: $set,
name: 'Genesis',
kind: 'book',
level: $bookLevel,
parentElement: null,
));
$exodus = $elementRepository->create(new CreateElementDto(
set: $set,
name: 'Exodus',
kind: 'book',
level: $bookLevel,
parentElement: null,
));
$exodusChapterOne = $elementRepository->create(
new CreateElementDto(
set: $set,
name: 'Exodus 1',
kind: 'chapter',
parentElement: $exodus,
),
);
$creation = $elementRepository->create(new CreateElementDto(
set: $set,
name: 'Creation',
kind: 'portion',
level: $portionLevel,
parentElement: $genesis,
));
$shemot = $elementRepository->create(new CreateElementDto(
name: 'Shemot',
level: $portionLevel,
parentElement: $exodus,
));
$genesisChapterOne = $elementRepository->create(
new CreateElementDto(
set: $set,
name: 'Genesis 1',
kind: 'chapter',
level: $chapterLevel,
parentElement: $creation,
),
);
$genesisChapterTwo = $elementRepository->create(
new CreateElementDto(
set: $set,
name: 'Genesis 2',
kind: 'chapter',
level: $chapterLevel,
parentElement: $creation,
),
);
$genesisChapterThree = $elementRepository->create(
new CreateElementDto(
set: $set,
name: 'Genesis 3',
kind: 'chapter',
level: $chapterLevel,
parentElement: $creation,
),
);
$exodusChapterOne = $elementRepository->create(
new CreateElementDto(
name: 'Exodus 1',
level: $chapterLevel,
parentElement: $shemot,
),
);
$exodusChapterTwo = $elementRepository->create(
new CreateElementDto(
set: $set,
name: 'Exodus 2',
kind: 'chapter',
parentElement: $exodus,
level: $chapterLevel,
parentElement: $shemot,
),
);
$schedule = (new CreateSchedule(
$setRepository,
$setLevelRepository,
$elementRepository,
$scheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
elementKind: 'chapter',
levelId: $chapterLevel->getId(),
startDate: '2026-08-10',
targetDate: '2026-08-12',
));
@ -139,29 +157,35 @@ class CreateScheduleTest extends TestCase
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$elementRepository = new FakeElementRepository;
$set = $setRepository->create(new CreateSetDto(
name: 'Course',
creator: $user,
));
$lessonLevel = $this->createLevel(
$setLevelRepository,
$set,
'lesson',
);
foreach (['First', 'Second', 'Third'] as $name) {
$elementRepository->create(new CreateElementDto(
set: $set,
name: $name,
kind: 'lesson',
level: $lessonLevel,
parentElement: null,
));
}
$schedule = (new CreateSchedule(
$setRepository,
$setLevelRepository,
$elementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
elementKind: 'lesson',
levelId: $lessonLevel->getId(),
startDate: '2026-08-10',
targetDate: '2026-08-16',
));
@ -178,26 +202,32 @@ class CreateScheduleTest extends TestCase
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$elementRepository = new FakeElementRepository;
$set = $setRepository->create(new CreateSetDto(
name: 'Project',
creator: $user,
));
$milestoneLevel = $this->createLevel(
$setLevelRepository,
$set,
'milestone',
);
$elementRepository->create(new CreateElementDto(
set: $set,
name: 'Ship it',
kind: 'milestone',
level: $milestoneLevel,
parentElement: null,
));
$schedule = (new CreateSchedule(
$setRepository,
$setLevelRepository,
$elementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
elementKind: 'milestone',
levelId: $milestoneLevel->getId(),
startDate: '2020-01-01',
targetDate: '2030-01-01',
));
@ -217,18 +247,19 @@ class CreateScheduleTest extends TestCase
(new CreateSchedule(
new FakeSetRepository,
new FakeSetLevelRepository,
new FakeElementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $this->user(),
setId: 999,
elementKind: 'chapter',
levelId: 1,
startDate: '2026-08-10',
targetDate: '2026-08-12',
));
}
public function test_it_rejects_an_unavailable_element_kind(): void
public function test_it_rejects_a_missing_level(): void
{
$user = $this->user();
$setRepository = new FakeSetRepository;
@ -238,18 +269,85 @@ class CreateScheduleTest extends TestCase
));
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage(
'elementKind is not available for set',
);
$this->expectExceptionMessage('levelId is required');
(new CreateSchedule(
$setRepository,
new FakeSetLevelRepository,
new FakeElementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
elementKind: 'Chapter',
levelId: null,
startDate: '2026-08-10',
targetDate: '2026-08-12',
));
}
public function test_it_rejects_a_level_from_another_set(): void
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$bible = $setRepository->create(new CreateSetDto(
name: 'Bible',
creator: $user,
));
$course = $setRepository->create(new CreateSetDto(
name: 'Course',
creator: $user,
));
$moduleLevel = $this->createLevel(
$setLevelRepository,
$course,
'module',
);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('level is not available for set');
(new CreateSchedule(
$setRepository,
$setLevelRepository,
new FakeElementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $bible->getId(),
levelId: $moduleLevel->getId(),
startDate: '2026-08-10',
targetDate: '2026-08-12',
));
}
public function test_it_rejects_a_level_without_elements(): void
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$set = $setRepository->create(new CreateSetDto(
name: 'Bible',
creator: $user,
));
$chapterLevel = $this->createLevel(
$setLevelRepository,
$set,
'chapter',
);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('level has no elements');
(new CreateSchedule(
$setRepository,
$setLevelRepository,
new FakeElementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
levelId: $chapterLevel->getId(),
startDate: '2026-08-10',
targetDate: '2026-08-12',
));
@ -259,22 +357,29 @@ class CreateScheduleTest extends TestCase
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$set = $setRepository->create(new CreateSetDto(
name: 'Bible',
creator: $user,
));
$chapterLevel = $this->createLevel(
$setLevelRepository,
$set,
'chapter',
);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('startDate must be a valid date');
(new CreateSchedule(
$setRepository,
$setLevelRepository,
new FakeElementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
elementKind: 'chapter',
levelId: $chapterLevel->getId(),
startDate: '2026-02-30',
targetDate: '2026-08-12',
));
@ -284,10 +389,16 @@ class CreateScheduleTest extends TestCase
{
$user = $this->user();
$setRepository = new FakeSetRepository;
$setLevelRepository = new FakeSetLevelRepository;
$set = $setRepository->create(new CreateSetDto(
name: 'Bible',
creator: $user,
));
$chapterLevel = $this->createLevel(
$setLevelRepository,
$set,
'chapter',
);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage(
@ -296,17 +407,29 @@ class CreateScheduleTest extends TestCase
(new CreateSchedule(
$setRepository,
$setLevelRepository,
new FakeElementRepository,
new FakeScheduleRepository,
))->execute(new CreateScheduleRequest(
user: $user,
setId: $set->getId(),
elementKind: 'chapter',
levelId: $chapterLevel->getId(),
startDate: '2026-08-12',
targetDate: '2026-08-10',
));
}
private function createLevel(
FakeSetLevelRepository $repository,
Set $set,
string $kind,
): SetLevel {
return $repository->create(new CreateSetLevelDto(
set: $set,
kind: $kind,
));
}
private function user(): User
{
return new User(