From fd5829affb93af9d215f4ed52bdf3c5ce3f10f73 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 4 Nov 2026 16:50:33 +0100 Subject: [PATCH] fix(migration): skip answers of deleted questions on import Deleted questions are not exported, their answers are. Importing such an answer failed, as no imported question belongs to it, and stopped the import of the remaining forms. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Andy Scherzinger --- lib/FormsMigrator.php | 4 ++++ tests/Unit/FormsMigratorTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/FormsMigrator.php b/lib/FormsMigrator.php index 91b4ad64b..740d66513 100644 --- a/lib/FormsMigrator.php +++ b/lib/FormsMigrator.php @@ -197,6 +197,10 @@ public function import(IUser $user, IImportSource $importSource, OutputInterface $this->submissionMapper->insert($submission); foreach ($submissionData['answers'] as $answerData) { + if (!array_key_exists($answerData['questionId'], $questionIdMap)) { + continue; + } + $answer = new Answer(); $answer->setSubmissionId($submission->getId()); $answer->setQuestionId($questionIdMap[$answerData['questionId']]); diff --git a/tests/Unit/FormsMigratorTest.php b/tests/Unit/FormsMigratorTest.php index a380cc464..ff3a41776 100644 --- a/tests/Unit/FormsMigratorTest.php +++ b/tests/Unit/FormsMigratorTest.php @@ -290,6 +290,31 @@ public function testImport(string $inputJson) { $this->formsMigrator->import($user, $importSource, $output); } + public function testImportSkipsAnswersOfQuestionsNotExported(): void { + $inputJson = '[{"title":"Link","description":"","created":1646251830,"access":{"permitAllUsers":false,"showToAllUsers":false},"expires":0,"state":0,"isAnonymous":false,"submitMultiple":false,"allowEditSubmissions":false,"showExpiration":false,"lastUpdated":123456789,' + . '"questions":[{"id":14,"order":1,"type":"short","isRequired":false,"text":"Name?","description":"","extraSettings":{},"options":[]}],' + . '"submissions":[{"userId":"anyUser@localhost","timestamp":1651354059,"answers":[{"questionId":14,"text":"Ann"},{"questionId":13,"text":"Answer of a deleted question"}]}]}]'; + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('someUser'); + $importSource = $this->createMock(IImportSource::class); + $importSource->method('getMigratorVersion')->willReturn(1); + $importSource->method('getFileContents')->willReturn($inputJson); + $this->questionMapper->method('insert')->willReturnCallback(function ($question) { + $question->setId(100); + return $question; + }); + + $this->answerMapper->expects($this->once()) + ->method('insert') + ->willReturnCallback(function ($answer) { + $this->assertSame(100, $answer->getQuestionId()); + return $answer; + }); + + $this->formsMigrator->import($user, $importSource, $this->createMock(OutputInterface::class)); + } + public function testImport_NoVersion() { $user = $this->createMock(IUser::class); $importSource = $this->createMock(IImportSource::class);