feat: record disposition deleted event for statistics
This commit is contained in:
@@ -14,6 +14,7 @@ use App\Enum\StatisticsEventName;
|
||||
use App\EventListener\StatisticsChangeSetListener;
|
||||
use App\Service\Statistics\Collector\ApplicationCreatedCollector;
|
||||
use App\Service\Statistics\Collector\CallOffCollector;
|
||||
use App\Service\Statistics\Collector\DispositionDeletedCollector;
|
||||
use App\Service\Statistics\Collector\JobProfileChangeCollector;
|
||||
use App\Service\Statistics\StatisticsRecorder;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -304,6 +305,46 @@ class StatisticsChangeSetListenerTest extends TestCase
|
||||
$this->flush([], [$application]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A hard delete leaves nothing behind, so this row is the only trace the placement, its
|
||||
* teamer and the admin's justification ever existed. The dimensions have to be read
|
||||
* while the entity is still whole - by postFlush Doctrine has nulled its id.
|
||||
*/
|
||||
public function testRecordsADispositionDeletion(): void
|
||||
{
|
||||
$disposition = $this->disposition();
|
||||
$disposition
|
||||
->setStatus(Disposition::STATUS_CONFIRMED)
|
||||
->setRemarks('Doppelt eingeteilt')
|
||||
;
|
||||
|
||||
$this->recorder
|
||||
->expects($this->once())
|
||||
->method('record')
|
||||
->with(
|
||||
StatisticsEventName::DISPOSITION_DELETED,
|
||||
$this->anything(),
|
||||
$this->callback(static function (array $payload): bool {
|
||||
return Disposition::STATUS_CONFIRMED === $payload['previous_status']
|
||||
&& 'Doppelt eingeteilt' === $payload['remarks']
|
||||
&& null === $payload['called_off_by'];
|
||||
})
|
||||
)
|
||||
;
|
||||
|
||||
$this->flush([], [], [$disposition]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A disposition being edited is not a disposition being deleted.
|
||||
*/
|
||||
public function testADispositionUpdateIsNotADeletion(): void
|
||||
{
|
||||
$this->recorder->expects($this->never())->method('record');
|
||||
|
||||
$this->flush([[$this->disposition(), ['remarks' => ['alt', 'neu']]]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Editing an application later is not a second application.
|
||||
*/
|
||||
@@ -327,20 +368,22 @@ class StatisticsChangeSetListenerTest extends TestCase
|
||||
/**
|
||||
* @param array<int, array{0: object, 1: array<string, mixed>}> $changeSets
|
||||
* @param array<int, object> $insertions
|
||||
* @param array<int, object> $deletions
|
||||
*/
|
||||
private function flush(array $changeSets, array $insertions = []): void
|
||||
private function flush(array $changeSets, array $insertions = [], array $deletions = []): void
|
||||
{
|
||||
$listener = $this->listener();
|
||||
|
||||
$listener->onFlush($this->onFlushArgs($changeSets, $insertions));
|
||||
$listener->onFlush($this->onFlushArgs($changeSets, $insertions, $deletions));
|
||||
$listener->postFlush($this->postFlushArgs());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{0: object, 1: array<string, mixed>}> $changeSets
|
||||
* @param array<int, object> $insertions
|
||||
* @param array<int, object> $deletions
|
||||
*/
|
||||
private function onFlushArgs(array $changeSets, array $insertions = []): OnFlushEventArgs
|
||||
private function onFlushArgs(array $changeSets, array $insertions = [], array $deletions = []): OnFlushEventArgs
|
||||
{
|
||||
$entities = [];
|
||||
$byEntity = new \SplObjectStorage();
|
||||
@@ -353,6 +396,7 @@ class StatisticsChangeSetListenerTest extends TestCase
|
||||
$unitOfWork = $this->createMock(UnitOfWork::class);
|
||||
$unitOfWork->method('getScheduledEntityUpdates')->willReturn($entities);
|
||||
$unitOfWork->method('getScheduledEntityInsertions')->willReturn($insertions);
|
||||
$unitOfWork->method('getScheduledEntityDeletions')->willReturn($deletions);
|
||||
$unitOfWork
|
||||
->method('getEntityChangeSet')
|
||||
->willReturnCallback(static fn (object $entity): array => $byEntity[$entity] ?? [])
|
||||
@@ -377,6 +421,7 @@ class StatisticsChangeSetListenerTest extends TestCase
|
||||
return new StatisticsChangeSetListener($this->recorder, [
|
||||
new JobProfileChangeCollector(),
|
||||
new CallOffCollector(),
|
||||
new DispositionDeletedCollector(),
|
||||
new ApplicationCreatedCollector(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service\Statistics;
|
||||
|
||||
use App\Entity\Application;
|
||||
use App\Entity\Assignment;
|
||||
use App\Entity\Destination;
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\Teamer;
|
||||
use App\Service\Statistics\StatisticsDimensions;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* The dimension columns are what every statistics screen groups by, so the SER-stripping
|
||||
* hotel-code rule has to be applied here exactly as the recorder used to apply it inline.
|
||||
*/
|
||||
class StatisticsDimensionsTest extends TestCase
|
||||
{
|
||||
public function testFreezesTheNormalizedHotelCodeOfTheAssignment(): void
|
||||
{
|
||||
$assignment = (new Assignment())->setDestination((new Destination())->setHotelCode('SERZIL'));
|
||||
|
||||
$this->assertSame('ZIL', StatisticsDimensions::forAssignment($assignment)['hotel_code']);
|
||||
}
|
||||
|
||||
public function testKeepsAnUnprefixedHotelCodeAsItIs(): void
|
||||
{
|
||||
$assignment = (new Assignment())->setDestination((new Destination())->setHotelCode('SBW'));
|
||||
|
||||
$this->assertSame('SBW', StatisticsDimensions::forAssignment($assignment)['hotel_code']);
|
||||
}
|
||||
|
||||
public function testADispositionCarriesItsTeamerAndTheAssignmentDimensions(): void
|
||||
{
|
||||
$teamer = new Teamer();
|
||||
$assignment = (new Assignment())->setDestination((new Destination())->setHotelCode('SERZIL'));
|
||||
$disposition = new Disposition(new Application($assignment, $teamer));
|
||||
|
||||
$dimensions = StatisticsDimensions::forDisposition($disposition);
|
||||
|
||||
$this->assertArrayHasKey('disposition_id', $dimensions);
|
||||
$this->assertSame($teamer->getId(), $dimensions['teamer_id']);
|
||||
$this->assertSame('ZIL', $dimensions['hotel_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* A disposition can lose its assignment or teamer; the columns just go null, they do not
|
||||
* blow up the flush that is trying to record the event.
|
||||
*/
|
||||
public function testDegradesToNullWithoutAnAssignmentOrTeamer(): void
|
||||
{
|
||||
$disposition = new Disposition(new Application(new Assignment(), new Teamer()));
|
||||
$disposition->setAssignment(null);
|
||||
$disposition->setTeamer(null);
|
||||
|
||||
$dimensions = StatisticsDimensions::forDisposition($disposition);
|
||||
|
||||
$this->assertNull($dimensions['assignment_id']);
|
||||
$this->assertNull($dimensions['destination_id']);
|
||||
$this->assertNull($dimensions['hotel_code']);
|
||||
$this->assertNull($dimensions['teamer_id']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user