or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions-matchers.mdcluster-management.mdcore-testing.mddata-generation.mdindex.mdmock-implementations.mdspecialized-testing.md

specialized-testing.mddocs/

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.