From 0b8d5a03f1650b1dcf57ad3c61654a891ab52cc3 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:53:38 -0300 Subject: [PATCH 01/15] test(php): inject IL10N mock into FieldDefinitionServiceTest setUp Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/php/Unit/Service/FieldDefinitionServiceTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/php/Unit/Service/FieldDefinitionServiceTest.php b/tests/php/Unit/Service/FieldDefinitionServiceTest.php index 0bc0211..c0e4793 100644 --- a/tests/php/Unit/Service/FieldDefinitionServiceTest.php +++ b/tests/php/Unit/Service/FieldDefinitionServiceTest.php @@ -18,22 +18,29 @@ use OCA\ProfileFields\Enum\FieldType; use OCA\ProfileFields\Service\FieldDefinitionService; use OCA\ProfileFields\Service\FieldDefinitionValidator; +use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class FieldDefinitionServiceTest extends TestCase { private FieldDefinitionMapper&MockObject $fieldDefinitionMapper; private FieldValueMapper&MockObject $fieldValueMapper; + private IL10N&MockObject $l10n; private FieldDefinitionService $service; protected function setUp(): void { parent::setUp(); $this->fieldDefinitionMapper = $this->createMock(FieldDefinitionMapper::class); $this->fieldValueMapper = $this->createMock(FieldValueMapper::class); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnCallback( + static fn (string $text, array $parameters = []): string => $parameters === [] ? $text : vsprintf($text, $parameters), + ); $this->service = new FieldDefinitionService( $this->fieldDefinitionMapper, $this->fieldValueMapper, new FieldDefinitionValidator(), + $this->l10n, ); } From 1ed73dc3b7a4be00b3b6f6e62d373b0dc45f51bc Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:53:43 -0300 Subject: [PATCH 02/15] test(php): inject IL10N mock into FieldValueServiceTest setUp Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/php/Unit/Service/FieldValueServiceTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/php/Unit/Service/FieldValueServiceTest.php b/tests/php/Unit/Service/FieldValueServiceTest.php index 28e436d..2b09232 100644 --- a/tests/php/Unit/Service/FieldValueServiceTest.php +++ b/tests/php/Unit/Service/FieldValueServiceTest.php @@ -19,19 +19,25 @@ use OCA\ProfileFields\Workflow\Event\ProfileFieldValueUpdatedEvent; use OCA\ProfileFields\Workflow\Event\ProfileFieldVisibilityUpdatedEvent; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class FieldValueServiceTest extends TestCase { private FieldValueMapper&MockObject $fieldValueMapper; private IEventDispatcher&MockObject $eventDispatcher; + private IL10N&MockObject $l10n; private FieldValueService $service; protected function setUp(): void { parent::setUp(); $this->fieldValueMapper = $this->createMock(FieldValueMapper::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); - $this->service = new FieldValueService($this->fieldValueMapper, $this->eventDispatcher); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnCallback( + static fn (string $text, array $parameters = []): string => $parameters === [] ? $text : vsprintf($text, $parameters), + ); + $this->service = new FieldValueService($this->fieldValueMapper, $this->eventDispatcher, $this->l10n); } public function testNormalizeNumberValue(): void { From a7c4add0cd346df04bd7d8085dbc7e11e6407be0 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:53:52 -0300 Subject: [PATCH 03/15] test(php): pass IL10N to FieldValueService in UserProfileFieldCheckTest Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/php/Unit/Workflow/UserProfileFieldCheckTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/Unit/Workflow/UserProfileFieldCheckTest.php b/tests/php/Unit/Workflow/UserProfileFieldCheckTest.php index b366491..8217f70 100644 --- a/tests/php/Unit/Workflow/UserProfileFieldCheckTest.php +++ b/tests/php/Unit/Workflow/UserProfileFieldCheckTest.php @@ -52,7 +52,7 @@ protected function setUp(): void { $this->userSession, $l10n, $this->fieldDefinitionService, - new FieldValueService($this->fieldValueMapper, $this->createMock(IEventDispatcher::class)), + new FieldValueService($this->fieldValueMapper, $this->createMock(IEventDispatcher::class), $l10n), $this->workflowSubjectContext, ); } From 010482965e2c8f9c56dbd1b96cf6583f0309dafb Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:53:53 -0300 Subject: [PATCH 04/15] test(php): pass IL10N to FieldValueService in LogProfileFieldChangeOperationTest Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../Workflow/LogProfileFieldChangeOperationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/php/ControllerIntegration/Workflow/LogProfileFieldChangeOperationTest.php b/tests/php/ControllerIntegration/Workflow/LogProfileFieldChangeOperationTest.php index 7de9c11..ff2449a 100644 --- a/tests/php/ControllerIntegration/Workflow/LogProfileFieldChangeOperationTest.php +++ b/tests/php/ControllerIntegration/Workflow/LogProfileFieldChangeOperationTest.php @@ -137,7 +137,7 @@ protected function setUp(): void { $urlGenerator->method('imagePath')->willReturn('/core/img/actions/profile.svg'); $subjectContext = new ProfileFieldValueSubjectContext(); - $fieldValueService = new FieldValueService($this->fieldValueMapper, $this->dispatcher); + $fieldValueService = new FieldValueService($this->fieldValueMapper, $this->dispatcher, $l10n); $check = new UserProfileFieldCheck( $this->userSession, $l10n, @@ -238,7 +238,7 @@ public function testUpsertDispatchTriggersConfiguredWorkflowOperation(): void { }), ); - $fieldValueService = new FieldValueService($this->fieldValueMapper, $this->dispatcher); + $fieldValueService = new FieldValueService($this->fieldValueMapper, $this->dispatcher, $this->createStub(IL10N::class)); $fieldValueService->upsert($this->definition, $userId, 'engineering', 'admin'); } From 4686c28162f5d3694d540be7dcf5f89c1d425a5e Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:02 -0300 Subject: [PATCH 05/15] test(php): assert raw exception message in FieldDefinitionApiController catch block Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../Controller/FieldDefinitionApiControllerTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/php/Unit/Controller/FieldDefinitionApiControllerTest.php b/tests/php/Unit/Controller/FieldDefinitionApiControllerTest.php index 4ffe647..2369d76 100644 --- a/tests/php/Unit/Controller/FieldDefinitionApiControllerTest.php +++ b/tests/php/Unit/Controller/FieldDefinitionApiControllerTest.php @@ -18,6 +18,7 @@ use OCA\ProfileFields\Service\FieldDefinitionService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IL10N; use OCP\IRequest; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -25,13 +26,18 @@ class FieldDefinitionApiControllerTest extends TestCase { private IRequest&MockObject $request; private FieldDefinitionService&MockObject $service; + private IL10N&MockObject $l10n; private FieldDefinitionApiController $controller; protected function setUp(): void { parent::setUp(); $this->request = $this->createMock(IRequest::class); $this->service = $this->createMock(FieldDefinitionService::class); - $this->controller = new FieldDefinitionApiController($this->request, $this->service); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnCallback(static function (string $text, array $parameters = []): string { + return $parameters === [] ? "tr:$text" : vsprintf("tr:$text", $parameters); + }); + $this->controller = new FieldDefinitionApiController($this->request, $this->service, $this->l10n); } public function testListReturnsSerializedDefinitions(): void { @@ -178,7 +184,7 @@ public function testUpdateReturnsNotFoundWhenDefinitionDoesNotExist(): void { ); $this->assertSame(Http::STATUS_NOT_FOUND, $response->getStatus()); - $this->assertSame(['message' => 'Field definition not found'], $response->getData()); + $this->assertSame(['message' => 'tr:Field definition not found'], $response->getData()); } public function testDeleteReturnsDeletedDefinition(): void { From 2dcac3c2a541281919e1d73d785bf8ca1c80ac74 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:02 -0300 Subject: [PATCH 06/15] test(php): assert raw exception message in FieldValueAdminApiController catch block Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../FieldValueAdminApiControllerTest.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/php/Unit/Controller/FieldValueAdminApiControllerTest.php b/tests/php/Unit/Controller/FieldValueAdminApiControllerTest.php index 6a67ea7..cd7d8c5 100644 --- a/tests/php/Unit/Controller/FieldValueAdminApiControllerTest.php +++ b/tests/php/Unit/Controller/FieldValueAdminApiControllerTest.php @@ -19,6 +19,7 @@ use OCA\ProfileFields\Service\FieldValueService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; @@ -30,6 +31,7 @@ class FieldValueAdminApiControllerTest extends TestCase { private FieldDefinitionService&MockObject $fieldDefinitionService; private FieldValueService&MockObject $fieldValueService; private IUserManager&MockObject $userManager; + private IL10N&MockObject $l10n; private FieldValueAdminApiController $controller; protected function setUp(): void { @@ -38,11 +40,16 @@ protected function setUp(): void { $this->fieldDefinitionService = $this->createMock(FieldDefinitionService::class); $this->fieldValueService = $this->createMock(FieldValueService::class); $this->userManager = $this->createMock(IUserManager::class); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnCallback(static function (string $text, array $parameters = []): string { + return $parameters === [] ? "tr:$text" : vsprintf("tr:$text", $parameters); + }); $this->controller = new FieldValueAdminApiController( $this->request, $this->fieldDefinitionService, $this->fieldValueService, $this->userManager, + $this->l10n, 'admin', ); } @@ -83,7 +90,7 @@ public function testUpsertReturnsNotFoundWhenDefinitionDoesNotExist(): void { $response = $this->controller->upsert('alice', 7, 'A+', FieldVisibility::USERS->value); $this->assertSame(Http::STATUS_NOT_FOUND, $response->getStatus()); - $this->assertSame(['message' => 'Field definition not found'], $response->getData()); + $this->assertSame(['message' => 'tr:Field definition not found'], $response->getData()); } public function testUpsertReturnsNotFoundWhenDefinitionIsInactive(): void { @@ -98,7 +105,7 @@ public function testUpsertReturnsNotFoundWhenDefinitionIsInactive(): void { $response = $this->controller->upsert('alice', 7, 'A+', FieldVisibility::USERS->value); $this->assertSame(Http::STATUS_NOT_FOUND, $response->getStatus()); - $this->assertSame(['message' => 'Field definition not found'], $response->getData()); + $this->assertSame(['message' => 'tr:Field definition not found'], $response->getData()); } public function testUpsertReturnsUnauthorizedWithoutAuthenticatedAdminUser(): void { @@ -107,13 +114,14 @@ public function testUpsertReturnsUnauthorizedWithoutAuthenticatedAdminUser(): vo $this->fieldDefinitionService, $this->fieldValueService, $this->userManager, + $this->l10n, null, ); $response = $controller->upsert('alice', 7, 'A+', FieldVisibility::USERS->value); $this->assertSame(Http::STATUS_UNAUTHORIZED, $response->getStatus()); - $this->assertSame(['message' => 'Authenticated admin user is required'], $response->getData()); + $this->assertSame(['message' => 'tr:Authenticated admin user is required'], $response->getData()); } public function testUpsertReturnsSerializedValue(): void { @@ -244,7 +252,7 @@ public function testLookupReturnsConflictWhenMoreThanOneUserMatches(): void { $response = $this->controller->lookup('cpf', '12345678900'); $this->assertSame(Http::STATUS_CONFLICT, $response->getStatus()); - $this->assertSame(['message' => 'Multiple users match the lookup field value'], $response->getData()); + $this->assertSame(['message' => 'tr:Multiple users match the lookup field value'], $response->getData()); } public function testSearchReturnsPaginatedMatchesWithDisplayNames(): void { @@ -310,7 +318,7 @@ public function testSearchReturnsNotFoundWhenDefinitionDoesNotExist(): void { $response = $this->controller->search('region', 'eq', 'LATAM'); $this->assertSame(Http::STATUS_NOT_FOUND, $response->getStatus()); - $this->assertSame(['message' => 'Search field definition not found'], $response->getData()); + $this->assertSame(['message' => 'tr:Search field definition not found'], $response->getData()); } private function buildDefinition(): FieldDefinition { From e7f961bd97255d6e1461bf3be0dc591a5c6ebf23 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:02 -0300 Subject: [PATCH 07/15] test(php): assert raw exception message in FieldValueApiController catch blocks Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../FieldValueApiControllerTest.php | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/php/Unit/Controller/FieldValueApiControllerTest.php b/tests/php/Unit/Controller/FieldValueApiControllerTest.php index f0395d9..23474d2 100644 --- a/tests/php/Unit/Controller/FieldValueApiControllerTest.php +++ b/tests/php/Unit/Controller/FieldValueApiControllerTest.php @@ -20,6 +20,7 @@ use OCA\ProfileFields\Service\FieldValueService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IL10N; use OCP\IRequest; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; @@ -30,6 +31,7 @@ class FieldValueApiControllerTest extends TestCase { private FieldDefinitionService&MockObject $fieldDefinitionService; private FieldValueService&MockObject $fieldValueService; private FieldAccessService&MockObject $fieldAccessService; + private IL10N&MockObject $l10n; private FieldValueApiController $controller; protected function setUp(): void { @@ -38,11 +40,16 @@ protected function setUp(): void { $this->fieldDefinitionService = $this->createMock(FieldDefinitionService::class); $this->fieldValueService = $this->createMock(FieldValueService::class); $this->fieldAccessService = $this->createMock(FieldAccessService::class); + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnCallback(static function (string $text, array $parameters = []): string { + return $parameters === [] ? "tr:$text" : vsprintf("tr:$text", $parameters); + }); $this->controller = new FieldValueApiController( $this->request, $this->fieldDefinitionService, $this->fieldValueService, $this->fieldAccessService, + $this->l10n, 'alice', ); } @@ -96,13 +103,14 @@ public function testIndexReturnsUnauthorizedWhenUserIsMissing(): void { $this->fieldDefinitionService, $this->fieldValueService, $this->fieldAccessService, + $this->l10n, null, ); $response = $controller->index(); $this->assertSame(Http::STATUS_UNAUTHORIZED, $response->getStatus()); - $this->assertSame(['message' => 'Authenticated user is required'], $response->getData()); + $this->assertSame(['message' => 'tr:Authenticated user is required'], $response->getData()); } #[DataProvider('forbiddenUpsertProvider')] @@ -142,9 +150,9 @@ public static function forbiddenUpsertProvider(): array { $forbidden->setActive(true); return [ - 'missing definition' => [null, Http::STATUS_NOT_FOUND, 'Field definition not found'], - 'inactive definition' => [$inactive, Http::STATUS_NOT_FOUND, 'Field definition not found'], - 'not editable by user' => [$forbidden, Http::STATUS_FORBIDDEN, 'Field cannot be edited by the user'], + 'missing definition' => [null, Http::STATUS_NOT_FOUND, 'tr:Field definition not found'], + 'inactive definition' => [$inactive, Http::STATUS_NOT_FOUND, 'tr:Field definition not found'], + 'not editable by user' => [$forbidden, Http::STATUS_FORBIDDEN, 'tr:Field cannot be edited by the user'], ]; } @@ -154,13 +162,14 @@ public function testUpsertReturnsUnauthorizedWhenUserIsMissing(): void { $this->fieldDefinitionService, $this->fieldValueService, $this->fieldAccessService, + $this->l10n, null, ); $response = $controller->upsert(7, 'A+'); $this->assertSame(Http::STATUS_UNAUTHORIZED, $response->getStatus()); - $this->assertSame(['message' => 'Authenticated user is required'], $response->getData()); + $this->assertSame(['message' => 'tr:Authenticated user is required'], $response->getData()); } public function testUpsertStoresOwnValueWhenFieldIsEditable(): void { @@ -271,10 +280,10 @@ public static function updateVisibilityRejectionProvider(): array { $editableButVisibilityForbidden->setExposurePolicy(\OCA\ProfileFields\Enum\FieldExposurePolicy::PRIVATE->value); return [ - 'missing definition' => [null, null, null, Http::STATUS_NOT_FOUND, 'Field definition not found'], - 'inactive definition' => [$inactive, null, null, Http::STATUS_NOT_FOUND, 'Field definition not found'], - 'not editable by user' => [$forbidden, false, null, Http::STATUS_FORBIDDEN, 'Field cannot be edited by the user'], - 'visibility forbidden' => [$editableButVisibilityForbidden, true, false, Http::STATUS_FORBIDDEN, 'Field visibility cannot be changed by the user'], + 'missing definition' => [null, null, null, Http::STATUS_NOT_FOUND, 'tr:Field definition not found'], + 'inactive definition' => [$inactive, null, null, Http::STATUS_NOT_FOUND, 'tr:Field definition not found'], + 'not editable by user' => [$forbidden, false, null, Http::STATUS_FORBIDDEN, 'tr:Field cannot be edited by the user'], + 'visibility forbidden' => [$editableButVisibilityForbidden, true, false, Http::STATUS_FORBIDDEN, 'tr:Field visibility cannot be changed by the user'], ]; } @@ -284,13 +293,14 @@ public function testUpdateVisibilityReturnsUnauthorizedWhenUserIsMissing(): void $this->fieldDefinitionService, $this->fieldValueService, $this->fieldAccessService, + $this->l10n, null, ); $response = $controller->updateVisibility(7, FieldVisibility::USERS->value); $this->assertSame(Http::STATUS_UNAUTHORIZED, $response->getStatus()); - $this->assertSame(['message' => 'Authenticated user is required'], $response->getData()); + $this->assertSame(['message' => 'tr:Authenticated user is required'], $response->getData()); } public function testUpdateVisibilityStoresUpdatedVisibility(): void { From 8f30b2300f60b16bbb4ec1132bf9a35bfcf579e9 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:11 -0300 Subject: [PATCH 08/15] test(php): pass IL10N to FieldDefinitionApiController in integration test Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../Controller/FieldDefinitionApiControllerTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/php/ControllerIntegration/Controller/FieldDefinitionApiControllerTest.php b/tests/php/ControllerIntegration/Controller/FieldDefinitionApiControllerTest.php index 4ee3763..cabe4df 100644 --- a/tests/php/ControllerIntegration/Controller/FieldDefinitionApiControllerTest.php +++ b/tests/php/ControllerIntegration/Controller/FieldDefinitionApiControllerTest.php @@ -21,6 +21,7 @@ use OCP\AppFramework\Http; use OCP\DB\ISchemaWrapper; use OCP\IDBConnection; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; @@ -76,6 +77,7 @@ public function testDefinitionCrudFlow(): void { $controller = new FieldDefinitionApiController( $this->createMock(IRequest::class), $this->fieldDefinitionService, + $this->createL10n(), ); $createResponse = $controller->create( @@ -133,6 +135,12 @@ private function createUser(string $userId): string { return $userId; } + private function createL10n(): IL10N { + $l10n = $this->createStub(IL10N::class); + $l10n->method('t')->willReturnCallback(static fn (string $text, array $parameters = []): string => $parameters === [] ? $text : vsprintf($text, $parameters)); + return $l10n; + } + private static function ensureSchemaExists(IDBConnection $connection): void { if ($connection->tableExists('profile_fields_definitions') && $connection->tableExists('profile_fields_values')) { return; From cb8cc4fd8b812ec437708b561ac3823b4fd46945 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:11 -0300 Subject: [PATCH 09/15] test(php): pass IL10N to FieldValueAdminApiController in integration test Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../Controller/FieldValueAdminApiControllerTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/php/ControllerIntegration/Controller/FieldValueAdminApiControllerTest.php b/tests/php/ControllerIntegration/Controller/FieldValueAdminApiControllerTest.php index 3e5fd38..b3a5db1 100644 --- a/tests/php/ControllerIntegration/Controller/FieldValueAdminApiControllerTest.php +++ b/tests/php/ControllerIntegration/Controller/FieldValueAdminApiControllerTest.php @@ -23,6 +23,7 @@ use OCP\AppFramework\Http; use OCP\DB\ISchemaWrapper; use OCP\IDBConnection; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; @@ -100,6 +101,7 @@ public function testAdminValueFlow(): void { $this->fieldDefinitionService, $this->fieldValueService, $this->userManager, + $this->createL10n(), $this->currentUserId, ); @@ -150,6 +152,7 @@ public function testAdminCanLookupUserByCpfAndRetrieveCooperativeFields(): void $this->fieldDefinitionService, $this->fieldValueService, $this->userManager, + $this->createL10n(), $this->currentUserId, ); @@ -187,6 +190,7 @@ public function testAdminCanSearchUsersByFieldWithPagination(): void { $this->fieldDefinitionService, $this->fieldValueService, $this->userManager, + $this->createL10n(), $this->currentUserId, ); @@ -249,6 +253,12 @@ private function createUser(string $userId): string { return $userId; } + private function createL10n(): IL10N { + $l10n = $this->createStub(IL10N::class); + $l10n->method('t')->willReturnCallback(static fn (string $text, array $parameters = []): string => $parameters === [] ? $text : vsprintf($text, $parameters)); + return $l10n; + } + private static function ensureSchemaExists(IDBConnection $connection): void { if ($connection->tableExists('profile_fields_definitions') && $connection->tableExists('profile_fields_values')) { return; From 5c269374d7f5a87a10ce60286b2d2907d1c4146e Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:11 -0300 Subject: [PATCH 10/15] test(php): pass IL10N to FieldValueApiController in integration test Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../Controller/FieldValueApiControllerTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/php/ControllerIntegration/Controller/FieldValueApiControllerTest.php b/tests/php/ControllerIntegration/Controller/FieldValueApiControllerTest.php index 05ab59b..8fb1673 100644 --- a/tests/php/ControllerIntegration/Controller/FieldValueApiControllerTest.php +++ b/tests/php/ControllerIntegration/Controller/FieldValueApiControllerTest.php @@ -27,6 +27,7 @@ use OCP\AppFramework\Http; use OCP\DB\ISchemaWrapper; use OCP\IDBConnection; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; @@ -108,6 +109,7 @@ public function testSelfServiceFlowListsEditableFieldsAndUpdatesVisibility(): vo $this->fieldDefinitionService, $this->fieldValueService, $this->fieldAccessService, + $this->createL10n(), $this->currentUserId, ); @@ -171,6 +173,12 @@ private function createUser(string $userId): string { return $userId; } + private function createL10n(): IL10N { + $l10n = $this->createStub(IL10N::class); + $l10n->method('t')->willReturnCallback(static fn (string $text, array $parameters = []): string => $parameters === [] ? $text : vsprintf($text, $parameters)); + return $l10n; + } + private function insertValue(int $fieldDefinitionId, string $userId, string $valueJson, string $visibility): FieldValue { $value = new FieldValue(); $value->setFieldDefinitionId($fieldDefinitionId); From 4ef29c1de429fb23fe72b046e4306db8670b2fde Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:17 -0300 Subject: [PATCH 11/15] refactor(php): inject IL10N into FieldDefinitionService and translate throws with literal strings Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/FieldDefinitionService.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Service/FieldDefinitionService.php b/lib/Service/FieldDefinitionService.php index fcf758b..8638c76 100644 --- a/lib/Service/FieldDefinitionService.php +++ b/lib/Service/FieldDefinitionService.php @@ -15,12 +15,14 @@ use OCA\ProfileFields\Db\FieldDefinition; use OCA\ProfileFields\Db\FieldDefinitionMapper; use OCA\ProfileFields\Db\FieldValueMapper; +use OCP\IL10N; class FieldDefinitionService { public function __construct( private FieldDefinitionMapper $fieldDefinitionMapper, private FieldValueMapper $fieldValueMapper, private FieldDefinitionValidator $validator, + private IL10N $l10n, ) { } @@ -30,7 +32,7 @@ public function __construct( public function create(array $definition): FieldDefinition { $validated = $this->validator->validate($definition); if ($this->fieldDefinitionMapper->findByFieldKey($validated['field_key']) !== null) { - throw new InvalidArgumentException('field_key already exists'); + throw new InvalidArgumentException($this->l10n->t('field_key already exists')); } $createdAt = $this->parseImportedDate($definition['created_at'] ?? null) ?? new DateTime(); @@ -46,7 +48,7 @@ public function create(array $definition): FieldDefinition { try { $entity->setOptions(isset($validated['options']) ? json_encode($validated['options'], JSON_THROW_ON_ERROR) : null); } catch (JsonException $e) { - throw new InvalidArgumentException('options could not be encoded: ' . $e->getMessage(), 0, $e); + throw new InvalidArgumentException($this->l10n->t('options could not be encoded: %s', [$e->getMessage()]), 0, $e); } $entity->setCreatedAt($createdAt); $entity->setUpdatedAt($updatedAt); @@ -60,11 +62,11 @@ public function create(array $definition): FieldDefinition { public function update(FieldDefinition $existing, array $definition): FieldDefinition { $validated = $this->validator->validate($definition + ['field_key' => $existing->getFieldKey()]); if (($definition['field_key'] ?? $existing->getFieldKey()) !== $existing->getFieldKey()) { - throw new InvalidArgumentException('field_key cannot be changed'); + throw new InvalidArgumentException($this->l10n->t('field_key cannot be changed')); } if ($validated['type'] !== $existing->getType() && $this->fieldValueMapper->hasValuesForFieldDefinitionId($existing->getId())) { - throw new InvalidArgumentException('type cannot be changed after values exist'); + throw new InvalidArgumentException($this->l10n->t('type cannot be changed after values exist')); } $existing->setLabel($validated['label']); @@ -76,7 +78,7 @@ public function update(FieldDefinition $existing, array $definition): FieldDefin try { $existing->setOptions(isset($validated['options']) ? json_encode($validated['options'], JSON_THROW_ON_ERROR) : null); } catch (JsonException $e) { - throw new InvalidArgumentException('options could not be encoded: ' . $e->getMessage(), 0, $e); + throw new InvalidArgumentException($this->l10n->t('options could not be encoded: %s', [$e->getMessage()]), 0, $e); } $existing->setUpdatedAt($this->parseImportedDate($definition['updated_at'] ?? null) ?? new DateTime()); From 009f128f91a817d5b10177b53691592165349f6a Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:17 -0300 Subject: [PATCH 12/15] refactor(php): inject IL10N into FieldValueService and translate throws with literal strings Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/FieldValueService.php | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/Service/FieldValueService.php b/lib/Service/FieldValueService.php index f527f4b..e0934cc 100644 --- a/lib/Service/FieldValueService.php +++ b/lib/Service/FieldValueService.php @@ -24,6 +24,7 @@ use OCA\ProfileFields\Workflow\Event\ProfileFieldVisibilityUpdatedEvent; use OCA\ProfileFields\Workflow\ProfileFieldValueWorkflowSubject; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IL10N; class FieldValueService { private const SEARCH_OPERATOR_EQ = 'eq'; @@ -33,6 +34,7 @@ class FieldValueService { public function __construct( private FieldValueMapper $fieldValueMapper, private IEventDispatcher $eventDispatcher, + private IL10N $l10n, ) { } @@ -51,7 +53,7 @@ public function upsert( $valueJson = $this->encodeValue($normalizedValue); $visibility = $currentVisibility ?? FieldExposurePolicy::from($definition->getExposurePolicy())->initialVisibility()->value; if (!FieldVisibility::isValid($visibility)) { - throw new InvalidArgumentException('current_visibility is not supported'); + throw new InvalidArgumentException($this->l10n->t('current_visibility is not supported')); } $entity = $this->fieldValueMapper->findByFieldDefinitionIdAndUserUid($definition->getId(), $userUid) ?? new FieldValue(); @@ -143,16 +145,16 @@ public function searchByDefinition( int $offset, ): array { if ($limit < 1 || $limit > self::SEARCH_MAX_LIMIT) { - throw new InvalidArgumentException(sprintf('limit must be between 1 and %d', self::SEARCH_MAX_LIMIT)); + throw new InvalidArgumentException($this->l10n->t('limit must be between 1 and %d', [self::SEARCH_MAX_LIMIT])); } if ($offset < 0) { - throw new InvalidArgumentException('offset must be greater than or equal to 0'); + throw new InvalidArgumentException($this->l10n->t('offset must be greater than or equal to 0')); } $normalizedOperator = strtolower(trim($operator)); if (!in_array($normalizedOperator, [self::SEARCH_OPERATOR_EQ, self::SEARCH_OPERATOR_CONTAINS], true)) { - throw new InvalidArgumentException('search operator is not supported'); + throw new InvalidArgumentException($this->l10n->t('search operator is not supported')); } $searchValue = $this->normalizeSearchValue($definition, $normalizedOperator, $rawValue); @@ -175,12 +177,12 @@ public function searchByDefinition( public function updateVisibility(FieldDefinition $definition, string $userUid, string $updatedByUid, string $currentVisibility): FieldValue { if (!FieldVisibility::isValid($currentVisibility)) { - throw new InvalidArgumentException('current_visibility is not supported'); + throw new InvalidArgumentException($this->l10n->t('current_visibility is not supported')); } $entity = $this->fieldValueMapper->findByFieldDefinitionIdAndUserUid($definition->getId(), $userUid); if ($entity === null) { - throw new InvalidArgumentException('field value not found'); + throw new InvalidArgumentException($this->l10n->t('field value not found')); } $previousValue = $this->extractScalarValue($entity->getValueJson()); @@ -227,7 +229,7 @@ public function serializeForResponse(FieldValue $value): array { */ private function normalizeTextValue(array|string|int|float|bool $rawValue): array { if (is_array($rawValue)) { - throw new InvalidArgumentException('text fields expect a scalar value'); + throw new InvalidArgumentException($this->l10n->t('text fields expect a scalar value')); } return ['value' => trim((string)$rawValue)]; @@ -239,13 +241,13 @@ private function normalizeTextValue(array|string|int|float|bool $rawValue): arra */ private function normalizeSelectValue(array|string|int|float|bool $rawValue, FieldDefinition $definition): array { if (!is_string($rawValue)) { - throw new InvalidArgumentException('select fields expect a string value'); + throw new InvalidArgumentException($this->l10n->t('select fields expect a string value')); } $value = trim($rawValue); $options = json_decode($definition->getOptions() ?? '[]', true); if (!in_array($value, $options, true)) { - throw new InvalidArgumentException(sprintf('"%s" is not a valid option for this field', $value)); + throw new InvalidArgumentException($this->l10n->t('"%s" is not a valid option for this field', [$value])); } return ['value' => $value]; @@ -257,7 +259,7 @@ private function normalizeSelectValue(array|string|int|float|bool $rawValue, Fie */ private function normalizeNumberValue(array|string|int|float|bool $rawValue): array { if (is_array($rawValue) || is_bool($rawValue) || !is_numeric($rawValue)) { - throw new InvalidArgumentException('number fields expect a numeric value'); + throw new InvalidArgumentException($this->l10n->t('number fields expect a numeric value')); } return ['value' => str_contains((string)$rawValue, '.') ? (float)$rawValue : (int)$rawValue]; @@ -270,7 +272,7 @@ private function encodeValue(array $value): string { try { return json_encode($value, JSON_THROW_ON_ERROR); } catch (JsonException $exception) { - throw new InvalidArgumentException('value_json could not be encoded', 0, $exception); + throw new InvalidArgumentException($this->l10n->t('value_json could not be encoded'), 0, $exception); } } @@ -281,11 +283,11 @@ private function decodeValue(string $valueJson): array { try { $decoded = json_decode($valueJson, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $exception) { - throw new InvalidArgumentException('value_json could not be decoded', 0, $exception); + throw new InvalidArgumentException($this->l10n->t('value_json could not be decoded'), 0, $exception); } if (!is_array($decoded)) { - throw new InvalidArgumentException('value_json must decode to an object payload'); + throw new InvalidArgumentException($this->l10n->t('value_json must decode to an object payload')); } return $decoded; @@ -326,13 +328,13 @@ private function normalizeSearchValue(FieldDefinition $definition, string $opera } if (FieldType::from($definition->getType()) !== FieldType::TEXT) { - throw new InvalidArgumentException('contains operator is only supported for text fields'); + throw new InvalidArgumentException($this->l10n->t('contains operator is only supported for text fields')); } $normalized = $this->normalizeValue($definition, $rawValue); $value = $normalized['value'] ?? null; if (!is_string($value) || $value === '') { - throw new InvalidArgumentException('contains operator requires a non-empty text value'); + throw new InvalidArgumentException($this->l10n->t('contains operator requires a non-empty text value')); } return ['value' => $value]; From c5e184b1774ff18f0498eb49bbe158cb7c4f2ab2 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:24 -0300 Subject: [PATCH 13/15] refactor(php): remove t() wrapping from catch blocks in FieldDefinitionApiController Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Controller/FieldDefinitionApiController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Controller/FieldDefinitionApiController.php b/lib/Controller/FieldDefinitionApiController.php index f8585ae..0326f28 100644 --- a/lib/Controller/FieldDefinitionApiController.php +++ b/lib/Controller/FieldDefinitionApiController.php @@ -17,6 +17,7 @@ use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; +use OCP\IL10N; use OCP\IRequest; /** @@ -26,6 +27,7 @@ class FieldDefinitionApiController extends OCSController { public function __construct( IRequest $request, private FieldDefinitionService $fieldDefinitionService, + private IL10N $l10n, ) { parent::__construct(Application::APP_ID, $request); } @@ -129,7 +131,7 @@ public function update( ): DataResponse { $existing = $this->fieldDefinitionService->findById($id); if ($existing === null) { - return new DataResponse(['message' => 'Field definition not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['message' => $this->l10n->t('Field definition not found')], Http::STATUS_NOT_FOUND); } try { @@ -167,7 +169,7 @@ public function update( public function delete(int $id): DataResponse { $definition = $this->fieldDefinitionService->delete($id); if ($definition === null) { - return new DataResponse(['message' => 'Field definition not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['message' => $this->l10n->t('Field definition not found')], Http::STATUS_NOT_FOUND); } return new DataResponse($definition->jsonSerialize(), Http::STATUS_OK); From 364cc7d6beaf76832fe3a5027d0599bb3ea37c08 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:24 -0300 Subject: [PATCH 14/15] refactor(php): remove t() wrapping from catch blocks in FieldValueAdminApiController Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../FieldValueAdminApiController.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Controller/FieldValueAdminApiController.php b/lib/Controller/FieldValueAdminApiController.php index 5a53fa3..f6f9701 100644 --- a/lib/Controller/FieldValueAdminApiController.php +++ b/lib/Controller/FieldValueAdminApiController.php @@ -19,6 +19,7 @@ use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; +use OCP\IL10N; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; @@ -35,6 +36,7 @@ public function __construct( private FieldDefinitionService $fieldDefinitionService, private FieldValueService $fieldValueService, private IUserManager $userManager, + private IL10N $l10n, private ?string $userId, ) { parent::__construct(Application::APP_ID, $request); @@ -82,12 +84,12 @@ public function upsert( ?string $currentVisibility = null, ): DataResponse { if ($this->userId === null) { - return new DataResponse(['message' => 'Authenticated admin user is required'], Http::STATUS_UNAUTHORIZED); + return new DataResponse(['message' => $this->l10n->t('Authenticated admin user is required')], Http::STATUS_UNAUTHORIZED); } $definition = $this->fieldDefinitionService->findById($fieldDefinitionId); if ($definition === null || !$definition->getActive()) { - return new DataResponse(['message' => 'Field definition not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['message' => $this->l10n->t('Field definition not found')], Http::STATUS_NOT_FOUND); } try { @@ -122,12 +124,12 @@ public function lookup( array|string|int|float|bool|null $fieldValue = null, ): DataResponse { if ($this->userId === null) { - return new DataResponse(['message' => 'Authenticated admin user is required'], Http::STATUS_UNAUTHORIZED); + return new DataResponse(['message' => $this->l10n->t('Authenticated admin user is required')], Http::STATUS_UNAUTHORIZED); } $definition = $this->fieldDefinitionService->findByFieldKey($fieldKey); if ($definition === null || !$definition->getActive()) { - return new DataResponse(['message' => 'Lookup field definition not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['message' => $this->l10n->t('Lookup field definition not found')], Http::STATUS_NOT_FOUND); } try { @@ -137,11 +139,11 @@ public function lookup( } if ($matches === []) { - return new DataResponse(['message' => 'User not found for lookup field value'], Http::STATUS_NOT_FOUND); + return new DataResponse(['message' => $this->l10n->t('User not found for lookup field value')], Http::STATUS_NOT_FOUND); } if (count($matches) > 1) { - return new DataResponse(['message' => 'Multiple users match the lookup field value'], Http::STATUS_CONFLICT); + return new DataResponse(['message' => $this->l10n->t('Multiple users match the lookup field value')], Http::STATUS_CONFLICT); } return new DataResponse($this->serializeLookupResult($definition, $matches[0]), Http::STATUS_OK); @@ -174,12 +176,12 @@ public function search( int $offset = 0, ): DataResponse { if ($this->userId === null) { - return new DataResponse(['message' => 'Authenticated admin user is required'], Http::STATUS_UNAUTHORIZED); + return new DataResponse(['message' => $this->l10n->t('Authenticated admin user is required')], Http::STATUS_UNAUTHORIZED); } $definition = $this->fieldDefinitionService->findByFieldKey($fieldKey); if ($definition === null || !$definition->getActive()) { - return new DataResponse(['message' => 'Search field definition not found'], Http::STATUS_NOT_FOUND); + return new DataResponse(['message' => $this->l10n->t('Search field definition not found')], Http::STATUS_NOT_FOUND); } try { From fc59d73b28471a158353ef85a5b5a19bb8438811 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Fri, 20 Mar 2026 12:54:24 -0300 Subject: [PATCH 15/15] refactor(php): remove t() wrapping from catch blocks in FieldValueApiController Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Controller/FieldValueApiController.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/Controller/FieldValueApiController.php b/lib/Controller/FieldValueApiController.php index afd4efc..52ba1a9 100644 --- a/lib/Controller/FieldValueApiController.php +++ b/lib/Controller/FieldValueApiController.php @@ -19,6 +19,7 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; +use OCP\IL10N; use OCP\IRequest; /** @@ -32,6 +33,7 @@ public function __construct( private FieldDefinitionService $fieldDefinitionService, private FieldValueService $fieldValueService, private FieldAccessService $fieldAccessService, + private IL10N $l10n, private ?string $userId, ) { parent::__construct(Application::APP_ID, $request); @@ -52,7 +54,7 @@ public function __construct( #[ApiRoute(verb: 'GET', url: '/api/v1/me/values')] public function index(): DataResponse { if ($this->userId === null) { - return new DataResponse(['message' => 'Authenticated user is required'], 401); + return new DataResponse(['message' => $this->l10n->t('Authenticated user is required')], 401); } $definitions = $this->fieldDefinitionService->findActiveOrdered(); @@ -113,20 +115,20 @@ public function upsert( ?string $currentVisibility = null, ): DataResponse { if ($this->userId === null) { - return new DataResponse(['message' => 'Authenticated user is required'], 401); + return new DataResponse(['message' => $this->l10n->t('Authenticated user is required')], 401); } $definition = $this->fieldDefinitionService->findById($fieldDefinitionId); if ($definition === null || !$definition->getActive()) { - return new DataResponse(['message' => 'Field definition not found'], 404); + return new DataResponse(['message' => $this->l10n->t('Field definition not found')], 404); } if (!$this->fieldAccessService->canEditValue($this->userId, $this->userId, $definition, false)) { - return new DataResponse(['message' => 'Field cannot be edited by the user'], 403); + return new DataResponse(['message' => $this->l10n->t('Field cannot be edited by the user')], 403); } if ($currentVisibility !== null && !$this->fieldAccessService->canChangeVisibility($this->userId, $this->userId, false)) { - return new DataResponse(['message' => 'Field visibility cannot be changed by the user'], 403); + return new DataResponse(['message' => $this->l10n->t('Field visibility cannot be changed by the user')], 403); } try { @@ -160,20 +162,20 @@ public function updateVisibility( string $currentVisibility, ): DataResponse { if ($this->userId === null) { - return new DataResponse(['message' => 'Authenticated user is required'], 401); + return new DataResponse(['message' => $this->l10n->t('Authenticated user is required')], 401); } $definition = $this->fieldDefinitionService->findById($fieldDefinitionId); if ($definition === null || !$definition->getActive()) { - return new DataResponse(['message' => 'Field definition not found'], 404); + return new DataResponse(['message' => $this->l10n->t('Field definition not found')], 404); } if (!$this->fieldAccessService->canEditValue($this->userId, $this->userId, $definition, false)) { - return new DataResponse(['message' => 'Field cannot be edited by the user'], 403); + return new DataResponse(['message' => $this->l10n->t('Field cannot be edited by the user')], 403); } if (!$this->fieldAccessService->canChangeVisibility($this->userId, $this->userId, false)) { - return new DataResponse(['message' => 'Field visibility cannot be changed by the user'], 403); + return new DataResponse(['message' => $this->l10n->t('Field visibility cannot be changed by the user')], 403); } try {