From c5259b49e2d2598ead6cdd87b94e6823ab037661 Mon Sep 17 00:00:00 2001 From: Hamza Date: Thu, 19 Feb 2026 15:47:20 +0100 Subject: [PATCH] test(unit): contacts integration Signed-off-by: Hamza --- .../ContactIntegrationControllerTest.php | 268 ++++++++++++++++++ .../Unit/Service/ContactsIntegrationTest.php | 140 +++++++++ 2 files changed, 408 insertions(+) create mode 100644 tests/Unit/Controller/ContactIntegrationControllerTest.php diff --git a/tests/Unit/Controller/ContactIntegrationControllerTest.php b/tests/Unit/Controller/ContactIntegrationControllerTest.php new file mode 100644 index 0000000000..948298dbd9 --- /dev/null +++ b/tests/Unit/Controller/ContactIntegrationControllerTest.php @@ -0,0 +1,268 @@ +createMock(IRequest::class); + $this->service = $this->createMock(ContactIntegrationService::class); + $this->cache = $this->createMock(ICache::class); + + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory->expects($this->once()) + ->method('createLocal') + ->with('mail.contacts') + ->willReturn($this->cache); + + $this->controller = new ContactIntegrationController( + 'mail', + $request, + $this->service, + $cacheFactory, + $this->userId, + ); + } + + public function testMatch(): void { + $mail = 'john@doe.com'; + $expected = [ + ['id' => '1', 'label' => 'John Doe', 'email' => 'john@doe.com'], + ]; + + $this->service->expects($this->once()) + ->method('findMatches') + ->with($mail) + ->willReturn($expected); + + $response = $this->controller->match($mail); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($expected, $response->getData()); + } + + public function testAddMail(): void { + $uid = 'contact-uid'; + $mail = 'new@example.com'; + $expected = ['status' => 'success']; + + $this->service->expects($this->once()) + ->method('addEMailToContact') + ->with($uid, $mail) + ->willReturn($expected); + + $response = $this->controller->addMail($uid, $mail); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($expected, $response->getData()); + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + } + + public function testAddMailNotFound(): void { + $uid = 'nonexistent'; + $mail = 'test@example.com'; + + $this->service->expects($this->once()) + ->method('addEMailToContact') + ->with($uid, $mail) + ->willReturn(null); + + $response = $this->controller->addMail($uid, $mail); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); + } + + public function testNewContact(): void { + $name = 'Jane Doe'; + $mail = 'jane@doe.com'; + $expected = ['status' => 'success']; + + $this->service->expects($this->once()) + ->method('newContact') + ->with($name, $mail) + ->willReturn($expected); + + $response = $this->controller->newContact($name, $mail); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($expected, $response->getData()); + $this->assertEquals(Http::STATUS_OK, $response->getStatus()); + } + + public function testNewContactFailed(): void { + $name = 'Jane Doe'; + $mail = 'jane@doe.com'; + + $this->service->expects($this->once()) + ->method('newContact') + ->with($name, $mail) + ->willReturn(null); + + $response = $this->controller->newContact($name, $mail); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals(Http::STATUS_NOT_ACCEPTABLE, $response->getStatus()); + } + + public function testAutoCompleteReturnsCachedResult(): void { + $term = 'john'; + $cachedData = [ + ['id' => '1', 'label' => 'John Doe', 'email' => 'john@doe.com'], + ]; + + $this->cache->expects($this->once()) + ->method('get') + ->with("{$this->userId}:$term") + ->willReturn(json_encode($cachedData)); + + $this->service->expects($this->never()) + ->method('autoComplete'); + + $this->cache->expects($this->never()) + ->method('set'); + + $response = $this->controller->autoComplete($term); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($cachedData, $response->getData()); + } + + public function testAutoCompleteCacheMissCallsService(): void { + $term = 'jane'; + $serviceResult = [ + ['id' => '2', 'label' => 'Jane Doe', 'email' => 'jane@doe.com'], + ]; + + $this->cache->expects($this->once()) + ->method('get') + ->with("{$this->userId}:$term") + ->willReturn(null); + + $this->service->expects($this->once()) + ->method('autoComplete') + ->with($term) + ->willReturn($serviceResult); + + $this->cache->expects($this->once()) + ->method('set') + ->with( + "{$this->userId}:$term", + json_encode($serviceResult), + 24 * 3600, + ); + + $response = $this->controller->autoComplete($term); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($serviceResult, $response->getData()); + } + + public function testAutoCompleteCacheInvalidJsonFallsBackToService(): void { + $term = 'bob'; + $serviceResult = [ + ['id' => '3', 'label' => 'Bob Smith', 'email' => 'bob@smith.com'], + ]; + + // Cache returns a string that is not valid JSON (json_decode returns null) + $this->cache->expects($this->once()) + ->method('get') + ->with("{$this->userId}:$term") + ->willReturn('not valid json {{{'); + + $this->service->expects($this->once()) + ->method('autoComplete') + ->with($term) + ->willReturn($serviceResult); + + $this->cache->expects($this->once()) + ->method('set') + ->with( + "{$this->userId}:$term", + json_encode($serviceResult), + 24 * 3600, + ); + + $response = $this->controller->autoComplete($term); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($serviceResult, $response->getData()); + } + + public function testAutoCompleteEmptyResult(): void { + $term = 'nonexistent'; + + $this->cache->expects($this->once()) + ->method('get') + ->with("{$this->userId}:$term") + ->willReturn(null); + + $this->service->expects($this->once()) + ->method('autoComplete') + ->with($term) + ->willReturn([]); + + $this->cache->expects($this->once()) + ->method('set') + ->with( + "{$this->userId}:$term", + json_encode([]), + 24 * 3600, + ); + + $response = $this->controller->autoComplete($term); + + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals([], $response->getData()); + } + + public function testAutoCompleteCacheKeyIsUserSpecific(): void { + $term = 'test'; + + // Verify the cache key includes the user ID prefix + $this->cache->expects($this->once()) + ->method('get') + ->with('testuser:test') + ->willReturn(null); + + $this->service->expects($this->once()) + ->method('autoComplete') + ->with($term) + ->willReturn([]); + + $this->cache->expects($this->once()) + ->method('set') + ->with('testuser:test', $this->anything(), $this->anything()); + + $this->controller->autoComplete($term); + } +} diff --git a/tests/Unit/Service/ContactsIntegrationTest.php b/tests/Unit/Service/ContactsIntegrationTest.php index c9edeca720..7b22bf6c8d 100644 --- a/tests/Unit/Service/ContactsIntegrationTest.php +++ b/tests/Unit/Service/ContactsIntegrationTest.php @@ -405,6 +405,146 @@ public function getPhotoDataProvider() { ]; } + public function testGetContactsWithName(): void { + $name = 'John'; + $searchResult = [ + [ + 'UID' => 'jd', + 'FN' => 'John Doe', + 'EMAIL' => 'john@doe.com', + ], + [ + 'UID' => 'js', + 'FN' => 'John Smith', + 'EMAIL' => ['john@smith.com', 'jsmith@example.com'], + ], + ]; + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') + ->willReturn('yes'); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($name, ['FN'], [ + 'strict_search' => false, + 'limit' => 20, + ]) + ->willReturn($searchResult); + + $expected = [ + [ + 'id' => 'jd', + 'label' => 'John Doe', + 'email' => 'john@doe.com', + ], + [ + 'id' => 'js', + 'label' => 'John Smith', + 'email' => ['john@smith.com', 'jsmith@example.com'], + ], + ]; + + $actual = $this->contactsIntegration->getContactsWithName($name); + + $this->assertEquals($expected, $actual); + } + + public function testGetContactsWithNameFiltersSystemUsersWhenDisabled(): void { + $name = 'John'; + $searchResult = [ + [ + 'UID' => 'jd', + 'FN' => 'John Doe', + 'EMAIL' => 'john@doe.com', + 'isLocalSystemBook' => true, + ], + [ + 'UID' => 'js', + 'FN' => 'John Smith', + 'EMAIL' => 'john@smith.com', + ], + ]; + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') + ->willReturn('no'); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($name, ['FN'], [ + 'strict_search' => false, + 'limit' => 20, + ]) + ->willReturn($searchResult); + + $expected = [ + [ + 'id' => 'js', + 'label' => 'John Smith', + 'email' => 'john@smith.com', + ], + ]; + + $actual = $this->contactsIntegration->getContactsWithName($name); + + $this->assertEquals($expected, $actual); + } + + public function testGetContactsWithNameNoResults(): void { + $name = 'Nonexistent'; + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') + ->willReturn('yes'); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($name, ['FN'], [ + 'strict_search' => false, + 'limit' => 20, + ]) + ->willReturn([]); + + $actual = $this->contactsIntegration->getContactsWithName($name); + + $this->assertEquals([], $actual); + } + + public function testGetContactsWithNameNoEmail(): void { + $name = 'John'; + $searchResult = [ + [ + 'UID' => 'jd', + 'FN' => 'John Doe', + ], + ]; + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') + ->willReturn('yes'); + $this->contactsManager->expects($this->once()) + ->method('search') + ->with($name, ['FN'], [ + 'strict_search' => false, + 'limit' => 20, + ]) + ->willReturn($searchResult); + + $expected = [ + [ + 'id' => 'jd', + 'label' => 'John Doe', + 'email' => null, + ], + ]; + + $actual = $this->contactsIntegration->getContactsWithName($name); + + $this->assertEquals($expected, $actual); + } + /** * @dataProvider getPhotoDataProvider */