0
# Specialized Testing
1
2
The Elasticsearch test framework provides specialized testing utilities for specific domains including XContent serialization, query validation, REST API testing, and disruption simulation. These utilities enable comprehensive testing of Elasticsearch's specialized functionality.
3
4
## XContent Testing
5
6
XContent testing utilities for JSON/XML serialization, deserialization, and content validation.
7
8
### AbstractXContentTestCase
9
10
Base class for testing XContent serialization and deserialization of domain objects.
11
12
```{ .api }
13
package org.elasticsearch.test;
14
15
import org.elasticsearch.xcontent.ToXContent;
16
import org.elasticsearch.xcontent.XContent;
17
import org.elasticsearch.xcontent.XContentBuilder;
18
import org.elasticsearch.xcontent.XContentParser;
19
import org.elasticsearch.xcontent.XContentType;
20
21
/**
22
* Abstract base class for testing XContent serialization/deserialization of objects.
23
* Provides comprehensive testing of JSON/XML round-trip conversion and parsing.
24
*
25
* @param <T> the type being tested for XContent compatibility
26
*/
27
public abstract class AbstractXContentTestCase<T extends ToXContent> extends ESTestCase {
28
29
/**
30
* Creates a random test instance of the object being tested.
31
* Subclasses must implement this to provide random test data.
32
*
33
* @return random instance for testing
34
*/
35
protected abstract T createTestInstance();
36
37
/**
38
* Parses an object from XContent. Subclasses must implement this
39
* to provide parsing logic for their specific type.
40
*
41
* @param parser XContent parser positioned at object start
42
* @return parsed object instance
43
*/
44
protected abstract T doParseInstance(XContentParser parser) throws IOException;
45
46
/**
47
* Tests round-trip serialization and deserialization across all XContent types.
48
* Verifies that objects can be serialized to XContent and parsed back
49
* to equivalent objects.
50
*/
51
public final void testFromXContent() throws IOException;
52
53
/**
54
* Tests serialization to XContent produces expected output format.
55
*/
56
public final void testToXContent() throws IOException;
57
58
/**
59
* Tests parsing with additional random fields to ensure forward compatibility.
60
*/
61
public final void testToXContentWithRandomFields() throws IOException;
62
63
/**
64
* Indicates whether the test should insert random fields during parsing tests.
65
* Override to return false if your type doesn't support unknown fields.
66
*
67
* @return true if random fields should be inserted, false otherwise
68
*/
69
protected boolean supportsUnknownFields();
70
71
/**
72
* Returns XContent types to test. Override to limit testing to specific types.
73
*
74
* @return array of XContent types to test
75
*/
76
protected XContentType[] getSupportedXContentTypes();
77
78
/**
79
* Returns ToXContent parameters to use during serialization testing.
80
*
81
* @return ToXContent.Params for serialization
82
*/
83
protected ToXContent.Params getToXContentParams();
84
85
/**
86
* Predicate to determine which fields should have random content inserted.
87
*
88
* @param field field path to check
89
* @return true if random content can be inserted at this field
90
*/
91
protected Predicate<String> getRandomFieldsExcludeFilter();
92
93
/**
94
* Assertion method to verify two instances are equivalent after round-trip.
95
* Override to provide custom equality checking logic.
96
*
97
* @param expected original instance
98
* @param actual parsed instance
99
*/
100
protected void assertEqualInstances(T expected, T actual);
101
102
/**
103
* Creates a mutated version of the instance for inequality testing.
104
*
105
* @param instance original instance to mutate
106
* @return mutated instance that should not be equal to original
107
*/
108
protected T mutateInstance(T instance);
109
}
110
```
111
112
### XContentTestUtils
113
114
Utility methods for XContent testing scenarios.
115
116
```{ .api }
117
package org.elasticsearch.test;
118
119
import org.elasticsearch.xcontent.XContentBuilder;
120
import org.elasticsearch.xcontent.XContentParser;
121
import org.elasticsearch.xcontent.XContentType;
122
123
/**
124
* Utility methods for XContent testing including field insertion,
125
* content manipulation, and parsing verification.
126
*/
127
public class XContentTestUtils {
128
129
/**
130
* Inserts random fields into XContent to test forward compatibility.
131
*
132
* @param builder XContent builder to insert fields into
133
* @param value object to serialize with random fields
134
* @param excludeFilter predicate to exclude certain field paths
135
* @param xContentType XContent type being used
136
* @return XContent with random fields inserted
137
*/
138
public static BytesReference insertRandomFields(XContentBuilder builder,
139
Object value,
140
Predicate<String> excludeFilter,
141
XContentType xContentType) throws IOException;
142
143
/**
144
* Shuffles XContent field order to test order independence.
145
*
146
* @param original original XContent
147
* @param xContentType XContent type
148
* @return XContent with shuffled field order
149
*/
150
public static BytesReference shuffle(BytesReference original,
151
XContentType xContentType) throws IOException;
152
153
/**
154
* Compares two XContent objects for semantic equality, ignoring field order.
155
*
156
* @param expected expected XContent
157
* @param actual actual XContent
158
* @param xContentType XContent type
159
* @return true if content is semantically equal
160
*/
161
public static boolean equivalentXContent(BytesReference expected,
162
BytesReference actual,
163
XContentType xContentType) throws IOException;
164
165
/**
166
* Creates a random XContent object with specified depth and field count.
167
*
168
* @param xContentType XContent type to generate
169
* @param maxDepth maximum nesting depth
170
* @param maxFields maximum fields per object
171
* @return random XContent for testing
172
*/
173
public static BytesReference randomXContent(XContentType xContentType,
174
int maxDepth,
175
int maxFields) throws IOException;
176
177
/**
178
* Validates that XContent can be parsed without errors.
179
*
180
* @param content XContent to validate
181
* @param xContentType XContent type
182
* @return true if content is valid and parseable
183
*/
184
public static boolean isValidXContent(BytesReference content,
185
XContentType xContentType);
186
}
187
```
188
189
### XContent Testing Examples
190
191
```java
192
import org.elasticsearch.test.AbstractXContentTestCase;
193
import org.elasticsearch.xcontent.XContentParser;
194
import org.elasticsearch.xcontent.XContentType;
195
196
public class MyObjectXContentTest extends AbstractXContentTestCase<MyObject> {
197
198
@Override
199
protected MyObject createTestInstance() {
200
return new MyObject(
201
randomAlphaOfLength(10),
202
randomIntBetween(1, 100),
203
randomBoolean(),
204
randomFrom("type1", "type2", "type3")
205
);
206
}
207
208
@Override
209
protected MyObject doParseInstance(XContentParser parser) throws IOException {
210
return MyObject.fromXContent(parser);
211
}
212
213
@Override
214
protected boolean supportsUnknownFields() {
215
return true; // MyObject can handle unknown fields gracefully
216
}
217
218
@Override
219
protected Predicate<String> getRandomFieldsExcludeFilter() {
220
// Exclude fields that have strict parsing requirements
221
return field -> field.equals("metadata.config");
222
}
223
224
public void testSpecificXContentScenarios() throws IOException {
225
MyObject original = createTestInstance();
226
227
// Test all supported XContent types
228
for (XContentType xContentType : XContentType.values()) {
229
if (xContentType == XContentType.SMILE || xContentType == XContentType.CBOR) {
230
continue; // Skip binary formats for this example
231
}
232
233
// Serialize to XContent
234
XContentBuilder builder = XContentBuilder.builder(xContentType.xContent());
235
original.toXContent(builder, ToXContent.EMPTY_PARAMS);
236
BytesReference serialized = BytesReference.bytes(builder);
237
238
// Parse back and verify
239
XContentParser parser = createParser(xContentType.xContent(), serialized);
240
MyObject parsed = doParseInstance(parser);
241
242
assertEqualInstances(original, parsed);
243
}
244
}
245
}
246
```
247
248
## Query Testing
249
250
Specialized testing utilities for Elasticsearch query builders and query validation.
251
252
### AbstractQueryTestCase
253
254
Base class for testing query builders with comprehensive validation.
255
256
```{ .api }
257
package org.elasticsearch.test;
258
259
import org.apache.lucene.search.Query;
260
import org.elasticsearch.index.query.QueryBuilder;
261
import org.elasticsearch.index.query.SearchExecutionContext;
262
263
/**
264
* Abstract base class for testing QueryBuilder implementations.
265
* Provides comprehensive testing of query serialization, parsing,
266
* Lucene query generation, and caching behavior.
267
*
268
* @param <QB> the QueryBuilder type being tested
269
*/
270
public abstract class AbstractQueryTestCase<QB extends QueryBuilder> extends AbstractXContentTestCase<QB> {
271
272
/**
273
* Creates a random query builder instance for testing.
274
*
275
* @return random query builder
276
*/
277
protected abstract QB doCreateTestQueryBuilder();
278
279
/**
280
* Tests that the query builder can be converted to a Lucene Query.
281
*/
282
public void testToQuery() throws IOException;
283
284
/**
285
* Tests query builder caching behavior.
286
*/
287
public void testCacheable() throws IOException;
288
289
/**
290
* Tests that malformed queries are rejected during parsing.
291
*/
292
public void testMalformedQueries() throws IOException;
293
294
/**
295
* Tests query builder equality and hashCode implementation.
296
*/
297
public void testQueryBuilderEquality();
298
299
/**
300
* Performs assertions on the generated Lucene query.
301
* Override to provide query-specific validation.
302
*
303
* @param query generated Lucene query
304
* @param queryBuilder original query builder
305
*/
306
protected void doAssertLuceneQuery(Query query, QB queryBuilder, SearchExecutionContext context);
307
308
/**
309
* Creates a SearchExecutionContext for query testing.
310
*
311
* @return configured search execution context
312
*/
313
protected SearchExecutionContext createSearchExecutionContext();
314
315
/**
316
* Returns field names that should be mapped in the test index.
317
*
318
* @return collection of field names
319
*/
320
protected Collection<String> getMappedFieldNames();
321
322
/**
323
* Returns the mapping configuration for test fields.
324
*
325
* @return mapping as XContent string
326
*/
327
protected String getMappingForTest();
328
329
/**
330
* Indicates if this query type supports caching.
331
*
332
* @return true if query is cacheable
333
*/
334
protected boolean isCacheable(QB queryBuilder);
335
336
/**
337
* Creates alternative query builders that should not be equal to the main one.
338
*
339
* @param original original query builder
340
* @return list of non-equal query builders
341
*/
342
protected List<QB> getAlternativeQueryBuilders(QB original);
343
}
344
```
345
346
### Query Builder Testing Examples
347
348
```java
349
import org.elasticsearch.test.AbstractQueryTestCase;
350
import org.elasticsearch.index.query.TermQueryBuilder;
351
import org.apache.lucene.search.TermQuery;
352
353
public class TermQueryBuilderTest extends AbstractQueryTestCase<TermQueryBuilder> {
354
355
@Override
356
protected TermQueryBuilder doCreateTestQueryBuilder() {
357
String fieldName = randomFrom(MAPPED_FIELD_NAMES);
358
Object value = getRandomValueForFieldName(fieldName);
359
TermQueryBuilder query = new TermQueryBuilder(fieldName, value);
360
361
if (randomBoolean()) {
362
query.boost(randomFloat());
363
}
364
if (randomBoolean()) {
365
query.queryName(randomAlphaOfLength(8));
366
}
367
368
return query;
369
}
370
371
@Override
372
protected void doAssertLuceneQuery(Query query,
373
TermQueryBuilder queryBuilder,
374
SearchExecutionContext context) {
375
assertThat(query, instanceOf(TermQuery.class));
376
377
TermQuery termQuery = (TermQuery) query;
378
assertThat(termQuery.getTerm().field(), equalTo(queryBuilder.fieldName()));
379
380
// Verify boost is applied correctly
381
if (queryBuilder.boost() != AbstractQueryBuilder.DEFAULT_BOOST) {
382
assertThat(query.getBoost(), equalTo(queryBuilder.boost()));
383
}
384
}
385
386
@Override
387
protected String getMappingForTest() {
388
return """
389
{
390
"properties": {
391
"text_field": {"type": "text"},
392
"keyword_field": {"type": "keyword"},
393
"number_field": {"type": "integer"},
394
"date_field": {"type": "date"}
395
}
396
}""";
397
}
398
399
public void testInvalidFieldName() {
400
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
401
() -> new TermQueryBuilder(null, "value"));
402
403
assertThat(e.getMessage(), containsString("field name cannot be null"));
404
}
405
406
public void testTermQueryWithNumberField() throws IOException {
407
TermQueryBuilder query = new TermQueryBuilder("number_field", 42);
408
Query luceneQuery = query.toQuery(createSearchExecutionContext());
409
410
assertThat(luceneQuery, instanceOf(TermQuery.class));
411
// Additional number-specific assertions...
412
}
413
}
414
```
415
416
## REST Testing Framework
417
418
Comprehensive framework for testing Elasticsearch REST APIs.
419
420
### ESRestTestCase
421
422
Base class for REST API integration testing.
423
424
```{ .api }
425
package org.elasticsearch.test.rest;
426
427
import org.apache.http.HttpEntity;
428
import org.elasticsearch.client.Request;
429
import org.elasticsearch.client.Response;
430
import org.elasticsearch.client.RestClient;
431
432
/**
433
* Base class for REST API integration tests. Provides REST client management,
434
* request/response utilities, and cluster lifecycle for REST endpoint testing.
435
*/
436
public abstract class ESRestTestCase extends ESTestCase {
437
438
/**
439
* Returns the REST client for making HTTP requests to Elasticsearch.
440
*
441
* @return configured RestClient
442
*/
443
protected static RestClient client();
444
445
/**
446
* Returns an admin-level REST client with elevated privileges.
447
*
448
* @return admin RestClient
449
*/
450
protected static RestClient adminClient();
451
452
/**
453
* Performs a REST request and returns the response.
454
*
455
* @param request HTTP request to execute
456
* @return HTTP response
457
* @throws IOException on request failure
458
*/
459
protected Response performRequest(Request request) throws IOException;
460
461
/**
462
* Performs a REST request with the specified method and endpoint.
463
*
464
* @param method HTTP method (GET, POST, PUT, DELETE)
465
* @param endpoint REST endpoint path
466
* @return HTTP response
467
* @throws IOException on request failure
468
*/
469
protected Response performRequest(String method, String endpoint) throws IOException;
470
471
/**
472
* Performs a REST request with method, endpoint, and request body.
473
*
474
* @param method HTTP method
475
* @param endpoint REST endpoint path
476
* @param entity HTTP request body
477
* @return HTTP response
478
* @throws IOException on request failure
479
*/
480
protected Response performRequest(String method,
481
String endpoint,
482
HttpEntity entity) throws IOException;
483
484
/**
485
* Asserts that a REST response has the expected status code.
486
*
487
* @param response HTTP response to check
488
* @param expectedStatus expected HTTP status code
489
*/
490
protected static void assertResponseStatus(Response response, int expectedStatus);
491
492
/**
493
* Asserts that a response body contains the expected JSON content.
494
*
495
* @param response HTTP response
496
* @param expectedContent expected JSON content as map
497
*/
498
protected static void assertResponseContent(Response response,
499
Map<String, Object> expectedContent);
500
501
/**
502
* Parses response body as JSON and returns as Map.
503
*
504
* @param response HTTP response with JSON body
505
* @return parsed JSON as Map
506
* @throws IOException on parsing failure
507
*/
508
protected static Map<String, Object> responseAsMap(Response response) throws IOException;
509
510
/**
511
* Waits for cluster to reach specified health status via REST API.
512
*
513
* @param status health status to wait for ("green", "yellow", "red")
514
* @param timeout maximum time to wait
515
* @throws IOException on request failure
516
*/
517
protected void ensureHealth(String status, TimeValue timeout) throws IOException;
518
519
/**
520
* Creates an index via REST API.
521
*
522
* @param name index name
523
* @throws IOException on request failure
524
*/
525
protected void createIndex(String name) throws IOException;
526
527
/**
528
* Creates an index with settings and mappings via REST API.
529
*
530
* @param name index name
531
* @param settings index settings as JSON string
532
* @param mappings index mappings as JSON string
533
* @throws IOException on request failure
534
*/
535
protected void createIndex(String name, String settings, String mappings) throws IOException;
536
537
/**
538
* Deletes an index via REST API.
539
*
540
* @param name index name
541
* @throws IOException on request failure
542
*/
543
protected void deleteIndex(String name) throws IOException;
544
545
/**
546
* Indexes a document via REST API.
547
*
548
* @param index index name
549
* @param id document ID
550
* @param source document source as JSON string
551
* @return indexing response as Map
552
* @throws IOException on request failure
553
*/
554
protected Map<String, Object> indexDocument(String index,
555
String id,
556
String source) throws IOException;
557
558
/**
559
* Searches for documents via REST API.
560
*
561
* @param index index name
562
* @param query search query as JSON string
563
* @return search response as Map
564
* @throws IOException on request failure
565
*/
566
protected Map<String, Object> search(String index, String query) throws IOException;
567
}
568
```
569
570
### ObjectPath
571
572
Utility for navigating JSON response structures in REST tests.
573
574
```{ .api }
575
package org.elasticsearch.test.rest;
576
577
import java.util.List;
578
import java.util.Map;
579
580
/**
581
* Utility for navigating and extracting values from nested JSON structures
582
* in REST API responses. Supports dot notation path traversal.
583
*/
584
public class ObjectPath {
585
586
/**
587
* Creates an ObjectPath from a response Map.
588
*
589
* @param response parsed JSON response as Map
590
* @return ObjectPath for navigation
591
*/
592
public static ObjectPath createFromResponse(Map<String, Object> response);
593
594
/**
595
* Evaluates a path expression and returns the value.
596
*
597
* @param path dot-notation path (e.g., "hits.hits.0._source.title")
598
* @return value at the specified path
599
* @throws IllegalArgumentException if path doesn't exist
600
*/
601
public <T> T evaluate(String path);
602
603
/**
604
* Evaluates a path and returns the value, or default if path doesn't exist.
605
*
606
* @param path dot-notation path
607
* @param defaultValue default value to return if path missing
608
* @return value at path or default value
609
*/
610
public <T> T evaluate(String path, T defaultValue);
611
612
/**
613
* Checks if a path exists in the object structure.
614
*
615
* @param path dot-notation path to check
616
* @return true if path exists, false otherwise
617
*/
618
public boolean exists(String path);
619
620
/**
621
* Evaluates a path that should return a List.
622
*
623
* @param path dot-notation path
624
* @return List value at the specified path
625
*/
626
public List<Object> evaluateArraySize(String path);
627
628
/**
629
* Gets the size of an array at the specified path.
630
*
631
* @param path path to array
632
* @return array size
633
*/
634
public int getArraySize(String path);
635
636
/**
637
* Evaluates multiple paths and returns a Map of path->value.
638
*
639
* @param paths array of paths to evaluate
640
* @return Map of path to evaluated value
641
*/
642
public Map<String, Object> evaluateMultiple(String... paths);
643
}
644
```
645
646
### REST Testing Examples
647
648
```java
649
import org.elasticsearch.test.rest.ESRestTestCase;
650
import org.elasticsearch.test.rest.ObjectPath;
651
652
public class MyRestApiTest extends ESRestTestCase {
653
654
public void testIndexCreationAndSearch() throws IOException {
655
// Create index with custom settings
656
String settings = """
657
{
658
"settings": {
659
"number_of_shards": 1,
660
"number_of_replicas": 0
661
},
662
"mappings": {
663
"properties": {
664
"title": {"type": "text"},
665
"category": {"type": "keyword"}
666
}
667
}
668
}""";
669
670
Request createRequest = new Request("PUT", "/my-index");
671
createRequest.setJsonEntity(settings);
672
Response createResponse = performRequest(createRequest);
673
assertResponseStatus(createResponse, 200);
674
675
// Index a document
676
String doc = """
677
{
678
"title": "Sample Document",
679
"category": "test",
680
"timestamp": "2023-01-01T00:00:00Z"
681
}""";
682
683
Map<String, Object> indexResponse = indexDocument("my-index", "1", doc);
684
assertEquals("created", indexResponse.get("result"));
685
686
// Refresh index
687
performRequest("POST", "/my-index/_refresh");
688
689
// Search for the document
690
String searchQuery = """
691
{
692
"query": {
693
"match": {
694
"title": "Sample"
695
}
696
}
697
}""";
698
699
Map<String, Object> searchResponse = search("my-index", searchQuery);
700
701
// Use ObjectPath to navigate response
702
ObjectPath path = ObjectPath.createFromResponse(searchResponse);
703
704
assertEquals(1, path.evaluate("hits.total.value"));
705
assertEquals("1", path.evaluate("hits.hits.0._id"));
706
assertEquals("Sample Document", path.evaluate("hits.hits.0._source.title"));
707
assertTrue(path.exists("hits.hits.0._score"));
708
}
709
710
public void testClusterHealth() throws IOException {
711
Response healthResponse = performRequest("GET", "/_cluster/health");
712
assertResponseStatus(healthResponse, 200);
713
714
Map<String, Object> health = responseAsMap(healthResponse);
715
ObjectPath healthPath = ObjectPath.createFromResponse(health);
716
717
String status = healthPath.evaluate("status");
718
assertThat(status, anyOf(equalTo("green"), equalTo("yellow")));
719
720
int numberOfNodes = healthPath.evaluate("number_of_nodes");
721
assertThat(numberOfNodes, greaterThan(0));
722
}
723
724
public void testBulkIndexing() throws IOException {
725
String bulkBody = """
726
{"index":{"_index":"bulk-test","_id":"1"}}
727
{"title":"First Document","value":10}
728
{"index":{"_index":"bulk-test","_id":"2"}}
729
{"title":"Second Document","value":20}
730
""";
731
732
Request bulkRequest = new Request("POST", "/_bulk");
733
bulkRequest.setJsonEntity(bulkBody);
734
bulkRequest.addParameter("refresh", "true");
735
736
Response bulkResponse = performRequest(bulkRequest);
737
assertResponseStatus(bulkResponse, 200);
738
739
Map<String, Object> bulk = responseAsMap(bulkResponse);
740
ObjectPath bulkPath = ObjectPath.createFromResponse(bulk);
741
742
assertFalse("Bulk should not have errors", bulkPath.evaluate("errors"));
743
assertEquals(2, bulkPath.getArraySize("items"));
744
}
745
}
746
```
747
748
## Disruption Testing
749
750
Framework for simulating network partitions, service disruptions, and failure scenarios.
751
752
### NetworkDisruption
753
754
Simulation of network failures and partitions.
755
756
```{ .api }
757
package org.elasticsearch.test.disruption;
758
759
import org.elasticsearch.cluster.node.DiscoveryNode;
760
import org.elasticsearch.common.unit.TimeValue;
761
762
/**
763
* Simulates network disruptions between cluster nodes including
764
* partitions, delays, and connection failures.
765
*/
766
public class NetworkDisruption implements ServiceDisruptionScheme {
767
768
/**
769
* Creates a network partition that isolates the specified node.
770
*
771
* @param isolatedNode node to isolate from the cluster
772
* @param duration duration of the isolation
773
*/
774
public NetworkDisruption(DiscoveryNode isolatedNode, TimeValue duration);
775
776
/**
777
* Creates a network partition between two sets of nodes.
778
*
779
* @param nodeSetA first set of nodes
780
* @param nodeSetB second set of nodes
781
* @param duration duration of the partition
782
*/
783
public NetworkDisruption(Set<DiscoveryNode> nodeSetA,
784
Set<DiscoveryNode> nodeSetB,
785
TimeValue duration);
786
787
/**
788
* Starts the network disruption.
789
*/
790
@Override
791
public void startDisrupting();
792
793
/**
794
* Stops the network disruption and restores connectivity.
795
*/
796
@Override
797
public void stopDisrupting();
798
799
/**
800
* Checks if the disruption is currently active.
801
*
802
* @return true if disruption is active
803
*/
804
@Override
805
public boolean isDisrupting();
806
807
/**
808
* Sets the network delay to introduce latency.
809
*
810
* @param delay network delay duration
811
*/
812
public void setNetworkDelay(TimeValue delay);
813
814
/**
815
* Sets the packet loss rate for network simulation.
816
*
817
* @param lossRate packet loss rate (0.0 to 1.0)
818
*/
819
public void setPacketLossRate(double lossRate);
820
}
821
```
822
823
### Disruption Testing Examples
824
825
```java
826
import org.elasticsearch.test.ESIntegTestCase;
827
import org.elasticsearch.test.disruption.NetworkDisruption;
828
829
public class DisruptionTest extends ESIntegTestCase {
830
831
public void testNetworkPartition() throws Exception {
832
InternalTestCluster cluster = internalCluster();
833
cluster.ensureAtLeastNumDataNodes(3);
834
835
String masterNode = cluster.getMasterName();
836
Set<String> otherNodes = cluster.getNodeNames()
837
.stream()
838
.filter(name -> !name.equals(masterNode))
839
.collect(Collectors.toSet());
840
841
// Create network partition isolating master
842
NetworkDisruption disruption = new NetworkDisruption(
843
cluster.clusterService(masterNode).localNode(),
844
TimeValue.timeValueMinutes(1)
845
);
846
847
cluster.setDisruptionScheme(disruption);
848
disruption.startDisrupting();
849
850
try {
851
// Verify cluster detects partition
852
cluster.waitForState(state ->
853
state.nodes().getMasterNode() == null ||
854
!state.nodes().getMasterNode().getName().equals(masterNode),
855
TimeValue.timeValueSeconds(30)
856
);
857
858
// Verify operations still work on majority side
859
String nonMasterNode = otherNodes.iterator().next();
860
Client nonMasterClient = cluster.client(nonMasterNode);
861
862
createIndex("partition-test");
863
ensureGreen("partition-test");
864
865
} finally {
866
disruption.stopDisrupting();
867
868
// Wait for cluster to recover
869
cluster.waitForConsensus(TimeValue.timeValueMinutes(1));
870
ensureGreen("partition-test");
871
}
872
}
873
874
public void testSlowNetwork() throws Exception {
875
NetworkDisruption disruption = new NetworkDisruption(
876
Collections.emptySet(),
877
Collections.emptySet(),
878
TimeValue.timeValueMinutes(5)
879
);
880
881
// Add network delay
882
disruption.setNetworkDelay(TimeValue.timeValueSeconds(2));
883
884
internalCluster().setDisruptionScheme(disruption);
885
disruption.startDisrupting();
886
887
try {
888
// Operations should still work but be slower
889
long startTime = System.currentTimeMillis();
890
891
createIndex("slow-network-test");
892
ensureYellow("slow-network-test");
893
894
long duration = System.currentTimeMillis() - startTime;
895
assertThat("Operations should be slower with network delay",
896
duration, greaterThan(1000L));
897
898
} finally {
899
disruption.stopDisrupting();
900
}
901
}
902
}
903
```
904
905
## Best Practices
906
907
### XContent Testing
908
- Test all supported XContent formats (JSON, YAML, CBOR, SMILE)
909
- Include unknown field testing for forward compatibility
910
- Verify field order independence in parsing
911
- Test edge cases like empty objects and null values
912
913
### Query Testing
914
- Test query builder serialization round-trips
915
- Verify Lucene query generation for all query variants
916
- Include malformed query rejection testing
917
- Test caching behavior when applicable
918
919
### REST Testing
920
- Use ObjectPath for clean response navigation
921
- Test both success and error response scenarios
922
- Include proper HTTP status code validation
923
- Test with various parameter combinations
924
925
### Disruption Testing
926
- Always restore normal operation in finally blocks
927
- Test both partition detection and recovery scenarios
928
- Use realistic disruption durations
929
- Verify cluster remains functional during disruptions
930
931
These specialized testing utilities enable comprehensive validation of Elasticsearch's complex functionality while maintaining test reliability and readability.