Skip to content

Commit dcd03d0

Browse files
authored
Add end-to-end Search Index Management Helper Methods Tests (#1184)
JAVA-5037
1 parent 82bc1b8 commit dcd03d0

File tree

5 files changed

+366
-0
lines changed

5 files changed

+366
-0
lines changed

.evergreen/.evg.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,16 @@ tasks:
14511451
commands:
14521452
- func: "run atlas test"
14531453

1454+
- name: "test-atlas-search-index-helpers"
1455+
commands:
1456+
- command: subprocess.exec
1457+
params:
1458+
working_dir: src
1459+
binary: bash
1460+
add_expansions_to_env: true
1461+
args:
1462+
- .evergreen/run-atlas-search-index-management-tests.sh
1463+
14541464
- name: publish-snapshot
14551465
depends_on:
14561466
- variant: "static-checks"
@@ -1810,6 +1820,33 @@ axes:
18101820
AWS_CREDENTIAL_PROVIDER: "builtIn"
18111821

18121822
task_groups:
1823+
- name: test_atlas_task_group_search_indexes
1824+
setup_group:
1825+
- func: fetch source
1826+
- func: prepare resources
1827+
- func: make files executable
1828+
- command: subprocess.exec
1829+
params:
1830+
working_dir: src
1831+
binary: bash
1832+
add_expansions_to_env: true
1833+
args:
1834+
- ${DRIVERS_TOOLS}/.evergreen/atlas/setup-atlas-cluster.sh
1835+
- command: expansions.update
1836+
params:
1837+
file: src/atlas-expansion.yml
1838+
teardown_group:
1839+
- command: subprocess.exec
1840+
params:
1841+
working_dir: src
1842+
binary: bash
1843+
add_expansions_to_env: true
1844+
args:
1845+
- ${DRIVERS_TOOLS}/.evergreen/atlas/teardown-atlas-cluster.sh
1846+
setup_group_can_fail_task: true
1847+
setup_group_timeout_secs: 1800
1848+
tasks:
1849+
- test-atlas-search-index-helpers
18131850
- name: testgcpkms_task_group
18141851
setup_group_can_fail_task: true
18151852
setup_group_timeout_secs: 1800 # 30 minutes
@@ -2029,6 +2066,12 @@ buildvariants:
20292066
tasks:
20302067
- name: "plain-auth-test"
20312068

2069+
- name: rhel80-test-search-indexes
2070+
display_name: Atlas Search Index Management Tests
2071+
run_on: rhel80-small
2072+
tasks:
2073+
- name: "test_atlas_task_group_search_indexes"
2074+
20322075
- matrix_name: "aws-auth-test"
20332076
matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17"], version: ["4.4", "5.0", "6.0", "7.0", "latest"], os: "ubuntu",
20342077
aws-credential-provider: "*" }
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
5+
# Supported/used environment variables:
6+
# MONGODB_URI Set the connection to an Atlas cluster
7+
8+
############################################
9+
# Main Program #
10+
############################################
11+
RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE[0]:-$0}")"
12+
source "${RELATIVE_DIR_PATH}/javaConfig.bash"
13+
14+
echo "Running Atlas Search tests"
15+
./gradlew -version
16+
./gradlew --stacktrace --info \
17+
-Dorg.mongodb.test.atlas.search.index.helpers=true \
18+
-Dorg.mongodb.test.uri=${MONGODB_URI} \
19+
driver-sync:test --tests AtlasSearchIndexManagementProseTest \
20+
driver-reactive-streams:test --tests AtlasSearchIndexManagementProseTest \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.reactivestreams.client;
18+
19+
20+
import com.mongodb.MongoClientSettings;
21+
import com.mongodb.client.AbstractAtlasSearchIndexManagementProseTest;
22+
import com.mongodb.client.MongoClient;
23+
import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient;
24+
25+
/**
26+
* See <a href="https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.rst#search-index-management-helpers">Search Index Management Tests</a>
27+
*/
28+
public class AtlasSearchIndexManagementProseTest extends AbstractAtlasSearchIndexManagementProseTest {
29+
protected MongoClient createMongoClient(final MongoClientSettings settings) {
30+
return new SyncMongoClient(MongoClients.create(settings));
31+
}
32+
}
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.client;
18+
19+
import com.mongodb.MongoClientSettings;
20+
import com.mongodb.client.model.SearchIndexModel;
21+
import org.bson.Document;
22+
import org.bson.conversions.Bson;
23+
import org.junit.jupiter.api.AfterEach;
24+
import org.junit.jupiter.api.Assertions;
25+
import org.junit.jupiter.api.Assumptions;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.DisplayName;
28+
import org.junit.jupiter.api.Test;
29+
30+
import java.util.Arrays;
31+
import java.util.List;
32+
import java.util.Map;
33+
import java.util.UUID;
34+
import java.util.concurrent.TimeUnit;
35+
import java.util.function.Function;
36+
import java.util.function.Predicate;
37+
import java.util.stream.Collectors;
38+
import java.util.stream.StreamSupport;
39+
40+
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
41+
import static com.mongodb.client.Fixture.getMongoClientSettings;
42+
import static org.hamcrest.MatcherAssert.assertThat;
43+
import static org.hamcrest.Matchers.contains;
44+
45+
/**
46+
* See <a href="https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.rst#search-index-management-helpers">Search Index Management Tests</a>
47+
*/
48+
public abstract class AbstractAtlasSearchIndexManagementProseTest {
49+
/**
50+
* The maximum number of attempts for waiting for changes or completion.
51+
* If this many attempts are made without success, the test will be marked as failed.
52+
*/
53+
private static final int MAX_WAIT_ATTEMPTS = 70;
54+
55+
/**
56+
* The duration in seconds to wait between each attempt when waiting for changes or completion.
57+
*/
58+
private static final int WAIT_INTERVAL_SECONDS = 5;
59+
60+
private static final String TEST_SEARCH_INDEX_NAME_1 = "test-search-index";
61+
private static final String TEST_SEARCH_INDEX_NAME_2 = "test-search-index-2";
62+
private static final Document NOT_DYNAMIC_MAPPING_DEFINITION = Document.parse(
63+
"{"
64+
+ " mappings: { dynamic: false }"
65+
+ "}");
66+
private static final Document DYNAMIC_MAPPING_DEFINITION = Document.parse(
67+
"{"
68+
+ " mappings: { dynamic: true }"
69+
+ "}");
70+
private MongoClient client = createMongoClient(getMongoClientSettings());
71+
private MongoDatabase db;
72+
private MongoCollection<Document> collection;
73+
74+
protected abstract MongoClient createMongoClient(MongoClientSettings settings);
75+
76+
protected AbstractAtlasSearchIndexManagementProseTest() {
77+
Assumptions.assumeTrue(serverVersionAtLeast(6, 0));
78+
Assumptions.assumeTrue(hasAtlasSearchIndexHelperEnabled(), "Atlas Search Index tests are disabled"); //TODO enable by flag
79+
}
80+
81+
private static boolean hasAtlasSearchIndexHelperEnabled() {
82+
return Boolean.parseBoolean(System.getProperty("org.mongodb.test.atlas.search.index.helpers"));
83+
}
84+
85+
@BeforeEach
86+
public void setUp() {
87+
client = createMongoClient(getMongoClientSettings());
88+
db = client.getDatabase("test");
89+
90+
String collectionName = UUID.randomUUID().toString();
91+
db.createCollection(collectionName);
92+
collection = db.getCollection(collectionName);
93+
}
94+
95+
@AfterEach
96+
void cleanUp() {
97+
try {
98+
collection.drop();
99+
db.drop();
100+
} finally {
101+
client.close();
102+
}
103+
}
104+
105+
@Test
106+
@DisplayName("Case 1: Driver can successfully create and list search indexes")
107+
public void shouldCreateAndListSearchIndexes() throws InterruptedException {
108+
//given
109+
SearchIndexModel searchIndexModel = new SearchIndexModel(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION);
110+
111+
//when
112+
String createdSearchIndexName = collection.createSearchIndex(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION);
113+
114+
//then
115+
Assertions.assertEquals(TEST_SEARCH_INDEX_NAME_1, createdSearchIndexName);
116+
assertIndexesChanges(isQueryable(), searchIndexModel);
117+
}
118+
119+
@Test
120+
@DisplayName("Case 2: Driver can successfully create multiple indexes in batch")
121+
public void shouldCreateMultipleIndexesInBatch() throws InterruptedException {
122+
//given
123+
SearchIndexModel searchIndexModel1 = new SearchIndexModel(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION);
124+
SearchIndexModel searchIndexModel2 = new SearchIndexModel(TEST_SEARCH_INDEX_NAME_2, NOT_DYNAMIC_MAPPING_DEFINITION);
125+
126+
//when
127+
List<String> searchIndexes = collection.createSearchIndexes(Arrays.asList(searchIndexModel1, searchIndexModel2));
128+
129+
//then
130+
assertThat(searchIndexes, contains(TEST_SEARCH_INDEX_NAME_1, TEST_SEARCH_INDEX_NAME_2));
131+
assertIndexesChanges(isQueryable(), searchIndexModel1, searchIndexModel2);
132+
}
133+
134+
@Test
135+
@DisplayName("Case 3: Driver can successfully drop search indexes")
136+
public void shouldDropSearchIndex() throws InterruptedException {
137+
//given
138+
String createdSearchIndexName = collection.createSearchIndex(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION);
139+
Assertions.assertEquals(TEST_SEARCH_INDEX_NAME_1, createdSearchIndexName);
140+
awaitIndexChanges(isQueryable(), new SearchIndexModel(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION));
141+
142+
//when
143+
collection.dropSearchIndex(TEST_SEARCH_INDEX_NAME_1);
144+
145+
//then
146+
assertIndexDeleted();
147+
}
148+
149+
@Test
150+
@DisplayName("Case 4: Driver can update a search index")
151+
public void shouldUpdateSearchIndex() throws InterruptedException {
152+
//given
153+
String createdSearchIndexName = collection.createSearchIndex(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION);
154+
Assertions.assertEquals(TEST_SEARCH_INDEX_NAME_1, createdSearchIndexName);
155+
awaitIndexChanges(isQueryable(), new SearchIndexModel(TEST_SEARCH_INDEX_NAME_1, NOT_DYNAMIC_MAPPING_DEFINITION));
156+
157+
//when
158+
collection.updateSearchIndex(TEST_SEARCH_INDEX_NAME_1, DYNAMIC_MAPPING_DEFINITION);
159+
160+
//then
161+
assertIndexesChanges(isReady().and(isQueryable()), new SearchIndexModel(TEST_SEARCH_INDEX_NAME_1, DYNAMIC_MAPPING_DEFINITION));
162+
}
163+
164+
@Test
165+
@DisplayName("Case 5: dropSearchIndex suppresses namespace not found errors")
166+
public void shouldSuppressNamespaceErrorWhenDroppingIndexWithoutCollection() {
167+
//given
168+
collection.drop();
169+
170+
//when
171+
collection.dropSearchIndex("not existent index");
172+
}
173+
174+
175+
private void assertIndexDeleted() throws InterruptedException {
176+
int attempts = MAX_WAIT_ATTEMPTS;
177+
while (collection.listSearchIndexes().first() != null && checkAttempt(attempts--)) {
178+
await();
179+
}
180+
}
181+
182+
private void assertIndexesChanges(final Predicate<Document> indexStatus, final SearchIndexModel... searchIndexModels)
183+
throws InterruptedException {
184+
185+
Map<String, Document> createdIndexes = awaitIndexChanges(indexStatus, searchIndexModels);
186+
Assertions.assertEquals(searchIndexModels.length, createdIndexes.size());
187+
188+
for (SearchIndexModel searchIndexModel : searchIndexModels) {
189+
Bson mappings = searchIndexModel.getDefinition();
190+
String searchIndexName = searchIndexModel.getName();
191+
192+
Document createdIndex = createdIndexes.get(searchIndexName);
193+
Assertions.assertNotNull(createdIndex);
194+
Assertions.assertEquals(createdIndex.get("latestDefinition"), mappings);
195+
}
196+
}
197+
198+
199+
private Map<String, Document> awaitIndexChanges(final Predicate<Document> indexStatus, final SearchIndexModel... searchIndexModels)
200+
throws InterruptedException {
201+
int attempts = MAX_WAIT_ATTEMPTS;
202+
while (checkAttempt(attempts--)) {
203+
Map<String, Document> existingIndexes = StreamSupport.stream(collection.listSearchIndexes().spliterator(), false)
204+
.filter(indexStatus)
205+
.collect(Collectors.toMap(document -> document.getString("name"), Function.identity()));
206+
207+
if (checkNames(existingIndexes, searchIndexModels)) {
208+
return existingIndexes;
209+
}
210+
await();
211+
}
212+
return Assertions.fail();
213+
}
214+
215+
private Predicate<Document> isQueryable() {
216+
return document -> document.getBoolean("queryable");
217+
}
218+
219+
private Predicate<Document> isReady() {
220+
return document -> "READY".equals(document.getString("status"));
221+
}
222+
223+
224+
private boolean checkAttempt(final int attempt) {
225+
Assertions.assertFalse(attempt <= 0, "Exceeded maximum attempts waiting for Search Index changes in Atlas cluster");
226+
return true;
227+
}
228+
229+
private static void await() throws InterruptedException {
230+
TimeUnit.SECONDS.sleep(WAIT_INTERVAL_SECONDS);
231+
}
232+
233+
private static boolean checkNames(final Map<String, Document> existingIndexes, final SearchIndexModel... searchIndexModels) {
234+
for (SearchIndexModel searchIndexModel : searchIndexModels) {
235+
String searchIndexName = searchIndexModel.getName();
236+
if (!existingIndexes.containsKey(searchIndexName)) {
237+
return false;
238+
}
239+
240+
}
241+
return true;
242+
}
243+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.client;
18+
19+
import com.mongodb.MongoClientSettings;
20+
21+
/**
22+
* See <a href="https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.rst#search-index-management-helpers">Search Index Management Tests</a>
23+
*/
24+
public class AtlasSearchIndexManagementProseTest extends AbstractAtlasSearchIndexManagementProseTest {
25+
protected MongoClient createMongoClient(final MongoClientSettings settings) {
26+
return MongoClients.create(settings);
27+
}
28+
}

0 commit comments

Comments
 (0)