test.java.com.cloudant.tests.util.Utils Maven / Gradle / Ivy
Show all versions of cloudant-client Show documentation
/*
* Copyright (c) 2015 IBM Corp. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package com.cloudant.tests.util;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.cloudant.client.api.CloudantClient;
import com.cloudant.client.api.Database;
import com.cloudant.client.api.DesignDocumentManager;
import com.cloudant.client.api.model.DesignDocument;
import com.cloudant.client.api.model.ReplicatorDocument;
import com.cloudant.client.api.model.Response;
import com.cloudant.client.internal.URIBase;
import com.cloudant.client.org.lightcouch.NoDocumentException;
import com.cloudant.http.Http;
import com.cloudant.http.HttpConnection;
import com.cloudant.http.HttpConnectionInterceptorContext;
import com.cloudant.http.HttpConnectionResponseInterceptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URI;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
public class Utils {
//wait up to 2 minutes for replications to complete
private static final long TIMEOUT_MILLISECONDS = TimeUnit.MINUTES.toMillis(2);
//Design documents directory
private static final File DESIGN_DOC_DIR
= new File(System.getProperty("user.dir") + "/src/test/resources/design-files");
public static void removeReplicatorTestDoc(CloudantClient account, String replicatorDocId)
throws Exception {
//Grab replicator doc revision using HTTP HEAD command
String replicatorDb = "_replicator";
URI uri = new URIBase(account.getBaseUri()).path(replicatorDb).path(replicatorDocId)
.build();
HttpConnection head = Http.HEAD(uri);
//add a response interceptor to allow us to retrieve the ETag revision header
final AtomicReference revisionRef = new AtomicReference();
head.responseInterceptors.add(new HttpConnectionResponseInterceptor() {
@Override
public HttpConnectionInterceptorContext interceptResponse
(HttpConnectionInterceptorContext context) {
revisionRef.set(context.connection.getConnection().getHeaderField("ETag"));
return context;
}
});
account.executeRequest(head);
String revision = revisionRef.get();
assertNotNull("The revision should not be null", revision);
Database replicator = account.database(replicatorDb, false);
Response removeResponse = replicator.remove(replicatorDocId,
revision.replaceAll("\"", ""));
assertThat(removeResponse.getError(), is(nullValue()));
}
public static ReplicatorDocument waitForReplicatorToStart(CloudantClient account,
String replicatorDocId)
throws Exception {
return waitForReplicatorToReachStatus(account, replicatorDocId, "triggered");
}
public static ReplicatorDocument waitForReplicatorToComplete(CloudantClient account,
String replicatorDocId)
throws Exception {
return waitForReplicatorToReachStatus(account, replicatorDocId, "completed");
}
public static ReplicatorDocument waitForReplicatorToReachStatus(CloudantClient account,
String replicatorDocId,
String status)
throws Exception {
ReplicatorDocument replicatorDoc = null;
boolean finished = false;
long startTime = System.currentTimeMillis();
long timeout = startTime + TIMEOUT_MILLISECONDS;
//initial wait of 100 ms
long delay = 100;
while (!finished && System.currentTimeMillis() < timeout) {
//Sleep before finding replication document
Thread.sleep(delay);
replicatorDoc = account.replicator()
.replicatorDocId(replicatorDocId)
.find();
//Check if replicator doc is in specified state
String state;
if (replicatorDoc != null && (state = replicatorDoc.getReplicationState()) != null) {
//if we've reached the status or we reached an error then we are finished
if (state.equalsIgnoreCase(status) || state.equalsIgnoreCase("error")) {
finished = true;
}
}
//double the delay for the next iteration
delay *= 2;
}
if (!finished) {
throw new TimeoutException("Timed out waiting for replication to complete");
}
return replicatorDoc;
}
public static String generateUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
/**
*
* When testing against a cluster requests can be routed to different nodes. This means after
* a save a document may not appear for all subsequent requests until an internal replication
* has finished between all the nodes. This method tries to find a document up to maxRetries
* times, with retries separated by 0.5 s. Retrying may route the request to a node that
* already has the document and the delay between retries also gives an opportunity for
* internal replication to complete between nodes increasing the chance of retrieving the
* document.
*
*
* It should be noted that trying to read back a document immediately after writing to a cluster
* is not a best practice, but is needed in some test cases to reproduce, for example,
* conflict conditions. It is also worth noting that a document being returned from this
* method is not a guarantee that it has reached all nodes, only that it has reached a node
* that received one of the requests.
*
*
* @param db
* @param docId
*/
public static T findDocumentWithRetries(Database db, String docId, Class clazz, int
maxRetries) throws Exception {
for (int i = 1; i <= maxRetries; i++) {
try {
return db.find(clazz, docId);
} catch (NoDocumentException e) {
if (i == maxRetries) {
throw e;
} else {
//sleep for 0.5 s before trying again
Thread.sleep(500);
}
}
}
throw new Exception("Retries exceeded");
}
/**
* Test utility that splits a string and asserts that the expected number of separators were
* found. Expected number is for separators, but the assertion is done on parts, so this
* method is not safe for strings that end in their separator sequence.
*
* @param toSplit the string to split
* @param splitOn the separator string
* @param expectedNumber the expected number of separators
* @return
*/
public static String[] splitAndAssert(String toSplit, String splitOn, int expectedNumber) {
String[] parts = toSplit.split(splitOn);
assertEquals("There should be " + expectedNumber + " instances of " + splitOn + " in the " +
"content", expectedNumber + 1, parts.length);
return parts;
}
/**
* Test utility to put design documents under the testing resource folder in to the database.
* @param db database to put the design docs
* @param directory location of design docs
*/
public static void putDesignDocs(Database db, File directory) throws FileNotFoundException {
//Get design documents from directory
List designDocuments = DesignDocumentManager.fromDirectory(directory);
DesignDocumentManager designDocumentManager = db.getDesignDocumentManager();
DesignDocument[] designDocArray = designDocuments
.toArray(new DesignDocument[designDocuments.size()]);
//Put documents into database
designDocumentManager.put(designDocArray);
}
public static void putDesignDocs(Database db) throws FileNotFoundException {
putDesignDocs(db, DESIGN_DOC_DIR);
}
public static void assertOKResponse(Response r) throws Exception {
assertTrue("The response code should be 2XX was " + r.getStatusCode(), r.getStatusCode()
/ 100 == 2);
}
}