entityManager = $this->createMock(EntityManagerInterface::class); $this->seasonTaskDocumentRepository = $this->createMock(SeasonTaskDocumentRepository::class); $this->synchronizer = new SeasonTaskSynchronizer( $this->entityManager, $this->seasonTaskDocumentRepository ); } public function testItCreatesOneRowPerEntryInEnumOrder(): void { $seasonAssignment = new SeasonAssignment(); $this->entityManager->expects($this->exactly(count(SeasonTask::cases())))->method('persist'); $this->entityManager->expects($this->once())->method('flush'); $this->synchronizer->sync($seasonAssignment); $this->assertSame( array_map(static fn (SeasonTask $task): string => $task->value, SeasonTask::cases()), $this->keysOf($seasonAssignment) ); } public function testItOnlyCreatesWhatIsMissing(): void { $seasonAssignment = new SeasonAssignment(); $seasonAssignment->addTask((new SeasonAssignmentTask())->setTaskKey(SeasonTask::CONTRACT->value)); $this->entityManager->expects($this->exactly(count(SeasonTask::cases()) - 1))->method('persist'); $this->synchronizer->sync($seasonAssignment); $this->assertCount(count(SeasonTask::cases()), $seasonAssignment->getTasks()); $this->assertSame( 1, count(array_filter( $this->keysOf($seasonAssignment), static fn (string $key): bool => SeasonTask::CONTRACT->value === $key )) ); } public function testASyncedAssignmentIsNotFlushedAgain(): void { $seasonAssignment = new SeasonAssignment(); foreach (SeasonTask::cases() as $case) { $seasonAssignment->addTask((new SeasonAssignmentTask())->setTaskKey($case->value)); } $this->entityManager->expects($this->never())->method('persist'); $this->entityManager->expects($this->never())->method('flush'); $this->synchronizer->sync($seasonAssignment); } public function testARowOfAnUnknownEntryIsKept(): void { $seasonAssignment = new SeasonAssignment(); $seasonAssignment->addTask((new SeasonAssignmentTask())->setTaskKey('entry_removed_from_the_enum')); $this->entityManager->expects($this->never())->method('remove'); $this->synchronizer->sync($seasonAssignment); $this->assertCount(count(SeasonTask::cases()) + 1, $seasonAssignment->getTasks()); $this->assertContains('entry_removed_from_the_enum', $this->keysOf($seasonAssignment)); } public function testAnEntryHandingOutADepositedDocumentStartsTickedForTheOffice(): void { $this->depositDocumentsFor(SeasonTask::MASTER_DATA); $seasonAssignment = new SeasonAssignment(); $this->synchronizer->sync($seasonAssignment); $this->assertSame([SeasonTask::MASTER_DATA->value], $this->officeDoneKeysOf($seasonAssignment)); } /** * The row keeps the file it was given. Replacing the library document later is what the next * season gets, and what the propagate action carries over by hand - never a read of this row. */ public function testTheDocumentIsCopiedOntoTheRow(): void { $document = new Upload(); $this->depositDocuments([SeasonTask::MASTER_DATA], $document); $seasonAssignment = new SeasonAssignment(); $this->synchronizer->sync($seasonAssignment); foreach ($seasonAssignment->getTasks() as $task) { $this->assertSame( SeasonTask::MASTER_DATA->value === $task->getTaskKey() ? $document : null, $task->getDocument(), sprintf('Entry "%s" was given the wrong document.', $task->getTaskKey()) ); } } /** * The entry hands a document out, it is just not deposited yet - so the office still has to * put it together and the row starts open. */ public function testAnEntryWithoutADepositedDocumentStartsOpen(): void { $this->depositDocumentsFor(); $seasonAssignment = new SeasonAssignment(); $this->synchronizer->sync($seasonAssignment); $this->assertSame([], $this->officeDoneKeysOf($seasonAssignment)); } public function testThePreTickNeverOverwritesAnExistingRow(): void { $this->depositDocumentsFor(SeasonTask::MASTER_DATA); $seasonAssignment = new SeasonAssignment(); $seasonAssignment->addTask( (new SeasonAssignmentTask())->setTaskKey(SeasonTask::MASTER_DATA->value) ); $this->synchronizer->sync($seasonAssignment); $this->assertSame( [], $this->officeDoneKeysOf($seasonAssignment), 'The office unticked this row, and the next sync has to leave it alone.' ); } private function depositDocumentsFor(SeasonTask ...$tasks): void { $this->depositDocuments($tasks, new Upload()); } /** * @param SeasonTask[] $tasks */ private function depositDocuments(array $tasks, Upload $document): void { $documents = []; foreach ($tasks as $task) { $documents[$task->value] = (new SeasonTaskDocument()) ->setTaskKey($task->value) ->setDocument($document) ; } $this->seasonTaskDocumentRepository->method('findIndexedByTaskKey')->willReturn($documents); } /** * @return string[] */ private function officeDoneKeysOf(SeasonAssignment $seasonAssignment): array { return array_values(array_map( static fn (SeasonAssignmentTask $task): string => $task->getTaskKey(), array_filter( $seasonAssignment->getTasks()->toArray(), static fn (SeasonAssignmentTask $task): bool => $task->isOfficeDone() ) )); } /** * @return string[] */ private function keysOf(SeasonAssignment $seasonAssignment): array { return array_map( static fn (SeasonAssignmentTask $task): string => $task->getTaskKey(), $seasonAssignment->getTasks()->toArray() ); } }