|
| 1 | +# Index Management Tests |
| 2 | + |
| 3 | +______________________________________________________________________ |
| 4 | + |
| 5 | +## Test Plan |
| 6 | + |
| 7 | +These prose tests are ported from the legacy enumerate-indexes spec. |
| 8 | + |
| 9 | +### Configurations |
| 10 | + |
| 11 | +- standalone node |
| 12 | +- replica set primary node |
| 13 | +- replica set secondary node |
| 14 | +- mongos node |
| 15 | + |
| 16 | +### Preparation |
| 17 | + |
| 18 | +For each of the configurations: |
| 19 | + |
| 20 | +- Create a (new) database |
| 21 | +- Create a collection |
| 22 | +- Create a single column index, a compound index, and a unique index |
| 23 | +- Insert at least one document containing all the fields that the above indicated indexes act on |
| 24 | + |
| 25 | +### Tests |
| 26 | + |
| 27 | +- Run the driver's method that returns a list of index names, and: |
| 28 | + - verify that *all* index names are represented in the result |
| 29 | + - verify that there are no duplicate index names |
| 30 | + - verify there are no returned indexes that do not exist |
| 31 | +- Run the driver's method that returns a list of index information records, and: |
| 32 | + - verify all the indexes are represented in the result |
| 33 | + - verify the "unique" flags show up for the unique index |
| 34 | + - verify there are no duplicates in the returned list |
| 35 | + - if the result consists of statically defined index models that include an `ns` field, verify that its value is |
| 36 | + accurate |
| 37 | + |
| 38 | +### Search Index Management Helpers |
| 39 | + |
| 40 | +These tests are intended to smoke test the search management helpers end-to-end against a live Atlas cluster. |
| 41 | + |
| 42 | +The search index management commands are asynchronous and mongod/mongos returns before the changes to a clusters' search |
| 43 | +indexes have completed. When these prose tests specify "waiting for the changes", drivers should repeatedly poll the |
| 44 | +cluster with `listSearchIndexes` until the changes are visible. Each test specifies the condition that is considered |
| 45 | +"ready". For example, when creating a new search index, waiting until the inserted index has a status `queryable: true` |
| 46 | +indicates that the index was successfully created. |
| 47 | + |
| 48 | +The commands tested in these prose tests take a while to successfully complete. Drivers should raise the timeout for |
| 49 | +each test to avoid timeout errors if the test timeout is too low. 5 minutes is a sufficiently large timeout that any |
| 50 | +timeout that occurs indicates a real failure, but this value is not required and can be tweaked per-driver. |
| 51 | + |
| 52 | +There is a server-side limitation that prevents multiple search indexes from being created with the same name, |
| 53 | +definition and collection name. This limitation does not take into account collection uuid. Because these commands are |
| 54 | +asynchronous, any cleanup code that may run after a test (cleaning a database or dropping search indexes) may not have |
| 55 | +completed by the next iteration of the test (or the next test run, if running locally). To address this issue, each test |
| 56 | +uses a randomly generated collection name. Drivers may generate this collection name however they like, but a suggested |
| 57 | +implementation is a hex representation of an ObjectId (`new ObjectId().toHexString()` in Node). |
| 58 | + |
| 59 | +#### Setup |
| 60 | + |
| 61 | +These tests must run against an Atlas cluster with a 7.0+ server. |
| 62 | +[Scripts are available](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/atlas) in |
| 63 | +drivers-evergreen-tools which can setup and teardown Atlas clusters. To ensure that the Atlas cluster is cleaned up |
| 64 | +after each CI run, drivers should configure evergreen to run these tests as a part of a task group. Be sure that the |
| 65 | +cluster gets torn down! |
| 66 | + |
| 67 | +When working locally on these tests, the same Atlas setup and teardown scripts can be used locally to provision a |
| 68 | +cluster for development. |
| 69 | + |
| 70 | +#### Case 1: Driver can successfully create and list search indexes |
| 71 | + |
| 72 | +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 73 | + |
| 74 | +2. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: |
| 75 | + |
| 76 | + ```typescript |
| 77 | + { |
| 78 | + name: 'test-search-index', |
| 79 | + definition: { |
| 80 | + mappings: { dynamic: false } |
| 81 | + } |
| 82 | + } |
| 83 | + ``` |
| 84 | + |
| 85 | +3. Assert that the command returns the name of the index: `"test-search-index"`. |
| 86 | + |
| 87 | +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied and store the |
| 88 | + value in a variable `index`: |
| 89 | + |
| 90 | + - An index with the `name` of `test-search-index` is present and the index has a field `queryable` with a value of |
| 91 | + `true`. |
| 92 | + |
| 93 | +5. Assert that `index` has a property `latestDefinition` whose value is `{ 'mappings': { 'dynamic': false } }` |
| 94 | + |
| 95 | +#### Case 2: Driver can successfully create multiple indexes in batch |
| 96 | + |
| 97 | +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 98 | + |
| 99 | +2. Create two new search indexes on `coll0` with the `createSearchIndexes` helper. Use the following definitions when |
| 100 | + creating the indexes. These definitions are referred to as `indexDefinitions`. |
| 101 | + |
| 102 | + ```typescript |
| 103 | + { |
| 104 | + name: 'test-search-index-1', |
| 105 | + definition: { |
| 106 | + mappings: { dynamic: false } |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + { |
| 111 | + name: 'test-search-index-2', |
| 112 | + definition: { |
| 113 | + mappings: { dynamic: false } |
| 114 | + } |
| 115 | + } |
| 116 | + ``` |
| 117 | + |
| 118 | +3. Assert that the command returns an array containing the new indexes' names: |
| 119 | + `["test-search-index-1", "test-search-index-2"]`. |
| 120 | + |
| 121 | +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following conditions are satisfied. |
| 122 | + |
| 123 | + - An index with the `name` of `test-search-index-1` is present and index has a field `queryable` with the value of |
| 124 | + `true`. Store result in `index1`. |
| 125 | + - An index with the `name` of `test-search-index-2` is present and index has a field `queryable` with the value of |
| 126 | + `true`. Store result in `index2`. |
| 127 | + |
| 128 | +5. Assert that `index1` and `index2` have the property `latestDefinition` whose value is |
| 129 | + `{ "mappings" : { "dynamic" : false } }` |
| 130 | + |
| 131 | +#### Case 3: Driver can successfully drop search indexes |
| 132 | + |
| 133 | +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 134 | + |
| 135 | +2. Create a new search index on `coll0` with the following definition: |
| 136 | + |
| 137 | + ```typescript |
| 138 | + { |
| 139 | + name: 'test-search-index', |
| 140 | + definition: { |
| 141 | + mappings: { dynamic: false } |
| 142 | + } |
| 143 | + } |
| 144 | + ``` |
| 145 | + |
| 146 | +3. Assert that the command returns the name of the index: `"test-search-index"`. |
| 147 | + |
| 148 | +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied: |
| 149 | + |
| 150 | + - An index with the `name` of `test-search-index` is present and index has a field `queryable` with the value of |
| 151 | + `true`. |
| 152 | + |
| 153 | +5. Run a `dropSearchIndex` on `coll0`, using `test-search-index` for the name. |
| 154 | + |
| 155 | +6. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until `listSearchIndexes` returns an empty array. |
| 156 | + |
| 157 | +This test fails if it times out waiting for the deletion to succeed. |
| 158 | + |
| 159 | +#### Case 4: Driver can update a search index |
| 160 | + |
| 161 | +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 162 | + |
| 163 | +2. Create a new search index on `coll0` with the following definition: |
| 164 | + |
| 165 | + ```typescript |
| 166 | + { |
| 167 | + name: 'test-search-index', |
| 168 | + definition: { |
| 169 | + mappings: { dynamic: false } |
| 170 | + } |
| 171 | + } |
| 172 | + ``` |
| 173 | + |
| 174 | +3. Assert that the command returns the name of the index: `"test-search-index"`. |
| 175 | + |
| 176 | +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied: |
| 177 | + |
| 178 | + - An index with the `name` of `test-search-index` is present and index has a field `queryable` with the value of |
| 179 | + `true`. |
| 180 | + |
| 181 | +5. Run a `updateSearchIndex` on `coll0`, using the following definition. |
| 182 | + |
| 183 | + ```typescript |
| 184 | + { |
| 185 | + name: 'test-search-index', |
| 186 | + definition: { |
| 187 | + mappings: { dynamic: true } |
| 188 | + } |
| 189 | + } |
| 190 | + ``` |
| 191 | + |
| 192 | +6. Assert that the command does not error and the server responds with a success. |
| 193 | + |
| 194 | +7. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following conditions are satisfied: |
| 195 | + |
| 196 | + - An index with the `name` of `test-search-index` is present. This index is referred to as `index`. |
| 197 | + - The index has a field `queryable` with a value of `true` and has a field `status` with the value of `READY`. |
| 198 | + |
| 199 | +8. Assert that an index is present with the name `test-search-index` and the definition has a property |
| 200 | + `latestDefinition` whose value is `{ 'mappings': { 'dynamic': true } }`. |
| 201 | + |
| 202 | +#### Case 5: `dropSearchIndex` suppresses namespace not found errors |
| 203 | + |
| 204 | +1. Create a driver-side collection object for a randomly generated collection name. Do not create this collection on the |
| 205 | + server. |
| 206 | +2. Run a `dropSearchIndex` command and assert that no error is thrown. |
| 207 | + |
| 208 | +#### Case 6: Driver can successfully create and list search indexes with non-default readConcern and writeConcern |
| 209 | + |
| 210 | +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 211 | + |
| 212 | +2. Apply a write concern `WriteConcern(w=1)` and a read concern with `ReadConcern(level="majority")` to `coll0`. |
| 213 | + |
| 214 | +3. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: |
| 215 | + |
| 216 | + ```typescript |
| 217 | + { |
| 218 | + name: 'test-search-index-case6', |
| 219 | + definition: { |
| 220 | + mappings: { dynamic: false } |
| 221 | + } |
| 222 | + } |
| 223 | + ``` |
| 224 | + |
| 225 | +4. Assert that the command returns the name of the index: `"test-search-index-case6"`. |
| 226 | + |
| 227 | +5. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied and store the |
| 228 | + value in a variable `index`: |
| 229 | + |
| 230 | + - An index with the `name` of `test-search-index-case6` is present and the index has a field `queryable` with a value |
| 231 | + of `true`. |
| 232 | + |
| 233 | +6. Assert that `index` has a property `latestDefinition` whose value is `{ 'mappings': { 'dynamic': false } }` |
| 234 | + |
| 235 | +#### Case 7: Driver can successfully handle search index types when creating indexes |
| 236 | + |
| 237 | +01. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 238 | + |
| 239 | +02. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: |
| 240 | + |
| 241 | + ```typescript |
| 242 | + |
| 243 | + { |
| 244 | + name: 'test-search-index-case7-implicit', |
| 245 | + definition: { |
| 246 | + mappings: { dynamic: false } |
| 247 | + } |
| 248 | + } |
| 249 | + ``` |
| 250 | + |
| 251 | +03. Assert that the command returns the name of the index: `"test-search-index-case7-implicit"`. |
| 252 | + |
| 253 | +04. Run `coll0.listSearchIndexes('test-search-index-case7-implicit')` repeatedly every 5 seconds until the following |
| 254 | + condition is satisfied and store the value in a variable `index1`: |
| 255 | + |
| 256 | + - An index with the `name` of `test-search-index-case7-implicit` is present and the index has a field `queryable` |
| 257 | + with a value of `true`. |
| 258 | + |
| 259 | +05. Assert that `index1` has a property `type` whose value is `search`. |
| 260 | + |
| 261 | +06. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: |
| 262 | + |
| 263 | + ```typescript |
| 264 | +
|
| 265 | + { |
| 266 | + name: 'test-search-index-case7-explicit', |
| 267 | + type: 'search', |
| 268 | + definition: { |
| 269 | + mappings: { dynamic: false } |
| 270 | + } |
| 271 | + } |
| 272 | + ``` |
| 273 | + |
| 274 | +07. Assert that the command returns the name of the index: `"test-search-index-case7-explicit"`. |
| 275 | + |
| 276 | +08. Run `coll0.listSearchIndexes('test-search-index-case7-explicit')` repeatedly every 5 seconds until the following |
| 277 | + condition is satisfied and store the value in a variable `index2`: |
| 278 | + |
| 279 | + - An index with the `name` of `test-search-index-case7-explicit` is present and the index has a field `queryable` |
| 280 | + with a value of `true`. |
| 281 | + |
| 282 | +09. Assert that `index2` has a property `type` whose value is `search`. |
| 283 | + |
| 284 | +10. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition: |
| 285 | + |
| 286 | + ```typescript |
| 287 | +
|
| 288 | + { |
| 289 | + name: 'test-search-index-case7-vector', |
| 290 | + type: 'vectorSearch', |
| 291 | + definition: { |
| 292 | + fields: [ |
| 293 | + { |
| 294 | + type: 'vector', |
| 295 | + path: 'plot_embedding', |
| 296 | + numDimensions: 1536, |
| 297 | + similarity: 'euclidean', |
| 298 | + }, |
| 299 | + ] |
| 300 | + } |
| 301 | + } |
| 302 | + ``` |
| 303 | + |
| 304 | +11. Assert that the command returns the name of the index: `"test-search-index-case7-vector"`. |
| 305 | + |
| 306 | +12. Run `coll0.listSearchIndexes('test-search-index-case7-vector')` repeatedly every 5 seconds until the following |
| 307 | + condition is satisfied and store the value in a variable `index3`: |
| 308 | + |
| 309 | + - An index with the `name` of `test-search-index-case7-vector` is present and the index has a field `queryable` with |
| 310 | + a value of `true`. |
| 311 | + |
| 312 | +13. Assert that `index3` has a property `type` whose value is `vectorSearch`. |
| 313 | + |
| 314 | +#### Case 8: Driver requires explicit type to create a vector search index |
| 315 | + |
| 316 | +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). |
| 317 | + |
| 318 | +2. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition: |
| 319 | + |
| 320 | + ```typescript |
| 321 | +
|
| 322 | + { |
| 323 | + name: 'test-search-index-case8-error', |
| 324 | + definition: { |
| 325 | + fields: [ |
| 326 | + { |
| 327 | + type: 'vector', |
| 328 | + path: 'plot_embedding', |
| 329 | + numDimensions: 1536, |
| 330 | + similarity: 'euclidean', |
| 331 | + }, |
| 332 | + ] |
| 333 | + } |
| 334 | + } |
| 335 | + ``` |
| 336 | + |
| 337 | +3. Assert that the command throws an exception containing the string "Attribute mappings missing" due to the `mappings` |
| 338 | + field missing. |
0 commit comments