// // Copyright (c) 2016 Couchbase, Inc. 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.couchbase.lite.mockserver; import com.couchbase.lite.Manager; import com.couchbase.lite.Misc; import com.couchbase.lite.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okio.Buffer; /** * Misc helper methods for MockWebserver-based Mock objects */ public class MockHelper { public static final String PATH_REGEX_DB = "/db/?"; public static final String PATH_REGEX_CHECKPOINT = "/db/_local.*"; public static final String PATH_REGEX_CHANGES = "/db/_changes.*"; public static final String PATH_REGEX_CHANGES_NORMAL = "/db/_changes\\?feed=normal.*"; public static final String PATH_REGEX_CHANGES_LONGPOLL = "/db/_changes\\?feed=longpoll.*"; public static final String PATH_REGEX_REVS_DIFF = "/db/_revs_diff.*"; public static final String PATH_REGEX_BULK_DOCS = "/db/_bulk_docs.*"; public static final String PATH_REGEX_SESSION = "/db/_session.*"; public static final String PATH_REGEX_SESSION_COUCHDB = "/_session.*"; public static final String PATH_REGEX_FACEBOOK_AUTH = "/db/_facebook.*"; public static final String PATH_REGEX_BULK_GET = "/db/_bulk_get.*"; public static final String PATH_REGEX_ALL_DOCS = "/db/_all_docs.*"; public static MockWebServer getMockWebServer(MockDispatcher dispatcher) { MockWebServer server = new MockWebServer(); server.setDispatcher(dispatcher); return server; } /** * Get a "preloaded" mock CouchDB suitable to be used as a pull replication target. * It's preloaded in the sense that it is ready serve up mock documents. * <p/> * This can't be used to simulate a Sync Gateway, because it does not support _bulk_get * * @param dispatcher the MockDispatcher * @param numMockDocsToServe how many docs should be served to pull replicator? * @param numDocsPerChangesResponse how many docs to add to each _changes response? MAXINT for all. */ public static MockWebServer getPreloadedPullTargetMockCouchDB(MockDispatcher dispatcher, int numMockDocsToServe, int numDocsPerChangesResponse) { return new MockPreloadedPullTarget(dispatcher, numMockDocsToServe, numDocsPerChangesResponse).getMockWebServer(); } public static void set200OKJson(MockResponse mockResponse) { mockResponse.setStatus("HTTP/1.1 200 OK") .setHeader("Content-Type", "application/json"); } public static void set201OKJson(MockResponse mockResponse) { mockResponse.setStatus("HTTP/1.1 201 OK") .setHeader("Content-Type", "application/json"); } public static void set404NotFoundJson(MockResponse mockResponse) { mockResponse.setStatus("HTTP/1.1 404 NOT FOUND") .setHeader("Content-Type", "application/json"); } public static void addFake404CheckpointResponse(MockWebServer mockWebServer) { MockResponse fakeCheckpointResponse = new MockResponse(); MockHelper.set404NotFoundJson(fakeCheckpointResponse); mockWebServer.enqueue(fakeCheckpointResponse); } public static Map<String, Object> generateRandomJsonMap() { Map<String, Object> randomJsonMap = new HashMap<String, Object>(); randomJsonMap.put(Misc.CreateUUID(), false); randomJsonMap.put("uuid", Misc.CreateUUID()); return randomJsonMap; } public static SmartMockResponse wrap(MockResponse mockResponse) { return new WrappedSmartMockResponse(mockResponse); } public static Map<String, Object> getJsonMapFromRequest(byte[] requestBody) throws IOException { return Manager.getObjectMapper().readValue(requestBody, Map.class); } /** * returns decompressed byte[] body */ public static byte[] getUncompressedBody(RecordedRequest request) { // NOTE: Buffer.readByteArray() consumes content. As we might reuse content later, // so needs to clone buffer. Buffer clone = request.getBody().clone(); byte[] body = clone.readByteArray(); if (isGzip(request)) { body = com.couchbase.lite.util.Utils.decompressByGzip(body); } return body; } /** * returns decompressed String body */ public static String getUtf8Body(RecordedRequest request) throws Exception { return new String(getUncompressedBody(request), "UTF-8"); } /* * check if gzip is used for request body */ public static boolean isGzip(RecordedRequest request) { return request.getHeader("Content-Encoding") != null && request.getHeader("Content-Encoding").contains("gzip"); } public static class Batcher<T> { private BlockingQueue<T> items; private int batchSize; public Batcher(List<T> items, int batchSize) { this.items = new LinkedBlockingQueue<T>(items); this.batchSize = batchSize; } public boolean hasMoreBatches() { return !this.items.isEmpty(); } public List<T> nextBatch() { List<T> batch = new ArrayList<T>(); for (int i = 0; i < batchSize; i++) { if (!this.items.isEmpty()) { try { T item = this.items.take(); batch.add(item); } catch (InterruptedException e) { throw new RuntimeException(e); } } } return batch; } } public static List<MockDocumentGet.MockDocument> getMockDocuments(int numDocs) { List<MockDocumentGet.MockDocument> mockDocs = new ArrayList<MockDocumentGet.MockDocument>(); for (int i = 0; i < numDocs; i++) { String docId = String.format("doc%s", i); String revIdHash = Misc.CreateUUID().substring(0, 4); String revId = String.format("1-%s", revIdHash); int seq = i; // mock documents to be pulled MockDocumentGet.MockDocument mockDoc = new MockDocumentGet.MockDocument(docId, revId, seq); mockDoc.setJsonMap(MockHelper.generateRandomJsonMap()); mockDocs.add(mockDoc); } return mockDocs; } public static boolean shutdown(MockWebServer server, MockDispatcher dispatcher) { if (dispatcher != null) dispatcher.setShutdown(true); return shutdown(server); } public static boolean shutdown(MockWebServer server) { return shutdown(server, 10); } public static boolean shutdown(MockWebServer server, int maxRetry) { if (server == null) return false; boolean ok = false; while (!ok && maxRetry-- > 0) { try { server.shutdown(); ok = true; } catch (IOException ioe) { Log.e(Log.TAG, "Failed to shutdown MockWebServer", ioe); } } return ok; } }