Skip to content

Commit a98a00e

Browse files
committed
test(search): cover profile directory search service
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 79a6f7a commit a98a00e

File tree

1 file changed

+204
-0
lines changed

1 file changed

+204
-0
lines changed
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OCA\ProfileFields\Tests\Unit\Search;
11+
12+
use DateTime;
13+
use OCA\ProfileFields\Db\FieldDefinition;
14+
use OCA\ProfileFields\Db\FieldValue;
15+
use OCA\ProfileFields\Db\FieldValueMapper;
16+
use OCA\ProfileFields\Search\ProfileFieldDirectorySearchService;
17+
use OCA\ProfileFields\Service\FieldDefinitionService;
18+
use OCP\IGroupManager;
19+
use OCP\IUser;
20+
use OCP\IUserManager;
21+
use PHPUnit\Framework\MockObject\MockObject;
22+
use PHPUnit\Framework\TestCase;
23+
24+
class ProfileFieldDirectorySearchServiceTest extends TestCase {
25+
private FieldDefinitionService&MockObject $fieldDefinitionService;
26+
private FieldValueMapper&MockObject $fieldValueMapper;
27+
private IUserManager&MockObject $userManager;
28+
private IGroupManager&MockObject $groupManager;
29+
private ProfileFieldDirectorySearchService $service;
30+
31+
protected function setUp(): void {
32+
parent::setUp();
33+
34+
$this->fieldDefinitionService = $this->createMock(FieldDefinitionService::class);
35+
$this->fieldValueMapper = $this->createMock(FieldValueMapper::class);
36+
$this->userManager = $this->createMock(IUserManager::class);
37+
$this->groupManager = $this->createMock(IGroupManager::class);
38+
39+
$this->service = new ProfileFieldDirectorySearchService(
40+
$this->fieldDefinitionService,
41+
$this->fieldValueMapper,
42+
$this->userManager,
43+
$this->groupManager,
44+
);
45+
}
46+
47+
public function testSearchReturnsVisibleMatchesGroupedByUser(): void {
48+
$actor = $this->createMock(IUser::class);
49+
$actor->method('getUID')->willReturn('analyst');
50+
51+
$region = $this->buildDefinition(10, 'region', 'Region');
52+
$team = $this->buildDefinition(20, 'team', 'Team');
53+
54+
$this->fieldDefinitionService->expects($this->once())
55+
->method('findActiveOrdered')
56+
->willReturn([$region, $team]);
57+
$this->fieldValueMapper->expects($this->once())
58+
->method('findAllOrdered')
59+
->willReturn([
60+
$this->buildValue(10, 'alice', 'public', 'LATAM'),
61+
$this->buildValue(20, 'alice', 'users', 'Platform'),
62+
$this->buildValue(10, 'bob', 'private', 'LATAM'),
63+
]);
64+
$this->groupManager->expects($this->once())
65+
->method('isAdmin')
66+
->with('analyst')
67+
->willReturn(false);
68+
$this->userManager->expects($this->once())
69+
->method('get')
70+
->with('alice')
71+
->willReturn($this->buildUser('Alice Doe'));
72+
73+
$result = $this->service->search($actor, 'latam', 10, 0);
74+
75+
$this->assertSame(1, $result['total']);
76+
$this->assertSame('alice', $result['items'][0]['user_uid']);
77+
$this->assertSame('Alice Doe', $result['items'][0]['display_name']);
78+
$this->assertSame([
79+
[
80+
'field_key' => 'region',
81+
'field_label' => 'Region',
82+
'value' => 'LATAM',
83+
],
84+
], $result['items'][0]['matched_fields']);
85+
}
86+
87+
public function testSearchDoesNotExposePrivateOrHiddenFieldsToNonAdmins(): void {
88+
$actor = $this->createMock(IUser::class);
89+
$actor->method('getUID')->willReturn('alice');
90+
91+
$publicDefinition = $this->buildDefinition(10, 'region', 'Region');
92+
$hiddenDefinition = $this->buildDefinition(20, 'secret_region', 'Secret Region', userVisible: false);
93+
94+
$this->fieldDefinitionService->expects($this->once())
95+
->method('findActiveOrdered')
96+
->willReturn([$publicDefinition, $hiddenDefinition]);
97+
$this->fieldValueMapper->expects($this->once())
98+
->method('findAllOrdered')
99+
->willReturn([
100+
$this->buildValue(10, 'alice', 'private', 'LATAM - Private'),
101+
$this->buildValue(10, 'bob', 'users', 'LATAM - Users'),
102+
$this->buildValue(20, 'carol', 'public', 'LATAM - Hidden'),
103+
]);
104+
$this->groupManager->expects($this->once())
105+
->method('isAdmin')
106+
->with('alice')
107+
->willReturn(false);
108+
$this->userManager->expects($this->once())
109+
->method('get')
110+
->with('bob')
111+
->willReturn($this->buildUser('Bob Doe'));
112+
113+
$result = $this->service->search($actor, 'latam', 10, 0);
114+
115+
$this->assertSame(1, $result['total']);
116+
$this->assertSame('bob', $result['items'][0]['user_uid']);
117+
$this->assertSame('LATAM - Users', $result['items'][0]['matched_fields'][0]['value']);
118+
}
119+
120+
public function testSearchAllowsAdminToFindPrivateAndHiddenFields(): void {
121+
$actor = $this->createMock(IUser::class);
122+
$actor->method('getUID')->willReturn('admin');
123+
124+
$hiddenDefinition = $this->buildDefinition(10, 'secret_region', 'Secret Region', userVisible: false);
125+
126+
$this->fieldDefinitionService->expects($this->once())
127+
->method('findActiveOrdered')
128+
->willReturn([$hiddenDefinition]);
129+
$this->fieldValueMapper->expects($this->once())
130+
->method('findAllOrdered')
131+
->willReturn([
132+
$this->buildValue(10, 'alice', 'private', 'LATAM - Private'),
133+
]);
134+
$this->groupManager->expects($this->once())
135+
->method('isAdmin')
136+
->with('admin')
137+
->willReturn(true);
138+
$this->userManager->expects($this->once())
139+
->method('get')
140+
->with('alice')
141+
->willReturn($this->buildUser('Alice Doe'));
142+
143+
$result = $this->service->search($actor, 'latam', 10, 0);
144+
145+
$this->assertSame(1, $result['total']);
146+
$this->assertSame('alice', $result['items'][0]['user_uid']);
147+
$this->assertSame('LATAM - Private', $result['items'][0]['matched_fields'][0]['value']);
148+
}
149+
150+
public function testSearchSupportsPagination(): void {
151+
$region = $this->buildDefinition(10, 'region', 'Region');
152+
153+
$this->fieldDefinitionService->method('findActiveOrdered')->willReturn([$region]);
154+
$this->fieldValueMapper->method('findAllOrdered')->willReturn([
155+
$this->buildValue(10, 'alice', 'public', 'LATAM'),
156+
$this->buildValue(10, 'bruno', 'public', 'LATAM'),
157+
]);
158+
$this->userManager->method('get')->willReturnCallback(fn (string $uid): IUser => $this->buildUser(strtoupper($uid)));
159+
160+
$result = $this->service->search(null, 'latam', 1, 1);
161+
162+
$this->assertSame(2, $result['total']);
163+
$this->assertCount(1, $result['items']);
164+
$this->assertSame('bruno', $result['items'][0]['user_uid']);
165+
}
166+
167+
private function buildDefinition(int $id, string $fieldKey, string $label, bool $userVisible = true): FieldDefinition {
168+
$definition = new FieldDefinition();
169+
$definition->setId($id);
170+
$definition->setFieldKey($fieldKey);
171+
$definition->setLabel($label);
172+
$definition->setType('text');
173+
$definition->setAdminOnly(false);
174+
$definition->setUserEditable(true);
175+
$definition->setUserVisible($userVisible);
176+
$definition->setInitialVisibility('private');
177+
$definition->setSortOrder(0);
178+
$definition->setActive(true);
179+
$definition->setCreatedAt(new DateTime());
180+
$definition->setUpdatedAt(new DateTime());
181+
182+
return $definition;
183+
}
184+
185+
private function buildValue(int $fieldDefinitionId, string $userUid, string $visibility, string $value): FieldValue {
186+
$fieldValue = new FieldValue();
187+
$fieldValue->setId(random_int(1, 9999));
188+
$fieldValue->setFieldDefinitionId($fieldDefinitionId);
189+
$fieldValue->setUserUid($userUid);
190+
$fieldValue->setValueJson(json_encode(['value' => $value], JSON_THROW_ON_ERROR));
191+
$fieldValue->setCurrentVisibility($visibility);
192+
$fieldValue->setUpdatedByUid('admin');
193+
$fieldValue->setUpdatedAt(new DateTime());
194+
195+
return $fieldValue;
196+
}
197+
198+
private function buildUser(string $displayName): IUser&MockObject {
199+
$user = $this->createMock(IUser::class);
200+
$user->method('getDisplayName')->willReturn($displayName);
201+
202+
return $user;
203+
}
204+
}

0 commit comments

Comments
 (0)