Files
ltbxd-actorle/tests/Import/AwardImporterTest.php
thibaud-leclere 295bb16ab7 fix: reduce false positives in award detection
Filter SPARQL query to only return entertainment awards (film, TV,
music, theater) and add a canonical award map to normalize variants
(e.g. all Oscar/Academy Award → "Oscar", all Golden Globe → "Golden
Globe"). Non-entertainment awards (orders, medals, honorary degrees)
are excluded both at SPARQL level and via PHP keyword filter.

Also restart messenger container on cache:clear to avoid stale DI
container errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:12:28 +02:00

282 lines
10 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Import;
use App\Entity\Actor;
use App\Entity\Award;
use App\Entity\AwardType;
use App\Gateway\WikidataGateway;
use App\Repository\AwardTypeRepository;
use App\Import\AwardImporter;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
class AwardImporterTest extends TestCase
{
private AwardImporter $importer;
private WikidataGateway&\PHPUnit\Framework\MockObject\MockObject $wikidataGateway;
private AwardTypeRepository&\PHPUnit\Framework\MockObject\MockObject $awardTypeRepository;
private EntityManagerInterface&\PHPUnit\Framework\MockObject\MockObject $em;
protected function setUp(): void
{
$this->wikidataGateway = $this->createMock(WikidataGateway::class);
$this->awardTypeRepository = $this->createMock(AwardTypeRepository::class);
$this->em = $this->createMock(EntityManagerInterface::class);
$this->importer = new AwardImporter(
$this->wikidataGateway,
$this->awardTypeRepository,
$this->em,
);
}
public function testSkipsActorWithAwardsAlreadyImported(): void
{
$actor = $this->createActorWithFlag('Already Imported', awardsImported: true);
$this->wikidataGateway->expects($this->never())->method('getAwardsForActors');
$this->importer->importForActors([$actor]);
}
public function testImportsAwardsAndSetsFlag(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([
'Test Actor' => [
['name' => 'Academy Award for Best Actor', 'year' => 2020],
],
]);
$existingType = new AwardType();
$existingType->setName('Oscar')->setPattern('Oscar');
$this->awardTypeRepository->method('findAll')->willReturn([$existingType]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor]);
$this->assertTrue($actor->isAwardsImported());
$this->assertCount(1, $persisted);
$this->assertInstanceOf(Award::class, $persisted[0]);
$this->assertSame('Academy Award for Best Actor', $persisted[0]->getName());
$this->assertSame(2020, $persisted[0]->getYear());
$this->assertSame($existingType, $persisted[0]->getAwardType());
$this->assertSame($actor, $persisted[0]->getActor());
}
public function testCanonicalMapGroupsRelatedAwards(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([
'Test Actor' => [
['name' => 'Screen Actors Guild Award for Outstanding Performance', 'year' => 2019],
],
]);
$this->awardTypeRepository->method('findAll')->willReturn([]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor]);
$this->assertTrue($actor->isAwardsImported());
// Should persist both a new AwardType and the Award
$this->assertCount(2, $persisted);
$newType = $persisted[0];
$this->assertInstanceOf(AwardType::class, $newType);
$this->assertSame('SAG', $newType->getName());
$this->assertSame('SAG', $newType->getPattern());
$award = $persisted[1];
$this->assertInstanceOf(Award::class, $award);
$this->assertSame($newType, $award->getAwardType());
}
public function testFallsBackToExtractPrefixWhenNotInCanonicalMap(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([
'Test Actor' => [
['name' => 'Bambi for Best Film', 'year' => 2019],
],
]);
$this->awardTypeRepository->method('findAll')->willReturn([]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor]);
$newType = $persisted[0];
$this->assertInstanceOf(AwardType::class, $newType);
$this->assertSame('Bambi', $newType->getName());
}
public function testExcludesNonEntertainmentAwards(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([
'Test Actor' => [
['name' => 'chevalier de la Légion d\'honneur', 'year' => 2015],
['name' => 'docteur honoris causa', 'year' => 2018],
['name' => 'bourse Rhodes', 'year' => 2010],
['name' => 'Oscar du meilleur acteur', 'year' => 2020],
],
]);
$this->awardTypeRepository->method('findAll')->willReturn([]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor]);
// Only the Oscar should be persisted (1 AwardType + 1 Award)
$this->assertCount(2, $persisted);
$this->assertInstanceOf(AwardType::class, $persisted[0]);
$this->assertSame('Oscar', $persisted[0]->getName());
$this->assertInstanceOf(Award::class, $persisted[1]);
}
public function testDoesNotSetFlagOnWikidataError(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')
->willThrowException(new \RuntimeException('Wikidata timeout'));
$this->importer->importForActors([$actor]);
$this->assertFalse($actor->isAwardsImported());
}
public function testHandlesActorWithNoAwards(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([]);
$this->awardTypeRepository->method('findAll')->willReturn([]);
$this->em->expects($this->never())->method('persist');
$this->importer->importForActors([$actor]);
$this->assertTrue($actor->isAwardsImported());
}
public function testBatchImportsMultipleActors(): void
{
$actor1 = $this->createActorWithFlag('Actor One', awardsImported: false);
$actor2 = $this->createActorWithFlag('Actor Two', awardsImported: false);
$alreadyImported = $this->createActorWithFlag('Actor Three', awardsImported: true);
$this->wikidataGateway->expects($this->once())->method('getAwardsForActors')
->with($this->callback(fn (array $actors) => 2 === \count($actors)))
->willReturn([
'Actor One' => [['name' => 'Academy Award for Best Actor', 'year' => 2020]],
'Actor Two' => [['name' => 'Golden Globe for Best Actor', 'year' => 2021]],
]);
$existingType = new AwardType();
$existingType->setName('Oscar')->setPattern('Oscar');
$this->awardTypeRepository->method('findAll')->willReturn([$existingType]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor1, $actor2, $alreadyImported]);
$this->assertTrue($actor1->isAwardsImported());
$this->assertTrue($actor2->isAwardsImported());
// 2 Awards + 1 new AwardType (Golden Globe)
$this->assertCount(3, $persisted);
}
public function testExtractPrefixHandlesFrenchPatterns(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([
'Test Actor' => [
['name' => 'Bodil du meilleur acteur', 'year' => 2019],
],
]);
$this->awardTypeRepository->method('findAll')->willReturn([]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor]);
$newType = $persisted[0];
$this->assertInstanceOf(AwardType::class, $newType);
$this->assertSame('Bodil', $newType->getName());
}
public function testCanonicalMapReusesExistingType(): void
{
$actor = $this->createActorWithFlag('Test Actor', awardsImported: false);
$this->wikidataGateway->method('getAwardsForActors')->willReturn([
'Test Actor' => [
['name' => 'oscar du meilleur acteur', 'year' => 2020],
['name' => 'Oscar de la meilleure actrice', 'year' => 2021],
],
]);
$existingOscar = new AwardType();
$existingOscar->setName('Oscar')->setPattern('Oscar');
$this->awardTypeRepository->method('findAll')->willReturn([$existingOscar]);
$persisted = [];
$this->em->method('persist')->willReturnCallback(function ($entity) use (&$persisted) {
$persisted[] = $entity;
});
$this->importer->importForActors([$actor]);
// Both awards should reuse the same "Oscar" type — only 2 Awards persisted, no new AwardType
$this->assertCount(2, $persisted);
$this->assertContainsOnlyInstancesOf(Award::class, $persisted);
$this->assertSame($existingOscar, $persisted[0]->getAwardType());
$this->assertSame($existingOscar, $persisted[1]->getAwardType());
}
private function createActorWithFlag(string $name, bool $awardsImported): Actor
{
$actor = new Actor();
$actor->setName($name);
$actor->setAwardsImported($awardsImported);
return $actor;
}
}