From 31036be2d93d236f52a619d63f004d4349d1f186 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Tue, 14 Apr 2026 20:58:14 -0300 Subject: [PATCH 1/5] fix(db): use compatible column fetch in repair mapper Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Db/FieldValueMapper.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Db/FieldValueMapper.php b/lib/Db/FieldValueMapper.php index e1053b0..ee69d21 100644 --- a/lib/Db/FieldValueMapper.php +++ b/lib/Db/FieldValueMapper.php @@ -13,6 +13,7 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Db\QBMapper; use OCP\IDBConnection; +use PDO; /** @template-extends QBMapper */ class FieldValueMapper extends QBMapper { @@ -98,7 +99,7 @@ public function findDistinctUserUids(): array { ->orderBy('user_uid', 'ASC'); $cursor = $qb->executeQuery(); - $userUids = $cursor->fetchFirstColumn(); + $userUids = $cursor->fetchAll(PDO::FETCH_COLUMN); $cursor->closeCursor(); return array_values(array_map(static fn (mixed $uid): string => (string)$uid, $userUids)); From 9d669f6e210a12e9ecade36e32be52163f5877c8 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Tue, 14 Apr 2026 20:58:17 -0300 Subject: [PATCH 2/5] test(db): cover distinct uid fetch compatibility Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/php/Unit/Db/FieldValueMapperTest.php | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/php/Unit/Db/FieldValueMapperTest.php diff --git a/tests/php/Unit/Db/FieldValueMapperTest.php b/tests/php/Unit/Db/FieldValueMapperTest.php new file mode 100644 index 0000000..2badbf2 --- /dev/null +++ b/tests/php/Unit/Db/FieldValueMapperTest.php @@ -0,0 +1,67 @@ +db = $this->createMock(IDBConnection::class); + $this->queryBuilder = $this->createMock(IQueryBuilder::class); + $this->result = $this->createMock(IResult::class); + $this->mapper = new FieldValueMapper($this->db); + } + + public function testFindDistinctUserUidsUsesFetchAllColumnModeForCompatibility(): void { + $this->db->expects($this->once()) + ->method('getQueryBuilder') + ->willReturn($this->queryBuilder); + + $this->queryBuilder->expects($this->once()) + ->method('selectDistinct') + ->with('user_uid') + ->willReturnSelf(); + $this->queryBuilder->expects($this->once()) + ->method('from') + ->with('profile_fields_values') + ->willReturnSelf(); + $this->queryBuilder->expects($this->once()) + ->method('orderBy') + ->with('user_uid', 'ASC') + ->willReturnSelf(); + $this->queryBuilder->expects($this->once()) + ->method('executeQuery') + ->willReturn($this->result); + + $this->result->expects($this->once()) + ->method('fetchAll') + ->with(PDO::FETCH_COLUMN) + ->willReturn(['alice', 7, 'bob']); + $this->result->expects($this->never()) + ->method('fetchFirstColumn'); + $this->result->expects($this->once()) + ->method('closeCursor') + ->willReturn(true); + + $this->assertSame(['alice', '7', 'bob'], $this->mapper->findDistinctUserUids()); + } +} From 5ee5889d70e8b31f7e6b3fbfd9f768762daa881d Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Tue, 14 Apr 2026 21:00:53 -0300 Subject: [PATCH 3/5] test(db): reduce mapper test coupling to query builder internals Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/php/Unit/Db/FieldValueMapperTest.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/php/Unit/Db/FieldValueMapperTest.php b/tests/php/Unit/Db/FieldValueMapperTest.php index 2badbf2..628cdbc 100644 --- a/tests/php/Unit/Db/FieldValueMapperTest.php +++ b/tests/php/Unit/Db/FieldValueMapperTest.php @@ -32,22 +32,13 @@ protected function setUp(): void { } public function testFindDistinctUserUidsUsesFetchAllColumnModeForCompatibility(): void { + $this->queryBuilder->method('selectDistinct')->willReturnSelf(); + $this->queryBuilder->method('from')->willReturnSelf(); + $this->queryBuilder->method('orderBy')->willReturnSelf(); + $this->db->expects($this->once()) ->method('getQueryBuilder') ->willReturn($this->queryBuilder); - - $this->queryBuilder->expects($this->once()) - ->method('selectDistinct') - ->with('user_uid') - ->willReturnSelf(); - $this->queryBuilder->expects($this->once()) - ->method('from') - ->with('profile_fields_values') - ->willReturnSelf(); - $this->queryBuilder->expects($this->once()) - ->method('orderBy') - ->with('user_uid', 'ASC') - ->willReturnSelf(); $this->queryBuilder->expects($this->once()) ->method('executeQuery') ->willReturn($this->result); From 1315deba308184f8da3713f8f1b4c15fde98e24c Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Tue, 14 Apr 2026 21:32:23 -0300 Subject: [PATCH 4/5] test(db): remove over-specified mapper unit test Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/php/Unit/Db/FieldValueMapperTest.php | 58 ---------------------- 1 file changed, 58 deletions(-) delete mode 100644 tests/php/Unit/Db/FieldValueMapperTest.php diff --git a/tests/php/Unit/Db/FieldValueMapperTest.php b/tests/php/Unit/Db/FieldValueMapperTest.php deleted file mode 100644 index 628cdbc..0000000 --- a/tests/php/Unit/Db/FieldValueMapperTest.php +++ /dev/null @@ -1,58 +0,0 @@ -db = $this->createMock(IDBConnection::class); - $this->queryBuilder = $this->createMock(IQueryBuilder::class); - $this->result = $this->createMock(IResult::class); - $this->mapper = new FieldValueMapper($this->db); - } - - public function testFindDistinctUserUidsUsesFetchAllColumnModeForCompatibility(): void { - $this->queryBuilder->method('selectDistinct')->willReturnSelf(); - $this->queryBuilder->method('from')->willReturnSelf(); - $this->queryBuilder->method('orderBy')->willReturnSelf(); - - $this->db->expects($this->once()) - ->method('getQueryBuilder') - ->willReturn($this->queryBuilder); - $this->queryBuilder->expects($this->once()) - ->method('executeQuery') - ->willReturn($this->result); - - $this->result->expects($this->once()) - ->method('fetchAll') - ->with(PDO::FETCH_COLUMN) - ->willReturn(['alice', 7, 'bob']); - $this->result->expects($this->never()) - ->method('fetchFirstColumn'); - $this->result->expects($this->once()) - ->method('closeCursor') - ->willReturn(true); - - $this->assertSame(['alice', '7', 'bob'], $this->mapper->findDistinctUserUids()); - } -} From ccb050434c6fe128748c393356d3feb1e23185a7 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Tue, 14 Apr 2026 21:35:53 -0300 Subject: [PATCH 5/5] test(behat): add repair scenario for orphaned field values Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/integration/features/api/repair.feature | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/integration/features/api/repair.feature diff --git a/tests/integration/features/api/repair.feature b/tests/integration/features/api/repair.feature new file mode 100644 index 0000000..de01b93 --- /dev/null +++ b/tests/integration/features/api/repair.feature @@ -0,0 +1,26 @@ +Feature: orphaned profile field value repair + Background: + Given user "repairuser" exists + And run the command "profile_fields:developer:reset --all" with result code 0 + + Scenario: maintenance:repair removes values for deleted users without errors + Given as user "admin" + When sending "post" to ocs "/apps/profile_fields/api/v1/definitions" + | fieldKey | repair_test_field | + | label | Repair test | + | type | text | + | editPolicy | users | + | exposurePolicy | private | + | sortOrder | 10 | + | active | true | + Then the response should have a status code 201 + And fetch field "(REPAIR_FIELD_ID)(jq).ocs.data.id" from previous JSON response + When sending "put" to ocs "/apps/profile_fields/api/v1/users/repairuser/values/" + | value | orphaned | + Then the response should have a status code 200 + And run the command "user:delete repairuser" with result code 0 + And run the command "maintenance:repair" with result code 0 + And the output of the last command should contain the following text: + """ + Repair orphaned profile field values + """