From d9b8853d119121774728e2a7cceab9f0af1345e6 Mon Sep 17 00:00:00 2001 From: thibaud-leclere Date: Sat, 11 Apr 2026 11:55:31 +0200 Subject: [PATCH] fix: keep synced actors available for award import --- src/Import/ActorSyncer.php | 9 ++- tests/Import/ActorSyncerTest.php | 116 +++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 tests/Import/ActorSyncerTest.php diff --git a/src/Import/ActorSyncer.php b/src/Import/ActorSyncer.php index 899bd65..90e7850 100644 --- a/src/Import/ActorSyncer.php +++ b/src/Import/ActorSyncer.php @@ -41,10 +41,15 @@ readonly class ActorSyncer $existingRole = $this->em->getRepository(MovieRole::class)->count(['actor' => $actor, 'movie' => $movie]); if (0 === $existingRole) { $role = new MovieRole() - ->setMovie($movie) - ->setActor($actor) ->setCharacter($actorModel->character); + // Keep both sides in sync so downstream code can rely on in-memory collections + // before Doctrine flushes and reloads the entities. + $role->setMovie($movie); + $role->setActor($actor); + $movie->addActor($role); + $actor->addMovieRole($role); + $this->em->persist($role); } } diff --git a/tests/Import/ActorSyncerTest.php b/tests/Import/ActorSyncerTest.php new file mode 100644 index 0000000..1f9f988 --- /dev/null +++ b/tests/Import/ActorSyncerTest.php @@ -0,0 +1,116 @@ +setTmdbId(123) + ->setTitle('Inception') + ->setLtbxdRef('film/inception'); + + $tmdbGateway = $this->createMock(TMDBGateway::class); + $tmdbGateway->method('getMovieCredits')->with(123)->willReturn( + new MovieCreditsContext([ + new TMDBMovieCredit( + id: 42, + name: 'Leonardo DiCaprio', + popularity: 99.0, + character: 'Cobb', + ), + ]), + ); + + $actorRepository = new class implements ObjectRepository { + public function findAll(): array + { + return []; + } + + public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array + { + return []; + } + + public function findOneBy(array $criteria): ?object + { + return null; + } + + public function getClassName(): string + { + return Actor::class; + } + }; + + $movieRoleRepository = new class implements ObjectRepository { + public function findAll(): array + { + return []; + } + + public function findBy(array $criteria, array|null $orderBy = null, int|null $limit = null, int|null $offset = null): array + { + return []; + } + + public function findOneBy(array $criteria): ?object + { + return null; + } + + public function getClassName(): string + { + return MovieRole::class; + } + + public function count(array $criteria = []): int + { + return 0; + } + }; + + $persisted = []; + + $em = $this->createMock(EntityManagerInterface::class); + $em->method('getRepository')->willReturnCallback( + static fn (string $class): ObjectRepository => match ($class) { + Actor::class => $actorRepository, + MovieRole::class => $movieRoleRepository, + default => throw new \InvalidArgumentException("Unexpected repository for {$class}"), + }, + ); + $em->method('persist')->willReturnCallback(function (object $entity) use (&$persisted): void { + $persisted[] = $entity; + }); + + $syncer = new ActorSyncer($tmdbGateway, $em); + $syncer->syncActorsForMovie($movie); + + self::assertCount(2, $persisted); + self::assertInstanceOf(Actor::class, $persisted[0]); + self::assertInstanceOf(MovieRole::class, $persisted[1]); + + self::assertCount(1, $movie->getActors()); + $role = $movie->getActors()->first(); + self::assertInstanceOf(MovieRole::class, $role); + self::assertSame($movie, $role->getMovie()); + self::assertSame($persisted[0], $role->getActor()); + self::assertCount(1, $persisted[0]->getMovieRoles()); + } +}