All Downloads are FREE. Search and download functionalities are using the official Maven repository.

test.java.com.cloudant.tests.util.Utils Maven / Gradle / Ivy

/*
 * Copyright © 2015, 2020 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.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.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.api.scheduler.SchedulerDocsResponse;
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 org.junit.jupiter.api.Assumptions;

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 String waitForReplicatorToStart(CloudantClient account,
                                                              String replicatorDocId)
            throws Exception {
        return waitForReplicatorToReachStatus(account, replicatorDocId, "triggered", "running");
    }

    public static String waitForReplicatorToComplete(CloudantClient account,
                                                                 String replicatorDocId)
            throws Exception {
        return waitForReplicatorToReachStatus(account, replicatorDocId, "completed");
    }

    public static String waitForReplicatorToReachStatus(CloudantClient account,
                                                                    String replicatorDocId,
                                                                    String... expectedStates)
            throws Exception {
        ReplicatorDocument replicatorDoc = null;

        long startTime = System.currentTimeMillis();
        long timeout = startTime + TIMEOUT_MILLISECONDS;
        //initial wait of 100 ms
        long delay = 100;

        while (System.currentTimeMillis() < timeout) {
            //Sleep before finding replication document
            Thread.sleep(delay);

            //Check if replicator doc is in specified state
            String state;

            if (account.metaInformation().getFeatures() != null &&
                    account.metaInformation().getFeatures().contains("scheduler")) {
                SchedulerDocsResponse.Doc doc = account.schedulerDoc(replicatorDocId);
                state = doc != null ? doc.getState() : null;
            } else {
                replicatorDoc = account.replicator()
                        .replicatorDocId(replicatorDocId)
                        .find();
                state = replicatorDoc != null ? replicatorDoc.getReplicationState() : null;
            }

            //if we've reached the status or we reached an error then we are finished
            if ("error".equalsIgnoreCase(state)) {
                return state;
            } else {
                for (String expectedState : expectedStates) {
                    if (expectedState.equalsIgnoreCase(state)) {
                        return state;
                    }
                }
            }
            //double the delay for the next iteration
            delay *= 2;
        }
        throw new TimeoutException("Timed out waiting for replication to complete");
    }

    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(expectedNumber + 1, parts.length, "There should be " + expectedNumber + " " + "instances of " + splitOn + " in the " + "content"); 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(r.getStatusCode() / 100 == 2, "The response code should be 2XX was " + r.getStatusCode()); } /** * Utility to ignore tests when running with optional OkHttp dependency and Java 8_252 or newer. * This should be applied to any test that uses the customSSLSocketFactory or * disableSSLAuthentication options. * * These options cannot be used with OkHttp and Java 8_252 or newer * (see https://github.com/square/okhttp/issues/5970. * OkHttp treats 8_252 and newer as equivalent to 9+ because of the availability of ALPN * support. In 9+ reflection cannot be used to infer the TrustManager from the SslSocketFactory * so setting the SslSocketFactory without a TrustManager is prevented. This blocks the route we * currently use to supply custom SslSocketFactory via the deprecated OkHttp OkUrlFactory path * via javax.net.ssl.HttpsURLConnection#setSSLSocketFactory(javax.net.ssl.SSLSocketFactory). * */ public static void assumeCustomSslAuthUsable(boolean isOkUsable) { String version = System.getProperty("java.version"); boolean java8_252OrHigher = false; // Java 1.8 or lower, 9+ are 9, 10 etc if (version.startsWith("1.")) { if (version.startsWith("1.8.")) { int update = Integer.parseInt(version.split("_")[1]); if (update >= 252) java8_252OrHigher = true; } } else { java8_252OrHigher = true; } Assumptions.assumeFalse(isOkUsable && java8_252OrHigher, "Test uses custom " + "SslSocketFactory, incompatible with OkHttp and Java 8_252 or newer."); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy