diff --git a/lib/SearchElasticConfigService.php b/lib/SearchElasticConfigService.php index ef42fb00..2934d6ef 100644 --- a/lib/SearchElasticConfigService.php +++ b/lib/SearchElasticConfigService.php @@ -357,7 +357,7 @@ public function setConfiguredWriteConnectors($connectorNameList) { } public function setConfiguredSearchConnector($connectorName) { - return $this->setValue(self::CONNECTORS_SEARCH, $connectorName); + $this->setValue(self::CONNECTORS_SEARCH, $connectorName); } /** diff --git a/tests/unit/Connectors/ConnectorLegacyTest.php b/tests/unit/Connectors/ConnectorLegacyTest.php index ce295930..ac7743d6 100644 --- a/tests/unit/Connectors/ConnectorLegacyTest.php +++ b/tests/unit/Connectors/ConnectorLegacyTest.php @@ -1,8 +1,6 @@ - * - * @copyright Copyright (c) 2019, ownCloud GmbH + * @copyright Copyright (c) 2023, ownCloud GmbH * @license GPL-2.0 * * This program is free software; you can redistribute it and/or modify it diff --git a/tests/unit/Connectors/ConnectorRelevanceV2Test.php b/tests/unit/Connectors/ConnectorRelevanceV2Test.php new file mode 100644 index 00000000..a6b71c71 --- /dev/null +++ b/tests/unit/Connectors/ConnectorRelevanceV2Test.php @@ -0,0 +1,858 @@ +client = $this->createMock(Client::class); + $this->factory = $this->createMock(ElasticaFactory::class); + $this->esConfig = $this->createMock(SearchElasticConfigService::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->logger = $this->createMock(ILogger::class); + + $this->connectorRelevanceV2 = new ConnectorRelevanceV2( + $this->client, + $this->factory, + $this->esConfig, + $this->groupManager, + $this->userManager, + $this->logger + ); + } + + public function testGetConnectorName() { + $this->assertSame('RelevanceV2', $this->connectorRelevanceV2->getConnectorName()); + } + + public function testIsSetupMissingIndex() { + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('exists') + ->willReturn(false); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + // no request to check the ingest pipeline is needed + $this->client->expects($this->never()) + ->method('request'); + + $this->assertFalse($this->connectorRelevanceV2->isSetup()); + } + + public function testIsSetupWrongPipeline() { + $this->esConfig->method('getRecommendedPrefixFor') + ->will($this->returnValueMap([ + ['index', 'oc-instanceid'], + ['processor', 'oc-processor-instanceid'], + ])); + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('exists') + ->willReturn(true); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $response = $this->createMock(Response::class); + $response->method('getStatus')->willReturn(404); + + $this->client->expects($this->once()) + ->method('request') + ->with('_ingest/pipeline/oc-processor-instanceid-relv2', Request::GET) + ->willReturn($response); + + $this->assertFalse($this->connectorRelevanceV2->isSetup()); + } + + public function testIsSetup() { + $this->esConfig->method('getRecommendedPrefixFor') + ->will($this->returnValueMap([ + ['index', 'oc-instanceid'], + ['processor', 'oc-processor-instanceid'], + ])); + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('exists') + ->willReturn(true); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $response = $this->createMock(Response::class); + $response->method('getStatus')->willReturn(200); + + $this->client->expects($this->once()) + ->method('request') + ->with('_ingest/pipeline/oc-processor-instanceid-relv2', Request::GET) + ->willReturn($response); + + $this->assertTrue($this->connectorRelevanceV2->isSetup()); + } + + public function testPrepareIndex() { + $this->esConfig->method('getRecommendedPrefixFor') + ->will($this->returnValueMap([ + ['index', 'oc-instanceid'], + ['processor', 'oc-processor-instanceid'], + ])); + + $expectedIndexConf = [ + 'number_of_shards' => 1, + 'number_of_replicas' => 0, + 'analysis' => [ + 'analyzer' => [ + 'filename_analyzer' => [ + 'type' => 'custom', + 'tokenizer' => 'filename_tokenizer', + 'filter' => ['lowercase'], + ], + 'filename_analyzer_ngram' => [ + 'type' => 'custom', + 'tokenizer' => 'filename_tokenizer', + 'filter' => ['lowercase', 'filename_ngram'], + ], + 'filename_analyzer_edgegram' => [ + 'type' => 'custom', + 'tokenizer' => 'filename_tokenizer', + 'filter' => ['lowercase', 'filename_edge_ngram'], + ], + ], + 'tokenizer' => [ + 'filename_tokenizer' => [ + 'type' => 'char_group', + 'tokenize_on_chars' => ['whitespace', 'punctuation'], + ], + ], + 'filter' => [ + 'filename_ngram' => [ + 'type' => 'ngram', + 'min_gram' => 2, + 'max_gram' => 3, + 'preserve_original' => true, + ], + 'filename_edge_ngram' => [ + 'type' => 'edge_ngram', + 'min_gram' => 2, + 'max_gram' => 3, + 'preserve_original' => true, + ], + ], + ], + ]; + + $expectedMappingConf = [ + 'mtime' => [ + 'type' => 'date', + 'format' => 'epoch_second||date||date_time_no_millis', + ], + 'size' => [ + 'properties' => [ + 'b' => ['type' => 'long'], + 'mb' => ['type' => 'double'], + ], + ], + 'name' => [ + 'type' => 'text', + 'analyzer' => 'filename_analyzer', + 'fields' => [ + 'ngram' => [ + 'type' => 'text', + 'analyzer' => 'filename_analyzer_ngram', + ], + 'edge_ngram' => [ + 'type' => 'text', + 'analyzer' => 'filename_analyzer_edgegram', + ], + ], + ], + 'ext' => [ + 'type' => 'text', + 'analyzer' => 'filename_analyzer', + ], + 'type' => ['type' => 'keyword'], + 'mime' => [ + 'type' => 'text', + 'fields' => [ + 'key' => ['type' => 'keyword'], + ], + ], + 'users' => ['type' => 'keyword'], + 'groups' => ['type' => 'keyword'], + ]; + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('create') + ->with(['settings' => $expectedIndexConf], true); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $mappingMock = $this->createMock(Mapping::class); + $mappingMock->expects($this->once()) + ->method('setProperties') + ->with($expectedMappingConf); + $mappingMock->expects($this->once()) + ->method('send') + ->with($indexMock); + $this->factory->expects($this->once()) + ->method('getNewMapping') + ->willReturn($mappingMock); + + $expectedPayload = [ + 'description' => 'Pipeline to process entries for ownCloud search with connector RelevanceV2', + 'processors' => [ + [ + 'attachment' => [ + 'field' => 'data', + 'target_field' => 'file', + 'indexed_chars' => '-1', + ] + ], + [ + 'remove' => [ + 'field' => 'data', + ] + ], + ], + ]; + + $this->client->expects($this->once()) + ->method('request') + ->with('_ingest/pipeline/oc-processor-instanceid-relv2', Request::PUT, $expectedPayload); + + $this->assertNull($this->connectorRelevanceV2->prepareIndex()); + } + + public function testFetchResults() { + $this->esConfig->expects($this->once()) + ->method('getGroupNoContentArray') + ->willReturn([]); + $this->esConfig->expects($this->once()) + ->method('shouldContentBeIncluded') + ->willReturn(true); + + $userObj = $this->createMock(IUser::class); + $userObj->method('getUID')->willReturn('mockedUser'); + + $this->userManager->expects($this->once()) + ->method('get') + ->with('mockedUser', true) + ->willReturn($userObj); + + $group1 = $this->createMock(IGroup::class); + $group1->method('getGID')->willReturn('G1'); + $group2 = $this->createMock(IGroup::class); + $group2->method('getGID')->willReturn('G2'); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($userObj) + ->willReturn([$group1, $group2]); + + $expectedQuery = [ + 'query' => [ + 'function_score' => [ + 'functions' => [ + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1y', + ], + ], + ], + 'weight' => 0.25, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1M', + ], + ], + ], + 'weight' => 0.5, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1w', + ], + ], + ], + 'weight' => 1, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1d', + ], + ], + ], + 'weight' => 2, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'gte' => 'now-1d', + ], + ], + ], + 'weight' => 4, + ], + ], + 'score_mode' => 'first', + 'query' => [ + 'bool' => [ + 'filter' => [ + [ + 'bool' => [ + 'should' => [ + [ + 'terms' => [ + 'users' => ['mockedUser'], + ] + ], + [ + 'terms' => [ + 'groups' => ['G1', 'G2'], + ], + ], + ], + ], + ], + ], + 'should' => [ + [ + 'query_string' => [ + 'query' => 'test query', + 'fields' => ['name', 'name.edge_ngram^0.5', 'name.ngram^0.25', 'file.content'], + 'auto_generate_synonyms_phrase_query' => false, + 'type' => 'most_fields', + ], + ], + ], + 'minimum_should_match' => 1, + ], + ], + ], + ], + 'highlight' => [ + 'fields' => ['file.content' => new \stdClass] + ], + 'fields' => [ + [ + 'field' => 'mtime', + 'format' => 'epoch_second', + ] + ], + 'size' => 30, + 'from' => 0, + ]; + + $query = $this->createMock(Query::class); + $query->expects($this->once()) + ->method('setRawQuery') + ->with($expectedQuery); + + $this->factory->expects($this->once()) + ->method('getNewQuery') + ->willReturn($query); + + $indexMock = $this->createMock(Index::class); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $search = $this->createMock(Search::class); + $search->expects($this->once()) + ->method('addIndex') + ->with($indexMock); + + $this->factory->expects($this->once()) + ->method('getNewSearch') + ->with($this->client) + ->willReturn($search); + + // can't perform checks over the result set + $this->connectorRelevanceV2->fetchResults('mockedUser', 'test query', 30, 0); + } + + public function testFetchResultsWithoutContent() { + $this->esConfig->expects($this->once()) + ->method('getGroupNoContentArray') + ->willReturn([]); + $this->esConfig->expects($this->once()) + ->method('shouldContentBeIncluded') + ->willReturn(false); + + $userObj = $this->createMock(IUser::class); + $userObj->method('getUID')->willReturn('mockedUser'); + + $this->userManager->expects($this->once()) + ->method('get') + ->with('mockedUser', true) + ->willReturn($userObj); + + $group1 = $this->createMock(IGroup::class); + $group1->method('getGID')->willReturn('G1'); + $group2 = $this->createMock(IGroup::class); + $group2->method('getGID')->willReturn('G2'); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($userObj) + ->willReturn([$group1, $group2]); + + $expectedQuery = [ + 'query' => [ + 'function_score' => [ + 'functions' => [ + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1y', + ], + ], + ], + 'weight' => 0.25, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1M', + ], + ], + ], + 'weight' => 0.5, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1w', + ], + ], + ], + 'weight' => 1, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1d', + ], + ], + ], + 'weight' => 2, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'gte' => 'now-1d', + ], + ], + ], + 'weight' => 4, + ], + ], + 'score_mode' => 'first', + 'query' => [ + 'bool' => [ + 'filter' => [ + [ + 'bool' => [ + 'should' => [ + [ + 'terms' => [ + 'users' => ['mockedUser'], + ] + ], + [ + 'terms' => [ + 'groups' => ['G1', 'G2'], + ], + ], + ], + ], + ], + ], + 'should' => [ + [ + 'query_string' => [ + 'query' => 'test query', + 'fields' => ['name', 'name.edge_ngram^0.5', 'name.ngram^0.25'], + 'auto_generate_synonyms_phrase_query' => false, + 'type' => 'most_fields', + ], + ], + ], + 'minimum_should_match' => 1, + ], + ], + ], + ], + 'highlight' => [ + 'fields' => ['file.content' => new \stdClass] + ], + 'fields' => [ + [ + 'field' => 'mtime', + 'format' => 'epoch_second', + ] + ], + 'size' => 30, + 'from' => 0, + ]; + + $query = $this->createMock(Query::class); + $query->expects($this->once()) + ->method('setRawQuery') + ->with($expectedQuery); + + $this->factory->expects($this->once()) + ->method('getNewQuery') + ->willReturn($query); + + $indexMock = $this->createMock(Index::class); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $search = $this->createMock(Search::class); + $search->expects($this->once()) + ->method('addIndex') + ->with($indexMock); + + $this->factory->expects($this->once()) + ->method('getNewSearch') + ->with($this->client) + ->willReturn($search); + + // can't perform checks over the result set + $this->connectorRelevanceV2->fetchResults('mockedUser', 'test query', 30, 0); + } + + public function testFetchResultsGroupNoContent() { + $this->esConfig->expects($this->once()) + ->method('getGroupNoContentArray') + ->willReturn(['G1']); + $this->esConfig->expects($this->once()) + ->method('shouldContentBeIncluded') + ->willReturn(true); + + $userObj = $this->createMock(IUser::class); + $userObj->method('getUID')->willReturn('mockedUser'); + + $this->userManager->expects($this->once()) + ->method('get') + ->with('mockedUser', true) + ->willReturn($userObj); + + $group1 = $this->createMock(IGroup::class); + $group1->method('getGID')->willReturn('G1'); + $group2 = $this->createMock(IGroup::class); + $group2->method('getGID')->willReturn('G2'); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($userObj) + ->willReturn([$group1, $group2]); + + $expectedQuery = [ + 'query' => [ + 'function_score' => [ + 'functions' => [ + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1y', + ], + ], + ], + 'weight' => 0.25, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1M', + ], + ], + ], + 'weight' => 0.5, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1w', + ], + ], + ], + 'weight' => 1, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'lt' => 'now-1d', + ], + ], + ], + 'weight' => 2, + ], + [ + 'filter' => [ + 'range' => [ + 'mtime' => [ + 'gte' => 'now-1d', + ], + ], + ], + 'weight' => 4, + ], + ], + 'score_mode' => 'first', + 'query' => [ + 'bool' => [ + 'filter' => [ + [ + 'bool' => [ + 'should' => [ + [ + 'terms' => [ + 'users' => ['mockedUser'], + ] + ], + [ + 'terms' => [ + 'groups' => ['G1', 'G2'], + ], + ], + ], + ], + ], + ], + 'should' => [ + [ + 'query_string' => [ + 'query' => 'test query', + 'fields' => ['name', 'name.edge_ngram^0.5', 'name.ngram^0.25'], + 'auto_generate_synonyms_phrase_query' => false, + 'type' => 'most_fields', + ], + ], + ], + 'minimum_should_match' => 1, + ], + ], + ], + ], + 'highlight' => [ + 'fields' => ['file.content' => new \stdClass] + ], + 'fields' => [ + [ + 'field' => 'mtime', + 'format' => 'epoch_second', + ] + ], + 'size' => 30, + 'from' => 0, + ]; + + $query = $this->createMock(Query::class); + $query->expects($this->once()) + ->method('setRawQuery') + ->with($expectedQuery); + + $this->factory->expects($this->once()) + ->method('getNewQuery') + ->willReturn($query); + + $indexMock = $this->createMock(Index::class); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $search = $this->createMock(Search::class); + $search->expects($this->once()) + ->method('addIndex') + ->with($indexMock); + + $this->factory->expects($this->once()) + ->method('getNewSearch') + ->with($this->client) + ->willReturn($search); + + // can't perform checks over the result set + $this->connectorRelevanceV2->fetchResults('mockedUser', 'test query', 30, 0); + } + + public function testFindInResultId() { + $result = $this->createMock(Result::class); + $result->method('getId')->willReturn(987); + + $this->assertSame(987, $this->connectorRelevanceV2->findInResult($result, 'id')); + } + + public function testFindInResultHighlight() { + $result = $this->createMock(Result::class); + $result->method('getHighlights')->willReturn([ + 'file.content' => ['high light number 1', 'another high'] + ]); + + $this->assertSame(['high light number 1', 'another high'], $this->connectorRelevanceV2->findInResult($result, 'highlights')); + } + + public function testFindInResultMtime() { + $result = $this->createMock(Result::class); + $result->method('getData')->willReturn([ + 'mtime' => 123456, + 'size' => 9876, + 'name' => 'a random name', + ]); + + $this->assertSame(123456, $this->connectorRelevanceV2->findInResult($result, 'mtime')); + } + + public function testDeleteByFileId() { + $response = $this->createMock(Response::class); + $response->method('isOk')->willReturn(true); + $response->method('getStatus')->willReturn(200); + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('deleteById') + ->willReturn($response); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $this->assertTrue($this->connectorRelevanceV2->deleteByFileId(50)); + } + + public function testDeleteByFileIdMissing() { + $response = $this->createMock(Response::class); + $response->method('isOk')->willReturn(false); + $response->method('getStatus')->willReturn(404); + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('deleteById') + ->willReturn($response); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $this->assertTrue($this->connectorRelevanceV2->deleteByFileId(50)); + } + + public function testDeleteByFileIdFailed() { + $response = $this->createMock(Response::class); + $response->method('isOk')->willReturn(false); + $response->method('getStatus')->willReturn(500); + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('deleteById') + ->willReturn($response); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $this->assertFalse($this->connectorRelevanceV2->deleteByFileId(50)); + } + + public function testGetStats() { + $stats = $this->createMock(Stats::class); + $stats->method('getData')->willReturn(['nodeCount' => 50]); + + $indexMock = $this->createMock(Index::class); + $indexMock->expects($this->once()) + ->method('getStats') + ->willReturn($stats); + + $this->factory->expects($this->once()) + ->method('getNewIndex') + ->willReturn($indexMock); + + $this->assertEquals(['nodeCount' => 50], $this->connectorRelevanceV2->getStats()); + } +} diff --git a/tests/unit/Connectors/HubTest.php b/tests/unit/Connectors/HubTest.php new file mode 100644 index 00000000..b7af1d66 --- /dev/null +++ b/tests/unit/Connectors/HubTest.php @@ -0,0 +1,559 @@ +esConfig = $this->createMock(SearchElasticConfigService::class); + $this->logger = $this->createMock(ILogger::class); + + $this->hub = new Hub($this->esConfig, $this->logger); + } + + public function testGetRegisteredConnectorNames() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->assertEquals(['Con001', 'Con002'], $this->hub->getRegisteredConnectorNames()); + } + + public function testGetRegisteredConnector() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->assertSame($con1, $this->hub->getRegisteredConnector('Con001')); + $this->assertSame($con2, $this->hub->getRegisteredConnector('Con002')); + } + + public function testGetRegisteredConnectorMissing() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + + $this->hub->registerConnector($con1); + + $this->assertNull($this->hub->getRegisteredConnector('Con002')); + } + + public function testPrepareWriteIndexesIsAllSetup() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->never()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->never()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertTrue($this->hub->prepareWriteIndexes()); + } + + public function testPrepareWriteIndexesIsNoneSetup() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturnOnConsecutiveCalls(false, true); + $con1->expects($this->once()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturnOnConsecutiveCalls(false, true); + $con2->expects($this->once()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertTrue($this->hub->prepareWriteIndexes()); + } + + public function testPrepareWriteIndexesIsAllSetupMultipleCallsCached() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->expects($this->once()) + ->method('isSetup') + ->willReturn(true); + $con1->expects($this->never()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->expects($this->once()) + ->method('isSetup') + ->willReturn(true); + $con2->expects($this->never()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertTrue($this->hub->prepareWriteIndexes()); + $this->assertTrue($this->hub->prepareWriteIndexes()); + } + + public function testPrepareWriteIndexesIsAllSetupForce() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->expects($this->once()) + ->method('isSetup') + ->willReturn(true); + $con1->expects($this->once()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->expects($this->once()) + ->method('isSetup') + ->willReturn(true); + $con2->expects($this->once()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertTrue($this->hub->prepareWriteIndexes(true)); + } + + public function testPrepareWriteIndexesIsNoneSetupFail() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(false); // both calls to isSetup fails + $con1->expects($this->once()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(false); // both calls to isSetup fails + $con2->expects($this->once()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertFalse($this->hub->prepareWriteIndexes()); + } + + public function testPrepareSearchIndexIsSetup() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->never()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertTrue($this->hub->prepareSearchIndex()); + } + + public function testPrepareSearchIndexIsNotSetup() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturnOnConsecutiveCalls(false, true); + $con1->expects($this->once()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertTrue($this->hub->prepareSearchIndex()); + } + + public function testPrepareSearchIndexIsSetupMultipleCallsCached() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->expects($this->once()) + ->method('isSetup') + ->willReturn(true); + $con1->expects($this->never()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertTrue($this->hub->prepareSearchIndex()); + $this->assertTrue($this->hub->prepareSearchIndex()); + } + + public function testPrepareSearchIndexIsSetupForce() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->expects($this->once()) + ->method('isSetup') + ->willReturn(true); + $con1->expects($this->once()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertTrue($this->hub->prepareSearchIndex(true)); + } + + public function testPrepareSearchIndexIsSetupFail() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(false); // both calls to isSetup fails + $con1->expects($this->once()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertFalse($this->hub->prepareSearchIndex()); + } + + public function testhubIsSetup() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->never()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->never()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertTrue($this->hub->hubIsSetup()); + } + + public function testhubIsSetupFail() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->never()) + ->method('prepareIndex'); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(false); + $con2->expects($this->once()) + ->method('prepareIndex'); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertFalse($this->hub->hubIsSetup()); + } + + public function testHubIndexNode() { + $node = $this->createMock(Node::class); + + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->once()) + ->method('indexNode') + ->with('userId001', $node, true) + ->willReturn(true); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->once()) + ->method('indexNode') + ->with('userId001', $node, true) + ->willReturn(true); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertTrue($this->hub->hubIndexNode('userId001', $node, true)); + } + + public function testHubIndexNodeFail() { + $node = $this->createMock(Node::class); + + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->once()) + ->method('indexNode') + ->with('userId001', $node, true) + ->willReturn(true); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->once()) + ->method('indexNode') + ->with('userId001', $node, true) + ->willReturn(false); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertFalse($this->hub->hubIndexNode('userId001', $node, true)); + } + + public function testHubIndexNodeNotPrepared() { + $node = $this->createMock(Node::class); + + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->never()) + ->method('indexNode') + ->with('userId001', $node, true) + ->willReturn(true); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(false); + $con2->expects($this->never()) + ->method('indexNode') + ->with('userId001', $node, true) + ->willReturn(true); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertFalse($this->hub->hubIndexNode('userId001', $node, true)); + } + + public function testHubFetchResults() { + $resultSet = $this->createMock(ResultSet::class); + + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->once()) + ->method('fetchResults') + ->with('userId001', 'test query', 30, 0) + ->willReturn($resultSet); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertEquals(['resultSet' => $resultSet, 'connector' => $con1], $this->hub->hubFetchResults('userId001', 'test query', 30, 0)); + } + + public function testHubFetchResultsNotPrepared() { + $resultSet = $this->createMock(ResultSet::class); + + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(false); + $con1->expects($this->never()) + ->method('fetchResults') + ->with('userId001', 'test query', 30, 0) + ->willReturn($resultSet); + + $con2 = $this->createMock(IConnector::class); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con001'); + + $this->assertFalse($this->hub->hubFetchResults('userId001', 'test query', 30, 0)); + } + + public function testHubDeleteByFileId() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->once()) + ->method('deleteByFileId') + ->with(123) + ->willReturn(true); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->once()) + ->method('deleteByFileId') + ->with(123) + ->willReturn(true); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertTrue($this->hub->hubDeleteByFileId(123)); + } + + public function testHubDeleteByFileIdFailed() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->once()) + ->method('deleteByFileId') + ->with(123) + ->willReturn(true); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->once()) + ->method('deleteByFileId') + ->with(123) + ->willReturn(false); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertFalse($this->hub->hubDeleteByFileId(123)); + } + + public function testHubDeleteByFileIdNotPrepared() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->never()) + ->method('deleteByFileId') + ->with(123) + ->willReturn(false); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(false); + $con2->expects($this->never()) + ->method('deleteByFileId') + ->with(123) + ->willReturn(true); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + + $this->assertFalse($this->hub->hubDeleteByFileId(123)); + } + + public function testHubGetStats() { + $con1 = $this->createMock(IConnector::class); + $con1->method('getConnectorName')->willReturn('Con001'); + $con1->method('isSetup')->willReturn(true); + $con1->expects($this->once()) + ->method('getStats') + ->willReturn(['key001' => 'value001', 'random' => 'not so random value']); + + $con2 = $this->createMock(IConnector::class); + $con2->method('getConnectorName')->willReturn('Con002'); + $con2->method('isSetup')->willReturn(true); + $con2->expects($this->once()) + ->method('getStats') + ->willReturn(['key001' => 'awesome', 'random' => 'maybe random value']); + + $this->hub->registerConnector($con1); + $this->hub->registerConnector($con2); + + $this->esConfig->method('getConfiguredWriteConnectors')->willReturn(['Con001', 'Con002']); + $this->esConfig->method('getConfiguredSearchConnector')->willReturn('Con002'); + + $expectedResult = [ + 'Con002' => ['key001' => 'awesome', 'random' => 'maybe random value'], + 'Con001' => ['key001' => 'value001', 'random' => 'not so random value'], + ]; + $this->assertEquals($expectedResult, $this->hub->hubGetStats()); + } +} diff --git a/tests/unit/controller/AdminSettingsControllerTest.php b/tests/unit/controller/AdminSettingsControllerTest.php index 1dd9aa98..9f1fc71c 100644 --- a/tests/unit/controller/AdminSettingsControllerTest.php +++ b/tests/unit/controller/AdminSettingsControllerTest.php @@ -157,4 +157,38 @@ public function testSaveServersWithErrors($urls, $authType, $authParams, $expect $expectedResponse = new JSONResponse(['message' => $expectedError], Http::STATUS_EXPECTATION_FAILED); $this->assertEquals($expectedResponse, $this->controller->saveServers($urls, $authType, $authParams)); } + + public function testSaveConnectors() { + $this->configService->expects($this->once()) + ->method('setConfiguredWriteConnectors') + ->with(['Con001', 'Con002']); + $this->configService->expects($this->once()) + ->method('setConfiguredSearchConnector') + ->with('Con001'); + + $expectedResponse = new JSONResponse([ + 'status' => 'success', + 'data' => [ + 'message' => 'Saved', + ] + ]); + + $this->assertEquals($expectedResponse, $this->controller->saveConnectors('Con001', ['Con001', 'Con002'])); + } + + public function testSaveConnectorsWrongData() { + $this->configService->expects($this->never()) + ->method('setConfiguredWriteConnectors'); + $this->configService->expects($this->never()) + ->method('setConfiguredSearchConnector'); + + $expectedResponse = new JSONResponse([ + 'status' => 'error', + 'data' => [ + 'message' => 'The search connector must be present in the write connectors', + ] + ], Http::STATUS_BAD_REQUEST); + + $this->assertEquals($expectedResponse, $this->controller->saveConnectors('Con003', ['Con001', 'Con002'])); + } }