diff --git a/Jenkinsfile b/Jenkinsfile index 99e321357..4509d0ba5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ #!groovy /* - * Copyright © 2016, 2017 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -31,7 +31,7 @@ def runTests(testEnv, isServiceTests) { try { sh './gradlew -Dtest.couch.username=$DB_USER -Dtest.couch.password=$DB_PASSWORD -Dtest.couch.host=$DB_HOST -Dtest.couch.port=$DB_PORT -Dtest.couch.http=$DB_HTTP $GRADLE_TARGET' } finally { - junit '**/build/test-results/*.xml' + junit '**/build/test-results/**/*.xml' } } } diff --git a/build.gradle b/build.gradle index 759f15d88..efb1014f0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright © 2016 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 diff --git a/cloudant-client/build.gradle b/cloudant-client/build.gradle index 291f56778..592931ac6 100644 --- a/cloudant-client/build.gradle +++ b/cloudant-client/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright © 2016, 2017 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -25,7 +25,10 @@ dependencies { compile project(':cloudant-http') linkableJavadoc project(':cloudant-http') //test dependencies - testCompile group: 'junit', name: 'junit', version: '4.12' + testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.1.0' + testRuntime group: 'org.junit.platform', name: 'junit-platform-console', version: '1.1.0' + testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.1.0' + testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3' testCompile group: 'com.squareup.okhttp3', name: 'mockwebserver', version: '3.8.1' testCompile group: 'org.jmockit', name: 'jmockit', version: '1.34' testCompile group: 'org.littleshoot', name: 'littleproxy', version: '1.1.0' @@ -54,6 +57,12 @@ gradle.projectsEvaluated { } } +// we need Java 1.8 features for JUnit 5 features, but our production code is 1.6 +compileTestJava { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 +} + tasks.withType(Test) { def jMockit doFirst { @@ -65,35 +74,45 @@ tasks.withType(Test) { } } -test { - // Run tests for any DB - useJUnit { - excludeCategories 'com.cloudant.test.main.RequiresCloudant', - 'com.cloudant.test.main.RequiresCouch' +// hacky - see https://discuss.gradle.org/t/passing-arguments-to-a-task/8427/9 +def CustomJunitPlatformTask(include, exclude) { + return tasks.create(name: "exec_${include}_${exclude}", type: JavaExec, dependsOn: testClasses) { + classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath + main = 'org.junit.platform.console.ConsoleLauncher' + // arguments to pass to the application + args ('--reports-dir',testResultsDir, + '--scan-class-path') + // build up includes + if (include.size > 0) { + args += '--include-tag' + args += include.join("|") + } + // build up excludes + if (exclude.size > 0) { + args += '--exclude-tag' + args += exclude.join("|") + } + // inherit system properties + systemProperties System.properties + // some tests expect user.dir to be same as working dir, which + // defaults to project dir + systemProperty "user.dir", project.projectDir } } -task noDBTest(type: Test, dependsOn: testClasses) { - // Run unit tests that do not need a running database - useJUnit { - excludeCategories 'com.cloudant.test.main.RequiresDB' - } -} +// Run tests which are compatible with all versions of Couch or +// Cloudant. +// This is the default test target. +task test (dependsOn:CustomJunitPlatformTask([],['RequiresCouch','RequiresCloudant']), overwrite: true){} -task cloudantTest(type: Test, dependsOn: testClasses) { - // Run tests that can use any Cloudant - useJUnit { - excludeCategories 'com.cloudant.test.main.RequiresCloudantService' - } -} +// Run 'unit' tests which do not require a database. +task noDBTest(dependsOn: CustomJunitPlatformTask([],['RequiresDB'])) {} -task cloudantServiceTest(type: Test, dependsOn: testClasses) { - // Run all Cloudant service tests - useJUnit { - excludeCategories 'com.cloudant.test.main.RequiresCloudantLocal', - 'com.cloudant.test.main.RequiresCouch' - } -} +// Run tests that can use any Cloudant. +task cloudantTest(dependsOn: CustomJunitPlatformTask([],['RequiresCloudantService'])) {} + +// Run all Cloudant service tests. +task cloudantServiceTest(dependsOn: CustomJunitPlatformTask([],['RequiresCloudantLocal', 'RequiresCouch'])) {} //task for generating a client properties file class ClientProperties extends DefaultTask { diff --git a/cloudant-client/src/test/java/com/cloudant/api/query/FieldAssertHelper.java b/cloudant-client/src/test/java/com/cloudant/api/query/FieldAssertHelper.java index 100ad355d..ace2a2ade 100644 --- a/cloudant-client/src/test/java/com/cloudant/api/query/FieldAssertHelper.java +++ b/cloudant-client/src/test/java/com/cloudant/api/query/FieldAssertHelper.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017 IBM Corp. All rights reserved. + * Copyright © 2017, 2018 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 @@ -14,8 +14,8 @@ package com.cloudant.api.query; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import com.cloudant.client.api.query.Field; import com.cloudant.client.api.query.JsonIndex; @@ -40,7 +40,7 @@ public static class Json extends FieldAssertHelper @Override protected void assertField(JsonIndex.Field field, Sort.Order order) { - assertEquals("The order should be the same", order, field.getOrder()); + assertEquals(order, field.getOrder(), "The order should be the same"); } } @@ -53,20 +53,20 @@ public static class Text extends FieldAssertHelper { @Override protected void assertField(TextIndex.Field field, Type type) { - assertEquals("The type should be the same", type, field.getType()); + assertEquals(type, field.getType(), "The type should be the same"); } } public void assertFields(List actualFields) { - assertEquals("There should be the correct number of fields", expectedFields.size(), - actualFields.size()); + assertEquals(expectedFields.size(), + actualFields.size(), "There should be the correct number of fields"); for (F field : actualFields) { assertNotNull("The field should have a name", field.getName()); T expected = expectedFields.remove(field.getName()); - assertNotNull("Unexpected field " + field.getName() + " found.", expected); + assertNotNull(expected, "Unexpected field " + field.getName() + " found."); assertField(field, expected); } - assertEquals("All fields should be asserted.", 0, expectedFields.size()); + assertEquals(0, expectedFields.size(), "All fields should be asserted."); } protected abstract void assertField(F field, T type); diff --git a/cloudant-client/src/test/java/com/cloudant/api/query/IndexCreationTests.java b/cloudant-client/src/test/java/com/cloudant/api/query/IndexCreationTests.java index f062f21d8..4d59f65d6 100644 --- a/cloudant-client/src/test/java/com/cloudant/api/query/IndexCreationTests.java +++ b/cloudant-client/src/test/java/com/cloudant/api/query/IndexCreationTests.java @@ -15,24 +15,24 @@ package com.cloudant.api.query; import static com.cloudant.client.api.query.Expression.gt; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.cloudant.client.api.query.JsonIndex; import com.cloudant.client.api.query.Selector; import com.cloudant.client.api.query.TextIndex; import com.cloudant.client.internal.query.Helpers; -import com.cloudant.tests.util.MockedServerTest; +import com.cloudant.tests.base.TestWithMockedServer; import com.google.gson.Gson; import com.google.gson.JsonObject; -import org.junit.Test; +import org.junit.jupiter.api.Test; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.RecordedRequest; import java.util.concurrent.TimeUnit; -public class IndexCreationTests extends MockedServerTest { +public class IndexCreationTests extends TestWithMockedServer { private static MockResponse CREATED = new MockResponse().setBody("{\"result\": \"created\"}"); @@ -122,7 +122,7 @@ public void createNamedTextIndex() throws Exception { @Test public void createTextIndexInDesignDoc() throws Exception { createIndexTest(TextIndex.builder() - .designDocument("testddoc") + .designDocument("testddoc") .definition(), "{type: \"text\", ddoc: \"testddoc\", index: {}}"); } @@ -216,6 +216,6 @@ private void createIndexTest(String definition, String expected) throws Exceptio db.createIndex(definition); RecordedRequest request = server.takeRequest(1, TimeUnit.SECONDS); JsonObject actual = new Gson().fromJson(request.getBody().readUtf8(), JsonObject.class); - assertEquals("The request body should match the expected", exp, actual); + assertEquals(exp, actual, "The request body should match the expected"); } } diff --git a/cloudant-client/src/test/java/com/cloudant/api/query/IndexDeletionTests.java b/cloudant-client/src/test/java/com/cloudant/api/query/IndexDeletionTests.java index 237573df1..4fdb5fe4d 100644 --- a/cloudant-client/src/test/java/com/cloudant/api/query/IndexDeletionTests.java +++ b/cloudant-client/src/test/java/com/cloudant/api/query/IndexDeletionTests.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017 IBM Corp. All rights reserved. + * Copyright © 2017, 2018 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 @@ -14,21 +14,21 @@ package com.cloudant.api.query; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import com.cloudant.tests.base.TestWithMockedServer; import com.cloudant.tests.util.MockWebServerResources; -import com.cloudant.tests.util.MockedServerTest; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import okhttp3.mockwebserver.RecordedRequest; import java.util.concurrent.TimeUnit; -public class IndexDeletionTests extends MockedServerTest { +public class IndexDeletionTests extends TestWithMockedServer { - @Before + @BeforeEach public void enqueueOK() { server.enqueue(MockWebServerResources.JSON_OK); } @@ -53,7 +53,7 @@ public void deleteTextIndex() throws Exception { private void assertDelete(String name, String ddoc, String type) throws Exception { RecordedRequest request = server.takeRequest(1, TimeUnit.SECONDS); - assertEquals("The request body should match the expected", "/" + dbResource.getDatabaseName() + - "/_index/_design/" + ddoc + "/" + type + "/" + name, request.getPath()); + assertEquals("/" + dbResource.getDatabaseName() + "/_index/_design/" + ddoc + "/" + type + + "/" + name, request.getPath(), "The request body should match the expected"); } } diff --git a/cloudant-client/src/test/java/com/cloudant/api/query/IndexLifecycleTest.java b/cloudant-client/src/test/java/com/cloudant/api/query/IndexLifecycleTest.java index 7e11454b4..90b95e7e7 100644 --- a/cloudant-client/src/test/java/com/cloudant/api/query/IndexLifecycleTest.java +++ b/cloudant-client/src/test/java/com/cloudant/api/query/IndexLifecycleTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017 IBM Corp. All rights reserved. + * Copyright © 2017, 2018 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 @@ -14,9 +14,8 @@ package com.cloudant.api.query; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import com.cloudant.client.api.Database; import com.cloudant.client.api.query.Field; import com.cloudant.client.api.query.Index; import com.cloudant.client.api.query.JsonIndex; @@ -24,22 +23,18 @@ import com.cloudant.client.api.query.TextIndex; import com.cloudant.client.api.query.Type; import com.cloudant.test.main.RequiresCloudant; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerTest; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; // This really requires Couch2.0 + text index support, but we don't have a way of expressing that -@Category(RequiresCloudant.class) +@RequiresCloudant /** * Index lifecycle test. @@ -47,18 +42,10 @@ * Test list indexes * After delete indexes */ -public class IndexLifecycleTest { +public class IndexLifecycleTest extends TestWithDbPerTest { - private CloudantClientResource clientResource = new CloudantClientResource(); - private DatabaseResource dbResource = new DatabaseResource(clientResource); - @Rule - public RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - private Database db; - - @Before + @BeforeEach public void createIndexes() throws Exception { - db = dbResource.get(); // Create a JSON index db.createIndex(JsonIndex.builder() @@ -84,14 +71,14 @@ public void listIndexes() throws Exception { { // JSON index List jIndexes = db.listIndexes().jsonIndexes(); - assertEquals("There should be one JSON index", 1, jIndexes.size()); + assertEquals(1, jIndexes.size(), "There should be one JSON index"); JsonIndex jIndex = jIndexes.get(0); - assertEquals("The ddoc should be correct", "_design/indexlifecycle", jIndex - .getDesignDocumentID()); - assertEquals("The name should be correct", "testjson", jIndex.getName()); - assertEquals("The type should be correct", "json", jIndex.getType()); + assertEquals("_design/indexlifecycle", jIndex + .getDesignDocumentID(), "The ddoc should be correct"); + assertEquals("testjson", jIndex.getName(), "The name should be correct"); + assertEquals("json", jIndex.getType(), "The type should be correct"); List fields = jIndex.getFields(); - assertEquals("There should be two fields", 2, fields.size()); + assertEquals(2, fields.size(), "There should be two fields"); // Field assertions new FieldAssertHelper.Json(Collections.singletonMap("testDefaultAsc", Sort.Order.ASC) , Collections.singletonMap("testAsc", Sort.Order.ASC)).assertFields(fields); @@ -100,14 +87,14 @@ public void listIndexes() throws Exception { { // Text index List tIndexes = db.listIndexes().textIndexes(); - assertEquals("There should be one text index", 1, tIndexes.size()); + assertEquals(1, tIndexes.size(), "There should be one text index"); TextIndex tIndex = tIndexes.get(0); - assertEquals("The ddoc should be correct", "_design/indexlifecycle", tIndex - .getDesignDocumentID()); - assertEquals("The name should be correct", "testtext", tIndex.getName()); - assertEquals("The type should be correct", "text", tIndex.getType()); + assertEquals("_design/indexlifecycle", tIndex + .getDesignDocumentID(), "The ddoc should be correct"); + assertEquals("testtext", tIndex.getName(), "The name should be correct"); + assertEquals("text", tIndex.getType(), "The type should be correct"); List fields = tIndex.getFields(); - assertEquals("There should be three fields", 3, fields.size()); + assertEquals(3, fields.size(), "There should be three fields"); // Field assertions new FieldAssertHelper.Text(Collections.singletonMap("testString", Type.STRING), Collections.singletonMap("testNumber", Type.NUMBER), @@ -117,23 +104,23 @@ public void listIndexes() throws Exception { { // All indexes List> allIndexes = db.listIndexes().allIndexes(); - assertEquals("There should be three total indexes", 3, allIndexes.size()); + assertEquals(3, allIndexes.size(), "There should be three total indexes"); for (Index index : allIndexes) { if (index.getType().equals("special")) { - assertEquals("The name should be correct", "_all_docs", index.getName()); - assertEquals("There should be 1 field", 1, index.getFields().size()); - assertEquals("There field should be called _id", "_id", index.getFields().get(0) - .getName()); + assertEquals("_all_docs", index.getName(), "The name should be correct"); + assertEquals(1, index.getFields().size(), "There should be 1 field"); + assertEquals("_id", index.getFields().get(0) + .getName(), "There field should be called _id"); } } } } - @After + @AfterEach public void deleteIndexes() throws Exception { db.deleteIndex("testjson", "indexlifecycle", "json"); db.deleteIndex("testtext", "indexlifecycle", "text"); List> allIndexes = db.listIndexes().allIndexes(); - assertEquals("There should be one (special) index", 1, allIndexes.size()); + assertEquals(1, allIndexes.size(), "There should be one (special) index"); } } diff --git a/cloudant-client/src/test/java/com/cloudant/api/query/IndexListTests.java b/cloudant-client/src/test/java/com/cloudant/api/query/IndexListTests.java index 8f28f2061..9502b062b 100644 --- a/cloudant-client/src/test/java/com/cloudant/api/query/IndexListTests.java +++ b/cloudant-client/src/test/java/com/cloudant/api/query/IndexListTests.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017 IBM Corp. All rights reserved. + * Copyright © 2017, 2018 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 @@ -14,8 +14,8 @@ package com.cloudant.api.query; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import com.cloudant.client.api.query.Field; import com.cloudant.client.api.query.Index; @@ -23,21 +23,20 @@ import com.cloudant.client.api.query.Sort; import com.cloudant.client.api.query.TextIndex; import com.cloudant.client.api.query.Type; -import com.cloudant.tests.util.MockedServerTest; +import com.cloudant.tests.base.TestWithMockedServer; import org.apache.commons.io.IOUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import okhttp3.mockwebserver.MockResponse; import java.io.BufferedInputStream; -import java.io.File; import java.io.FileInputStream; import java.util.Collections; import java.util.List; import java.util.Map; -public class IndexListTests extends MockedServerTest { +public class IndexListTests extends TestWithMockedServer { private static String SELECTOR_STRING = "{\"year\":{\"$gt\":2010}}"; @@ -57,12 +56,11 @@ public class IndexListTests extends MockedServerTest { * @return */ private static String fromFile(String resourceFileName) { - System.out.println(new File(".").getAbsolutePath()); try { return IOUtils.toString(new BufferedInputStream(new FileInputStream("" + "./src/test/resources/query-tests/" + resourceFileName + ".js")), "UTF-8"); } catch (Exception e) { - fail("Error reading test resource: " + e.getMessage()); + fail("Error reading test resource: " + e.getMessage()); } return null; } @@ -92,12 +90,11 @@ private void assertIndex(Index index, String name, String type, String selector) private void assertIndex(Index index, String name, String ddoc, String type, String selector) throws Exception { - assertEquals("The index should have the correct name", name, index.getName()); - assertEquals("The index should have the correct ddoc", ddoc, index - .getDesignDocumentID()); - assertEquals("The index should have the correct type", type, index.getType()); - assertEquals("The index should have the correct selector", selector, index - .getPartialFilterSelector()); + assertEquals(name, index.getName(), "The index should have the correct name"); + assertEquals(ddoc, index.getDesignDocumentID(), "The index should have the correct ddoc"); + assertEquals(type, index.getType(), "The index should have the correct type"); + assertEquals(selector, index.getPartialFilterSelector(), "The index should have the " + + "correct selector"); } private void assertJsonIndex(JsonIndex index, String name, String selector, Map... expectedFields) throws Exception { assertIndex(index, name, "text", selector); - assertEquals("The analyzer should be correct", analyzer, index.getAnalyzer()); - assertEquals("The default field should be correct", defaultField, index.getDefaultField()); + assertEquals(analyzer, index.getAnalyzer(), "The analyzer should be correct"); + assertEquals(defaultField, index.getDefaultField(), "The default field should be correct"); // Assert the fields new FieldAssertHelper.Text(expectedFields).assertFields(index.getFields()); } @@ -146,7 +143,7 @@ private void assertComplexText(TextIndex index) throws Exception { public void listSimpleJsonIndex() throws Exception { enqueueList(JSON_SIMPLE); List indexes = db.listIndexes().jsonIndexes(); - assertEquals("There should be 1 JSON index", 1, indexes.size()); + assertEquals(1, indexes.size(), "There should be 1 JSON index"); JsonIndex simple = indexes.get(0); assertSimpleJson(simple); } @@ -155,7 +152,7 @@ public void listSimpleJsonIndex() throws Exception { public void listComplexJsonIndex() throws Exception { enqueueList(JSON_COMPLEX); List indexes = db.listIndexes().jsonIndexes(); - assertEquals("There should be 1 JSON index", 1, indexes.size()); + assertEquals(1, indexes.size(), "There should be 1 JSON index"); JsonIndex complex = indexes.get(0); assertComplexJson(complex); @@ -165,7 +162,7 @@ public void listComplexJsonIndex() throws Exception { public void listMultipleJsonIndexes() throws Exception { enqueueList(JSON_SIMPLE, JSON_COMPLEX); List indexes = db.listIndexes().jsonIndexes(); - assertEquals("There should be 2 JSON indexes", 2, indexes.size()); + assertEquals(2, indexes.size(), "There should be 2 JSON indexes"); JsonIndex simple = indexes.get(0); assertSimpleJson(simple); JsonIndex complex = indexes.get(1); @@ -176,7 +173,7 @@ public void listMultipleJsonIndexes() throws Exception { public void listSimpleTextIndex() throws Exception { enqueueList(TEXT_SIMPLE); List indexes = db.listIndexes().textIndexes(); - assertEquals("There should be 1 text index", 1, indexes.size()); + assertEquals(1, indexes.size(), "There should be 1 text index"); TextIndex simple = indexes.get(0); assertSimpleText(simple); } @@ -191,7 +188,7 @@ public void listSimpleTextIndex() throws Exception { public void listSimpleTextIndexWithSelector() throws Exception { enqueueList(TEXT_SIMPLE_SELECTOR); List indexes = db.listIndexes().textIndexes(); - assertEquals("There should be 1 text index", 1, indexes.size()); + assertEquals(1, indexes.size(), "There should be 1 text index"); TextIndex simple = indexes.get(0); assertTextIndex(simple, "simpleselector", SELECTOR_STRING, "\"keyword\"", "{}", Collections.singletonMap @@ -202,7 +199,7 @@ public void listSimpleTextIndexWithSelector() throws Exception { public void listComplexTextIndex() throws Exception { enqueueList(TEXT_COMPLEX); List indexes = db.listIndexes().textIndexes(); - assertEquals("There should be 1 text index", 1, indexes.size()); + assertEquals(1, indexes.size(), "There should be 1 text index"); TextIndex complex = indexes.get(0); assertComplexText(complex); } @@ -211,7 +208,7 @@ public void listComplexTextIndex() throws Exception { public void listMultipleTextIndexes() throws Exception { enqueueList(TEXT_SIMPLE, TEXT_COMPLEX, TEXT_ALL_FIELDS); List indexes = db.listIndexes().textIndexes(); - assertEquals("There should be 3 text indexes", 3, indexes.size()); + assertEquals(3, indexes.size(), "There should be 3 text indexes"); TextIndex simple = indexes.get(0); assertSimpleText(simple); TextIndex complex = indexes.get(1); @@ -226,7 +223,7 @@ public void listAllIndexes() throws Exception { enqueueList(JSON_SIMPLE, JSON_COMPLEX, TEXT_SIMPLE, TEXT_COMPLEX, TEXT_ALL_FIELDS); List> indexes = db.listIndexes().allIndexes(); // Note 5 listed here, plus the special index that is always included - assertEquals("There should be 6 indexes", 6, indexes.size()); + assertEquals(6, indexes.size(), "There should be 6 indexes"); for (int i = 0; i < indexes.size(); i++) { String name; String type; @@ -235,9 +232,9 @@ public void listAllIndexes() throws Exception { case 0: Index a = indexes.get(i); assertIndex(a, "_all_docs", null, "special", null); - assertEquals("There should be 1 field", 1, a.getFields().size()); - assertEquals("There field should be called _id", "_id", a.getFields().get(0) - .getName()); + assertEquals(1, a.getFields().size(), "There should be 1 field"); + assertEquals("_id", a.getFields().get(0).getName(), "There field should be " + + "called _id"); return; case 1: name = "simplejson"; diff --git a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudant.java b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudant.java index a750a3471..36a24e962 100644 --- a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudant.java +++ b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,8 +14,19 @@ package com.cloudant.test.main; +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** - * JUnit category to label tests which require Cloudant Service or Cloudant Local + * JUnit tag to label tests which require Cloudant Service or Cloudant Local */ -public class RequiresCloudant extends RequiresDB { +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Tag("RequiresCloudant") +@Tag("RequiresDB") +public @interface RequiresCloudant { } diff --git a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantLocal.java b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantLocal.java index 5a55614d7..910435d9b 100644 --- a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantLocal.java +++ b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantLocal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,8 +14,20 @@ package com.cloudant.test.main; +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** - * JUnit category to label tests which require Cloudant local + * JUnit tag to label tests which require Cloudant local */ -public class RequiresCloudantLocal extends RequiresCloudant { +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Tag("RequiresCloudantLocal") +@Tag("RequiresCloudant") +@Tag("RequiresDB") +public @interface RequiresCloudantLocal { } diff --git a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantService.java b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantService.java index b62243347..52bb5985d 100644 --- a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantService.java +++ b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCloudantService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,8 +14,20 @@ package com.cloudant.test.main; +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** - * JUnit category to label tests which require the Cloudant service + * JUnit tag to label tests which require the Cloudant service */ -public class RequiresCloudantService extends RequiresCloudant { +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Tag("RequiresCloudantService") +@Tag("RequiresCloudant") +@Tag("RequiresDB") +public @interface RequiresCloudantService { } diff --git a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCouch.java b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCouch.java index b36edcec4..c19a3fec6 100644 --- a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCouch.java +++ b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresCouch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,8 +14,19 @@ package com.cloudant.test.main; +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** - * JUnit category to label tests which require a running DB + * JUnit tag to label tests which require a running DB */ -public class RequiresCouch extends RequiresDB { +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Tag("RequiresCouch") +@Tag("RequiresDB") +public @interface RequiresCouch { } diff --git a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresDB.java b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresDB.java index b706bd0df..7d45bb389 100644 --- a/cloudant-client/src/test/java/com/cloudant/test/main/RequiresDB.java +++ b/cloudant-client/src/test/java/com/cloudant/test/main/RequiresDB.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,8 +14,18 @@ package com.cloudant.test.main; +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** - * JUnit category to label tests which require a running DB + * JUnit tag to label tests which require a running DB */ -public class RequiresDB { +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Tag("RequiresDB") +public @interface RequiresDB { } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/AttachmentsTest.java b/cloudant-client/src/test/java/com/cloudant/tests/AttachmentsTest.java index 0207ebac1..637093e0b 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/AttachmentsTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/AttachmentsTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,32 +14,25 @@ */ package com.cloudant.tests; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -import com.cloudant.client.api.Database; import com.cloudant.client.api.model.Attachment; import com.cloudant.client.api.model.Document; import com.cloudant.client.api.model.Params; import com.cloudant.client.api.model.Response; -import com.cloudant.client.internal.DatabaseURIHelper; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; import com.cloudant.tests.util.Utils; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -47,21 +40,8 @@ import java.io.InputStream; import java.net.URISyntaxException; -@Category(RequiresDB.class) -public class AttachmentsTest { - - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - private static Database db; - - @BeforeClass - public static void setUp() { - db = dbResource.get(); - } - +@RequiresDB +public class AttachmentsTest extends TestWithDbPerClass { @Test public void attachmentInline() { @@ -177,9 +157,9 @@ public void addNewAttachmentToExistingDocument() throws Exception { Response attResponse = db.saveAttachment(bytesIn, "foo.txt", "text/plain", response.getId (), response.getRev()); - assertEquals("The document ID should be the same", response.getId(), attResponse.getId()); - assertTrue("The response code should be a 20x", attResponse.getStatusCode() / 100 == 2); - assertNull("There should be no error saving the attachment", attResponse.getError()); + assertEquals(response.getId(), attResponse.getId(), "The document ID should be the same"); + assertTrue(attResponse.getStatusCode() / 100 == 2, "The response code should be a 20x"); + assertNull(attResponse.getError(), "There should be no error saving the attachment"); // Assert the attachment is correct Document doc = db.find(Document.class, response.getId(), attResponse.getRev()); @@ -227,7 +207,7 @@ public void attachmentStandaloneGivenId() throws IOException, URISyntaxException // Save the attachment to a doc with the given ID Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", docId, null); - assertEquals("The saved document ID should match", docId, response.getId()); + assertEquals(docId, response.getId(), "The saved document ID should match"); InputStream in = db.getAttachment(docId, "foo.txt"); @@ -241,26 +221,43 @@ public void attachmentStandaloneGivenId() throws IOException, URISyntaxException } } - @Test(expected = IllegalArgumentException.class) + @Test public void attachmentStandaloneNullDocNonNullRev() { - byte[] bytesToDB = "binary data".getBytes(); - ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesToDB); - Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", null, "1-abcdef"); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + byte[] bytesToDB = "binary data".getBytes(); + ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesToDB); + Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", null, + "1-abcdef"); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void attachmentStandaloneEmptyDocId() { - byte[] bytesToDB = "binary data".getBytes(); - ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesToDB); - Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", "", "1-abcdef"); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + byte[] bytesToDB = "binary data".getBytes(); + ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesToDB); + Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", "", + "1-abcdef"); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void attachmentStandaloneDocIdEmptyRev() { - String docId = Utils.generateUUID(); - byte[] bytesToDB = "binary data".getBytes(); - ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesToDB); - Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", docId, ""); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + String docId = Utils.generateUUID(); + byte[] bytesToDB = "binary data".getBytes(); + ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesToDB); + Response response = db.saveAttachment(bytesIn, "foo.txt", "text/plain", docId, ""); + } + }); } @Test @@ -284,23 +281,38 @@ public void removeAttachment() { assertNull(bar3.getAttachments()); } - @Test(expected = IllegalArgumentException.class) + @Test public void removeAttachmentNullIdNonNullRev() { - String attachmentName = "txt_1.txt"; - Response response = db.removeAttachment(null, "1-abcdef", attachmentName); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + String attachmentName = "txt_1.txt"; + Response response = db.removeAttachment(null, "1-abcdef", attachmentName); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void removeAttachmentNonNullIdNullRev() { - String docId = Utils.generateUUID(); - String attachmentName = "txt_1.txt"; - Response response = db.removeAttachment(docId, null, attachmentName); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + String docId = Utils.generateUUID(); + String attachmentName = "txt_1.txt"; + Response response = db.removeAttachment(docId, null, attachmentName); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void removeAttachmentNonNullIdNonNullRevNullAttachmentName() { - String docId = Utils.generateUUID(); - String rev = "1-abcdef"; - Response response = db.removeAttachment(docId, rev, null); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + String docId = Utils.generateUUID(); + String rev = "1-abcdef"; + Response response = db.removeAttachment(docId, rev, null); + } + }); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/BulkDocumentTest.java b/cloudant-client/src/test/java/com/cloudant/tests/BulkDocumentTest.java index d704f8646..6fd1ac28f 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/BulkDocumentTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/BulkDocumentTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -15,39 +15,20 @@ package com.cloudant.tests; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; -import com.cloudant.client.api.Database; import com.cloudant.client.api.model.Response; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; import com.google.gson.JsonObject; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -@Category(RequiresDB.class) -public class BulkDocumentTest { - - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - - private static Database db; - - @BeforeClass - public static void setUp() { - db = dbResource.get(); - } +@RequiresDB +public class BulkDocumentTest extends TestWithDbPerClass { @Test public void bulkModifyDocs() { diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ChangeNotificationsTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ChangeNotificationsTest.java index d05687ea1..60338d9dd 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ChangeNotificationsTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ChangeNotificationsTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -15,10 +15,10 @@ package com.cloudant.tests; import static org.hamcrest.CoreMatchers.is; -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 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.Changes; import com.cloudant.client.api.CloudantClient; @@ -28,15 +28,13 @@ import com.cloudant.client.api.model.DbInfo; import com.cloudant.client.api.model.Response; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; +import com.cloudant.tests.extensions.MockWebServerExtension; import com.google.gson.JsonObject; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -45,22 +43,16 @@ import java.util.List; import java.util.concurrent.TimeUnit; -@Category(RequiresDB.class) -public class ChangeNotificationsTest { +@RequiresDB +public class ChangeNotificationsTest extends TestWithDbPerClass { - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); - @ClassRule - public static MockWebServer mockWebServer = new MockWebServer(); + @RegisterExtension + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + private static MockWebServer mockWebServer; - @Rule - public DatabaseResource dbResource = new DatabaseResource(clientResource); - - private Database db; - - @Before + @BeforeEach public void setup() { - db = dbResource.get(); + mockWebServer = mockWebServerExt.get(); } @Test @@ -129,9 +121,10 @@ public void changesDescending() throws Exception { mockWebServer.enqueue(new MockResponse().setBody("{\"results\": []}")); db.changes().descending(true).getChanges(); RecordedRequest request = mockWebServer.takeRequest(1, TimeUnit.SECONDS); - assertNotNull("There should be a changes request", request); - assertTrue("There should be a descending parameter on the request", request.getPath() - .contains("descending=true")); + assertNotNull(request, "There should be a changes request"); + assertTrue(request.getPath() + .contains("descending=true"), "There should be a descending parameter on the " + + "request"); } /** @@ -146,11 +139,12 @@ public void changesCustomParameter() throws Exception { mockWebServer.enqueue(new MockResponse().setBody("{\"results\": []}")); db.changes().filter("myFilter").parameter("myParam", "paramValue").getChanges(); RecordedRequest request = mockWebServer.takeRequest(1, TimeUnit.SECONDS); - assertNotNull("There should be a changes request", request); - assertTrue("There should be a filter parameter on the request", request.getPath() - .contains("filter=myFilter")); - assertTrue("There should be a custom parameter on the request", request.getPath() - .contains("myParam=paramValue")); + assertNotNull(request, "There should be a changes request"); + assertTrue(request.getPath() + .contains("filter=myFilter"), "There should be a filter parameter on the request"); + assertTrue(request.getPath() + .contains("myParam=paramValue"), "There should be a custom parameter on the " + + "request"); } /** @@ -184,13 +178,13 @@ public void changesFeedLastSeq() throws Exception { Changes c = db.changes().continuousChanges(); int nChanges = 0; // check against regression where hasNext() will hang - while(c.hasNext()) { + while (c.hasNext()) { nChanges++; c.next(); } RecordedRequest request = mockWebServer.takeRequest(1, TimeUnit.SECONDS); - assertNotNull("There should be a changes request", request); - assertEquals("There should be 14 changes", 14, nChanges); + assertNotNull(request, "There should be a changes request"); + assertEquals(14, nChanges, "There should be 14 changes"); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ClientLoadTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ClientLoadTest.java index 2b03e9975..b3a4de5b1 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ClientLoadTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ClientLoadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -16,21 +16,19 @@ import com.cloudant.client.api.CloudantClient; import com.cloudant.client.api.Database; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; -import com.cloudant.tests.util.TestLog; +import com.cloudant.tests.extensions.CloudantClientExtension; +import com.cloudant.tests.extensions.DatabaseExtension; -import org.junit.After; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -@Ignore +@Disabled public class ClientLoadTest { /** @@ -38,21 +36,18 @@ public class ClientLoadTest { */ private static final int MAX_CONNECTIONS = 20; - @ClassRule - public static final TestLog log = new TestLog(); - - public static CloudantClientResource clientResource = new CloudantClientResource - (CloudantClientHelper.getClientBuilder() - .maxConnections(MAX_CONNECTIONS)); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); + @RegisterExtension + public static CloudantClientExtension clientResource = new CloudantClientExtension( + CloudantClientHelper.getClientBuilder().maxConnections(MAX_CONNECTIONS)); + @RegisterExtension + public static DatabaseExtension.PerTest dbResource = new DatabaseExtension.PerTest + (clientResource); private static CloudantClient dbClient; private static Database db; - @BeforeClass - public static void setUp() { + @BeforeEach + public void setUp() { dbClient = clientResource.get(); db = dbResource.get(); } @@ -61,7 +56,7 @@ public static void setUp() { private static final int DOCS_PER_THREAD = 10; - @After + @AfterEach public void tearDown() { dbClient.shutdown(); } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/CloudFoundryServiceTest.java b/cloudant-client/src/test/java/com/cloudant/tests/CloudFoundryServiceTest.java index c1969ca54..857c0ff6c 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/CloudFoundryServiceTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/CloudFoundryServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -17,7 +17,9 @@ import com.cloudant.client.api.ClientBuilder; import com.google.gson.GsonBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import java.util.ArrayList; import java.util.HashMap; @@ -87,25 +89,37 @@ public void vcapValidServiceNameSpecified() { vcap.createNewService("test_bluemix_service_1", CloudantClientHelper.SERVER_URI_WITH_USER_INFO, CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD); - ClientBuilder.bluemix(vcap.toJson(), serviceName, "test_bluemix_service_1").build().serverVersion(); + ClientBuilder.bluemix(vcap.toJson(), serviceName, "test_bluemix_service_1").build() + .serverVersion(); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapMissingServiceNameSpecified() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1", - CloudantClientHelper.SERVER_URI_WITH_USER_INFO, - CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD); - ClientBuilder.bluemix(vcap.toJson(), "missingService", "test_bluemix_service_1").build(); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewService("test_bluemix_service_1", + CloudantClientHelper.SERVER_URI_WITH_USER_INFO, + CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD); + ClientBuilder.bluemix(vcap.toJson(), "missingService", "test_bluemix_service_1") + .build(); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapNullServiceNameSpecified() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1", - CloudantClientHelper.SERVER_URI_WITH_USER_INFO, - CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD); - ClientBuilder.bluemix(vcap.toJson(), null, "test_bluemix_service_1").build(); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewService("test_bluemix_service_1", + CloudantClientHelper.SERVER_URI_WITH_USER_INFO, + CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD); + ClientBuilder.bluemix(vcap.toJson(), null, "test_bluemix_service_1").build(); + } + }); } @Test @@ -126,70 +140,119 @@ public void vcapSingleServiceNoNameSpecified() { ClientBuilder.bluemix(vcap.toJson()).build().serverVersion(); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapSingleServiceMissingNamedService() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1","http://foo1.bar", "admin1", "pass1"); - ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_2"); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewService("test_bluemix_service_1", "http://foo1.bar", "admin1", + "pass1"); + ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_2"); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapSingleServiceEmptyCredentials() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewServiceWithEmptyCredentials("test_bluemix_service_1"); - ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_1"); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewServiceWithEmptyCredentials("test_bluemix_service_1"); + ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_1"); + } + }); } @Test public void vcapMultiService() { VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1","http://foo1.bar", "admin1", "pass1"); - vcap.createNewService("test_bluemix_service_2","http://foo2.bar", "admin2", "pass2"); + vcap.createNewService("test_bluemix_service_1", "http://foo1.bar", "admin1", "pass1"); + vcap.createNewService("test_bluemix_service_2", "http://foo2.bar", "admin2", "pass2"); vcap.createNewService("test_bluemix_service_3", CloudantClientHelper.SERVER_URI_WITH_USER_INFO, CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD); ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_3").build().serverVersion(); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapMultiServiceNoNameSpecified() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1","http://foo1.bar", "admin1", "pass1"); - vcap.createNewService("test_bluemix_service_2","http://foo2.bar", "admin2", "pass2"); - vcap.createNewService("test_bluemix_service_3","http://foo3.bar", "admin3", "pass3"); - ClientBuilder.bluemix(vcap.toJson()); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewService("test_bluemix_service_1", "http://foo1.bar", "admin1", + "pass1"); + vcap.createNewService("test_bluemix_service_2", "http://foo2.bar", "admin2", + "pass2"); + vcap.createNewService("test_bluemix_service_3", "http://foo3.bar", "admin3", + "pass3"); + ClientBuilder.bluemix(vcap.toJson()); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapMultiServiceMissingNamedService() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1","http://foo1.bar", "admin1", "pass1"); - vcap.createNewService("test_bluemix_service_2","http://foo2.bar", "admin2", "pass2"); - vcap.createNewService("test_bluemix_service_3","http://foo3.bar", "admin3", "pass3"); - ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_4"); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewService("test_bluemix_service_1", "http://foo1.bar", "admin1", + "pass1"); + vcap.createNewService("test_bluemix_service_2", "http://foo2.bar", "admin2", + "pass2"); + vcap.createNewService("test_bluemix_service_3", "http://foo3.bar", "admin3", + "pass3"); + ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_4"); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapMultiServiceEmptyCredentials() { - VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); - vcap.createNewService("test_bluemix_service_1","http://foo1.bar", "admin1", "pass1"); - vcap.createNewService("test_bluemix_service_2","http://foo2.bar", "admin2", "pass2"); - vcap.createNewServiceWithEmptyCredentials("test_bluemix_service_3"); - ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_3"); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + VCAPGenerator vcap = new CloudFoundryServiceTest.VCAPGenerator(); + vcap.createNewService("test_bluemix_service_1", "http://foo1.bar", "admin1", + "pass1"); + vcap.createNewService("test_bluemix_service_2", "http://foo2.bar", "admin2", + "pass2"); + vcap.createNewServiceWithEmptyCredentials("test_bluemix_service_3"); + ClientBuilder.bluemix(vcap.toJson(), "test_bluemix_service_3"); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapNoServicesPresent() { - ClientBuilder.bluemix(new CloudFoundryServiceTest.VCAPGenerator().toJson()); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + ClientBuilder.bluemix(new CloudFoundryServiceTest.VCAPGenerator().toJson()); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapInvalidJSON() { - ClientBuilder.bluemix("{\"cloudantNoSQLDB\":[]"); // invalid JSON + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + ClientBuilder.bluemix("{\"cloudantNoSQLDB\":[]"); // invalid JSON + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void vcapNotPresent() { - ClientBuilder.bluemix(null); + Assertions.assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + ClientBuilder.bluemix(null); + } + }); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientHelper.java b/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientHelper.java index 77912c75b..08b06df89 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientHelper.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientHelper.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -16,6 +16,7 @@ import com.cloudant.client.api.ClientBuilder; import com.cloudant.client.api.CloudantClient; + import okhttp3.mockwebserver.MockWebServer; import java.net.MalformedURLException; diff --git a/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientTests.java b/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientTests.java index ddad4e403..0c959fe67 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientTests.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/CloudantClientTests.java @@ -1,534 +1,574 @@ -/* - * Copyright © 2015, 2017 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; - -import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.createPost; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.cloudant.client.api.ClientBuilder; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; -import com.cloudant.client.api.model.ApiKey; -import com.cloudant.client.api.model.Membership; -import com.cloudant.client.api.model.Task; -import com.cloudant.client.org.lightcouch.CouchDbException; -import com.cloudant.client.org.lightcouch.NoDocumentException; -import com.cloudant.client.org.lightcouch.PreconditionFailedException; -import com.cloudant.http.Http; -import com.cloudant.http.HttpConnection; -import com.cloudant.http.interceptors.BasicAuthInterceptor; -import com.cloudant.http.internal.interceptors.UserAgentInterceptor; -import com.cloudant.test.main.RequiresCloudant; -import com.cloudant.test.main.RequiresCloudantService; -import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.MockWebServerResources; -import com.cloudant.tests.util.TestLog; -import com.cloudant.tests.util.Utils; - -import org.apache.commons.io.IOUtils; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import okhttp3.mockwebserver.SocketPolicy; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLEncoder; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.net.ServerSocketFactory; - -/** - * Note some tests in this class use Java 1.7 features - */ -public class CloudantClientTests { - - @ClassRule - public static final TestLog TEST_LOG = new TestLog(); - - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); - private CloudantClient account = clientResource.get(); - - @Rule - public MockWebServer server = new MockWebServer(); - - @Test - @Category(RequiresCloudantService.class) - public void apiKey() { - ApiKey key = account.generateApiKey(); - assertNotNull(key); - assertNotNull(key.getKey()); - assertNotNull(key.getPassword()); - } - - @Test - @Category(RequiresCloudant.class) - public void activeTasks() { - List tasks = account.getActiveTasks(); - assertNotNull(tasks); - } - - @Test - @Category(RequiresCloudant.class) - public void membership() { - Membership mship = account.getMembership(); - assertNotNull(mship); - assertNotNull(mship.getClusterNodes()); - assertNotNull(mship.getClusterNodes().hasNext()); - assertNotNull(mship.getAllNodes()); - assertNotNull(mship.getAllNodes().hasNext()); - } - - @Test - @Category(RequiresCloudant.class) - public void cookieTest() { - - Membership membership = account.getMembership(); - assertNotNull(membership); - } - - // java-cloudant/n.n.n or java-cloudant/unknown followed by 4 groups of /anything - private final String userAgentRegex = "java-cloudant/(?:(?:\\d+.\\d+.\\d+))" + - "(?:/{1}[^/]+){4}"; - - private final String userAgentUnknownRegex = "cloudant-http/(?:(?:unknown))" + - "(?:/{1}[^/]+){4}"; - - - private final String userAgentFormat = "java-cloudant/version/jvm.version/jvm.vendor/os" + - ".name/os.arch"; - - /** - * Assert that the User-Agent header is of the expected form. - */ - @Test - public void testUserAgentHeaderString() throws Exception { - - // This doesn't read the a properties file, since the tests do not run from the published - // jars. - String userAgentHeader = new UserAgentInterceptor(UserAgentInterceptor.class - .getClassLoader(), - "META-INF/com.cloudant.client.properties").getUserAgent(); - assertTrue("The value of the User-Agent header: " + userAgentHeader + " should match the " + - "format: " + userAgentFormat, - userAgentHeader.matches(userAgentUnknownRegex)); - } - - @Test - public void testUserAgentHeaderStringFromFile() throws Exception { - // This doesn't read the a properties file, since the tests do not run from the published - // jars. - // Point to the built classes, it's a bit awkward but we need to load the class cleanly - File f = new File("../cloudant-http/build/classes/main/"); - String userAgentHeader = new UserAgentInterceptor(new URLClassLoader(new URL[]{f.toURI() - .toURL()}) { - - @Override - public InputStream getResourceAsStream(String name) { - if (name.equals("META-INF/com.cloudant.client.properties")) { - try { - return new ByteArrayInputStream(("user.agent.name=java-cloudant\nuser" + - ".agent.version=1.6.1").getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - return super.getResourceAsStream(name); - } - }, "META-INF/com.cloudant.client.properties").getUserAgent(); - assertTrue("The value of the User-Agent header: " + userAgentHeader + " should match the " + - "format: " + userAgentFormat, - userAgentHeader.matches(userAgentRegex)); - } - - /** - * Assert that requests have the User-Agent header added. This test runs a local HTTP server - * process that can handle a single request to receive the request and validate the header. - */ - @Test - public void testUserAgentHeaderIsAddedToRequest() throws Exception { - - //send back an OK 200 - server.enqueue(new MockResponse()); - - //instantiating the client performs a single post request - CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(server) - .build(); - String response = client.executeRequest(createPost(client.getBaseUri(), null, - "application/json")).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); - - //assert that the request had the expected header - String userAgentHeader = server.takeRequest(10, TimeUnit.SECONDS) - .getHeader("User-Agent"); - assertNotNull("The User-Agent header should be present on the request", - userAgentHeader); - assertTrue("The value of the User-Agent header " + userAgentHeader + " on the request" + - " should match the format " + userAgentFormat, - userAgentHeader.matches(userAgentUnknownRegex)); - } - - /** - * Test a NoDocumentException is thrown when trying an operation on a DB that doesn't exist - */ - @Test(expected = NoDocumentException.class) - @Category(RequiresDB.class) - public void nonExistentDatabaseException() { - //try and get a DB that doesn't exist - Database db = account.database("not_really_there", false); - //try an operation against the non-existant DB - db.info(); - } - - /** - * Validate that no exception bubbles up when trying to create a DB that already exists - */ - @Test - @Category(RequiresDB.class) - public void existingDatabaseCreateException() { - String id = Utils.generateUUID(); - String dbName = "existing" + id; - try { - //create a DB for this test - account.createDB(dbName); - - // Get a database instance using create true for the already existing DB - account.database(dbName, true); - } finally { - //clean up the DB created by this test - account.deleteDB(dbName); - } - } - - /** - * Validate that a PreconditionFailedException is thrown when using the createDB method to - * create a database that already exists. - */ - @Test(expected = PreconditionFailedException.class) - @Category(RequiresDB.class) - public void existingDatabaseCreateDBException() { - String id = Utils.generateUUID(); - String dbName = "existing" + id; - try { - //create a DB for this test - account.createDB(dbName); - - //do a get with create true for the already existing DB - account.createDB(dbName); - - } finally { - //clean up the DB created by this test - account.deleteDB(dbName); - } - } - - @Test - public void testDefaultPorts() throws Exception { - CloudantClient c = null; - - c = CloudantClientHelper.newTestAddressClient().build(); - - assertEquals("The http port should be 80", 80, c.getBaseUri().getPort()); - - - c = CloudantClientHelper.newHttpsTestAddressClient().build(); - - assertEquals("The http port should be 443", 443, c.getBaseUri().getPort()); - } - - /** - * Check that the connection timeout throws a SocketTimeoutException when it can't connect - * within the timeout. - */ - @Test(expected = SocketTimeoutException.class) - public void connectionTimeout() throws Throwable { - // Do this test on the loopback - InetAddress loopback = InetAddress.getLoopbackAddress(); - ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(0, 1, - loopback); - - int port = serverSocket.getLocalPort(); - //block the single connection to our server - Socket socket = new Socket(loopback.getHostAddress(), port); - - //now try to connect, but should timeout because there is no connection available - try { - CloudantClient c = ClientBuilder.url(new URL("http", loopback.getHostAddress(), port, - "")).connectTimeout(100, TimeUnit.MILLISECONDS) - // Unfortunately openjdk doesn't honour the connect timeout so we set the read - // timeout as well so that the test doesn't take too long on that platform - .readTimeout(250, TimeUnit.MILLISECONDS) - .build(); - - // Make a request - c.getAllDbs(); - } catch (CouchDbException e) { - //unwrap the CouchDbException - if (e.getCause() != null) { - //whilst it would be really nice to actually assert that this was a connect - //exception and not some other SocketTimeoutException there are JVM differences in - //this respect (i.e. OpenJDK does not appear to distinguish between read/connect) - //in its exception messages - throw e.getCause(); - } else { - throw e; - } - } finally { - //make sure we close the sockets - IOUtils.closeQuietly(socket); - IOUtils.closeQuietly(serverSocket); - } - } - - /** - * Checks that the read timeout works. The test sets a read timeout of 0.25 s and the mock - * server thread never sends a response. If things are working - * correctly then the client should see a SocketTimeoutException for the read. - */ - @Test(expected = SocketTimeoutException.class) - public void readTimeout() throws Throwable { - // Don't respond so the read will timeout - server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE)); - - try { - CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(server) - .readTimeout(25, TimeUnit.MILLISECONDS).build(); - - //do a call that expects a response - c.getAllDbs(); - } catch (CouchDbException e) { - //unwrap the CouchDbException - if (e.getCause() != null) { - throw e.getCause(); - } else { - throw e; - } - } - } - - /** - * This tests that a CouchDbException is thrown if the user is null, but the password is - * supplied. - */ - @Test(expected = CouchDbException.class) - public void nullUser() throws Exception { - CloudantClientHelper.newTestAddressClient() - .password(":0-myPassword") - .build(); - - } - - /** - * This tests that a CouchDbException is thrown if the user is supplied, but the password is - * null. - */ - @Test(expected = CouchDbException.class) - public void nullPassword() throws Exception { - CloudantClientHelper.newTestAddressClient() - .username("user") - .build(); - } - - /** - * Test that user info provided in a url is correctly removed and made into user name and - * password fields. - */ - @Test - public void testUserInfoInUrl() throws Exception { - urlCheck("user", "password"); - } - - // A String of all the URI reserved and "unsafe" characters, plus © and an emoji. Encodes to: - // %21*%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D+%22%25 - // -.%3C%3E%5C%5E_%60%7B%7C%7D%7E%C2%A9%F0%9F%94%92 - private static String SPECIALS = "!*'();:@&=+$,/?#[] \"%-.<>\\^_`{|}~©\uD83D\uDD12"; - - /** - * Test that user info provided in a url is correctly removed and made into user name and - * password fields when the user info includes URL encoded characters. - */ - @Test - public void testUserInfoInUrlWithSpecials() throws Exception { - String user = URLEncoder.encode("user" + SPECIALS, "UTF-8"); - String pw = URLEncoder.encode("password" + SPECIALS, "UTF-8"); - urlCheck(user, pw); - } - - /** - * Test that user info provided via the username and password methods including URL special - * characters is encoded correctly. - */ - @Test - public void testUserInfoWithSpecials() throws Exception { - String user = "user" + SPECIALS; - String pw = "password" + SPECIALS; - credentialsCheck(CloudantClientHelper.newMockWebServerClientBuilder(server).username - (user).password(pw), URLEncoder.encode(user, "UTF-8"), URLEncoder.encode(pw, - "UTF-8")); - } - - private static Pattern CREDENTIALS = Pattern.compile("name=([^&]+)&password=(.+)"); - - private void urlCheck(String encodedUser, String encodedPassword) throws Exception { - ClientBuilder b = ClientBuilder.url(server.url("").newBuilder().encodedUsername - (encodedUser).encodedPassword(encodedPassword).build().url()); - credentialsCheck(b, encodedUser, encodedPassword); - } - - private void credentialsCheck(ClientBuilder b, String encodedUser, String encodedPassword) - throws Exception { - CloudantClient c = b.build(); - - server.enqueue(MockWebServerResources.OK_COOKIE); - server.enqueue(MockWebServerResources.JSON_OK); - - HttpConnection conn = c.executeRequest(Http.GET(c.getBaseUri())); - // Consume response stream and assert ok: true - String responseStr = conn.responseAsString(); - assertNotNull(responseStr); - - // One request to _session then one to get info - assertEquals("There should be two requests", 2, server.getRequestCount()); - - // Get the _session request - RecordedRequest request = server.takeRequest(); - String body = request.getBody().readUtf8(); - // body should be of form: - // name=YourUserName&password=YourPassword - Matcher m = CREDENTIALS.matcher(body); - assertTrue("The _session request should match the regex", m.matches()); - assertEquals("There should be a username group and a password group in the creds", 2, m - .groupCount()); - assertEquals("The username should match", encodedUser, m.group(1)); - assertEquals("The password should match", encodedPassword, m.group(2)); - - //ensure that building a URL from it does not throw any exceptions - new URL(c.getBaseUri().toString()); - } - - @Test - public void sessionDeleteOnShutdown() throws Exception { - // Mock a 200 OK for the _session DELETE request - server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"ok\":\"true\"}")); - - CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(server).build(); - c.shutdown(); - - RecordedRequest request = server.takeRequest(10, TimeUnit.SECONDS); - assertEquals("The request method should be DELETE", "DELETE", request.getMethod()); - assertEquals("The request should be to the _session path", "/_session", request.getPath()); - } - - /** - * Test that adding the Basic Authentication interceptor to CloudantClient works. - */ - @Test - @Category(RequiresCloudant.class) - public void testBasicAuth() throws IOException { - BasicAuthInterceptor interceptor = - new BasicAuthInterceptor(CloudantClientHelper.COUCH_USERNAME - + ":" + CloudantClientHelper.COUCH_PASSWORD); - - CloudantClient client = ClientBuilder.account(CloudantClientHelper.COUCH_USERNAME) - .interceptors(interceptor).build(); - - // Test passes if there are no exceptions - client.getAllDbs(); - } - - /** - * Test that configuring the Basic Authentication interceptor from credentials and adding to - * the CloudantClient works. - */ - @Test - public void testBasicAuthFromCredentials() throws Exception { - BasicAuthInterceptor interceptor = - BasicAuthInterceptor.createFromCredentials("username", "password"); - - // send back a mock OK 200 - server.enqueue(new MockResponse()); - - CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(server) - .interceptors(interceptor).build(); - - client.getAllDbs(); - - // expected 'username:password' - assertEquals("Basic dXNlcm5hbWU6cGFzc3dvcmQ=", server.takeRequest().getHeader("Authorization")); - } - - @Test - public void gatewayStyleURL() throws Exception { - - final String gatewayPath = "/gateway"; - - // Set a dispatcher that returns 200 if the requests have the correct path /gateway/_all_dbs - // Otherwise return 400. - server.setDispatcher(new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) throws InterruptedException { - if (request.getPath().equals(gatewayPath + "/_all_dbs")) { - return new MockResponse(); - } else { - return new MockResponse().setResponseCode(400); - } - } - }); - - // Build a client with a URL that includes a path - CloudantClient c = ClientBuilder.url(new URL(server.url(gatewayPath).toString())).build(); - // If the request path is wrong this call will return 400 and throw an exception failing the - // test. - c.getAllDbs(); - - // Build a client with a URL that includes a path with a trailing / - c = ClientBuilder.url(new URL(server.url(gatewayPath + "/").toString())).build(); - // If the request path is wrong this call will return 400 and throw an exception failing the - // test. - c.getAllDbs(); - } - - /** - * Assert that a {@code null} URL causes an IllegalArgumentException to be thrown. - * - * @throws Exception - */ - @Test(expected = IllegalArgumentException.class) - public void nullURLThrowsIAE() throws Exception { - ClientBuilder.url(null); - } -} +/* + * Copyright © 2015, 2018 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; + +import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.createPost; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.cloudant.client.api.ClientBuilder; +import com.cloudant.client.api.CloudantClient; +import com.cloudant.client.api.Database; +import com.cloudant.client.api.model.ApiKey; +import com.cloudant.client.api.model.Membership; +import com.cloudant.client.api.model.Task; +import com.cloudant.client.org.lightcouch.CouchDbException; +import com.cloudant.client.org.lightcouch.NoDocumentException; +import com.cloudant.client.org.lightcouch.PreconditionFailedException; +import com.cloudant.http.Http; +import com.cloudant.http.HttpConnection; +import com.cloudant.http.interceptors.BasicAuthInterceptor; +import com.cloudant.http.internal.interceptors.UserAgentInterceptor; +import com.cloudant.test.main.RequiresCloudant; +import com.cloudant.test.main.RequiresCloudantService; +import com.cloudant.test.main.RequiresDB; +import com.cloudant.tests.base.TestWithDbPerClass; +import com.cloudant.tests.extensions.MockWebServerExtension; +import com.cloudant.tests.util.MockWebServerResources; +import com.cloudant.tests.util.Utils; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.function.Executable; + +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import okhttp3.mockwebserver.SocketPolicy; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLEncoder; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ServerSocketFactory; + +/** + * Note some tests in this class use Java 1.7 features + */ +public class CloudantClientTests extends TestWithDbPerClass { + + @RegisterExtension + public MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + + public MockWebServer server; + + @BeforeEach + public void beforeEach() { + server = mockWebServerExt.get(); + } + + @Test + @RequiresCloudantService + public void apiKey() { + ApiKey key = account.generateApiKey(); + assertNotNull(key); + assertNotNull(key.getKey()); + assertNotNull(key.getPassword()); + } + + @Test + @RequiresCloudant + public void activeTasks() { + List tasks = account.getActiveTasks(); + assertNotNull(tasks); + } + + @Test + @RequiresCloudant + public void membership() { + Membership mship = account.getMembership(); + assertNotNull(mship); + assertNotNull(mship.getClusterNodes()); + assertNotNull(mship.getClusterNodes().hasNext()); + assertNotNull(mship.getAllNodes()); + assertNotNull(mship.getAllNodes().hasNext()); + } + + @Test + @RequiresCloudant + public void cookieTest() { + + Membership membership = account.getMembership(); + assertNotNull(membership); + } + + // java-cloudant/n.n.n or java-cloudant/unknown followed by 4 groups of /anything + private final String userAgentRegex = "java-cloudant/(?:(?:\\d+.\\d+.\\d+))" + + "(?:/{1}[^/]+){4}"; + + private final String userAgentUnknownRegex = "cloudant-http/(?:(?:unknown))" + + "(?:/{1}[^/]+){4}"; + + + private final String userAgentFormat = "java-cloudant/version/jvm.version/jvm.vendor/os" + + ".name/os.arch"; + + /** + * Assert that the User-Agent header is of the expected form. + */ + @Test + public void testUserAgentHeaderString() throws Exception { + + // This doesn't read the a properties file, since the tests do not run from the published + // jars. + String userAgentHeader = new UserAgentInterceptor(UserAgentInterceptor.class + .getClassLoader(), + "META-INF/com.cloudant.client.properties").getUserAgent(); + assertTrue(userAgentHeader.matches(userAgentUnknownRegex), "The value of the User-Agent " + + "header: " + userAgentHeader + " should match the " + "format: " + userAgentFormat); + } + + @Test + public void testUserAgentHeaderStringFromFile() throws Exception { + // This doesn't read the a properties file, since the tests do not run from the published + // jars. + // Point to the built classes, it's a bit awkward but we need to load the class cleanly + File f = new File("../cloudant-http/build/classes/main/"); + String userAgentHeader = new UserAgentInterceptor(new URLClassLoader(new URL[]{f.toURI() + .toURL()}) { + + @Override + public InputStream getResourceAsStream(String name) { + if (name.equals("META-INF/com.cloudant.client.properties")) { + try { + return new ByteArrayInputStream(("user.agent.name=java-cloudant\nuser" + + ".agent.version=1.6.1").getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + return super.getResourceAsStream(name); + } + }, "META-INF/com.cloudant.client.properties").getUserAgent(); + assertTrue(userAgentHeader.matches(userAgentRegex), "The value of the User-Agent header: " + + "" + userAgentHeader + " should match the " + "format: " + userAgentFormat); + } + + /** + * Assert that requests have the User-Agent header added. This test runs a local HTTP server + * process that can handle a single request to receive the request and validate the header. + */ + @Test + public void testUserAgentHeaderIsAddedToRequest() throws Exception { + + //send back an OK 200 + server.enqueue(new MockResponse()); + + //instantiating the client performs a single post request + CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(server) + .build(); + String response = client.executeRequest(createPost(client.getBaseUri(), null, + "application/json")).responseAsString(); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); + + //assert that the request had the expected header + String userAgentHeader = server.takeRequest(10, TimeUnit.SECONDS) + .getHeader("User-Agent"); + assertNotNull(userAgentHeader, "The User-Agent header should be present on the request"); + assertTrue(userAgentHeader.matches(userAgentUnknownRegex), "The value of the User-Agent " + + "header " + userAgentHeader + " on the request" + " should match the format " + + userAgentFormat); + } + + /** + * Test a NoDocumentException is thrown when trying an operation on a DB that doesn't exist + */ + @Test + @RequiresDB + public void nonExistentDatabaseException() { + assertThrows(NoDocumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + //try and get a DB that doesn't exist + Database db = account.database("not_really_there", false); + //try an operation against the non-existant DB + db.info(); + } + }); + } + + /** + * Validate that no exception bubbles up when trying to create a DB that already exists + */ + @Test + @RequiresDB + public void existingDatabaseCreateException() { + String id = Utils.generateUUID(); + String dbName = "existing" + id; + try { + //create a DB for this test + account.createDB(dbName); + + // Get a database instance using create true for the already existing DB + account.database(dbName, true); + } finally { + //clean up the DB created by this test + account.deleteDB(dbName); + } + } + + /** + * Validate that a PreconditionFailedException is thrown when using the createDB method to + * create a database that already exists. + */ + @Test + @RequiresDB + public void existingDatabaseCreateDBException() { + assertThrows(PreconditionFailedException.class, new Executable() { + @Override + public void execute() throws Throwable { + + String id = Utils.generateUUID(); + String dbName = "existing" + id; + try { + //create a DB for this test + account.createDB(dbName); + + //do a get with create true for the already existing DB + account.createDB(dbName); + + } finally { + //clean up the DB created by this test + account.deleteDB(dbName); + } + } + }); + } + + @Test + public void testDefaultPorts() throws Exception { + CloudantClient c = null; + + c = CloudantClientHelper.newTestAddressClient().build(); + + assertEquals(80, c.getBaseUri().getPort(), "The http port should be 80"); + + + c = CloudantClientHelper.newHttpsTestAddressClient().build(); + + assertEquals(443, c.getBaseUri().getPort(), "The http port should be 443"); + } + + /** + * Check that the connection timeout throws a SocketTimeoutException when it can't connect + * within the timeout. + */ + @Test + public void connectionTimeout() throws Throwable { + assertThrows(SocketTimeoutException.class, new Executable() { + @Override + public void execute() throws Throwable { + + // Do this test on the loopback + InetAddress loopback = InetAddress.getLoopbackAddress(); + ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket + (0, 1, + loopback); + + int port = serverSocket.getLocalPort(); + //block the single connection to our server + Socket socket = new Socket(loopback.getHostAddress(), port); + + //now try to connect, but should timeout because there is no connection available + try { + CloudantClient c = ClientBuilder.url(new URL("http", loopback.getHostAddress + (), port, + "")).connectTimeout(100, TimeUnit.MILLISECONDS) + // Unfortunately openjdk doesn't honour the connect timeout so we set + // the read + // timeout as well so that the test doesn't take too long on that + // platform + .readTimeout(250, TimeUnit.MILLISECONDS) + .build(); + + // Make a request + c.getAllDbs(); + } catch (CouchDbException e) { + //unwrap the CouchDbException + if (e.getCause() != null) { + //whilst it would be really nice to actually assert that this was a connect + //exception and not some other SocketTimeoutException there are JVM + // differences in + //this respect (i.e. OpenJDK does not appear to distinguish between + // read/connect) + //in its exception messages + throw e.getCause(); + } else { + throw e; + } + } finally { + //make sure we close the sockets + IOUtils.closeQuietly(socket); + IOUtils.closeQuietly(serverSocket); + } + } + }); + } + + /** + * Checks that the read timeout works. The test sets a read timeout of 0.25 s and the mock + * server thread never sends a response. If things are working + * correctly then the client should see a SocketTimeoutException for the read. + */ + @Test + public void readTimeout() throws Throwable { + assertThrows(SocketTimeoutException.class, new Executable() { + @Override + public void execute() throws Throwable { + // Don't respond so the read will timeout + server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE)); + try { + CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(server) + .readTimeout(25, TimeUnit.MILLISECONDS).build(); + + //do a call that expects a response + c.getAllDbs(); + } catch (CouchDbException e) { + //unwrap the CouchDbException + if (e.getCause() != null) { + throw e.getCause(); + } else { + throw e; + } + } + } + }); + } + + /** + * This tests that a CouchDbException is thrown if the user is null, but the password is + * supplied. + */ + @Test + public void nullUser() throws Exception { + assertThrows(CouchDbException.class, new Executable() { + @Override + public void execute() throws Throwable { + CloudantClientHelper.newTestAddressClient() + .password(":0-myPassword") + .build(); + } + }); + } + + /** + * This tests that a CouchDbException is thrown if the user is supplied, but the password is + * null. + */ + @Test + public void nullPassword() throws Exception { + assertThrows(CouchDbException.class, new Executable() { + @Override + public void execute() throws Throwable { + CloudantClientHelper.newTestAddressClient() + .username("user") + .build(); + } + }); + } + + /** + * Test that user info provided in a url is correctly removed and made into user name and + * password fields. + */ + @Test + public void testUserInfoInUrl() throws Exception { + urlCheck("user", "password"); + } + + // A String of all the URI reserved and "unsafe" characters, plus © and an emoji. Encodes to: + // %21*%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D+%22%25 + // -.%3C%3E%5C%5E_%60%7B%7C%7D%7E%C2%A9%F0%9F%94%92 + private static String SPECIALS = "!*'();:@&=+$,/?#[] \"%-.<>\\^_`{|}~©\uD83D\uDD12"; + + /** + * Test that user info provided in a url is correctly removed and made into user name and + * password fields when the user info includes URL encoded characters. + */ + @Test + public void testUserInfoInUrlWithSpecials() throws Exception { + String user = URLEncoder.encode("user" + SPECIALS, "UTF-8"); + String pw = URLEncoder.encode("password" + SPECIALS, "UTF-8"); + urlCheck(user, pw); + } + + /** + * Test that user info provided via the username and password methods including URL special + * characters is encoded correctly. + */ + @Test + public void testUserInfoWithSpecials() throws Exception { + String user = "user" + SPECIALS; + String pw = "password" + SPECIALS; + credentialsCheck(CloudantClientHelper.newMockWebServerClientBuilder(server).username + (user).password(pw), URLEncoder.encode(user, "UTF-8"), URLEncoder.encode(pw, + "UTF-8")); + } + + private static Pattern CREDENTIALS = Pattern.compile("name=([^&]+)&password=(.+)"); + + private void urlCheck(String encodedUser, String encodedPassword) throws Exception { + ClientBuilder b = ClientBuilder.url(server.url("").newBuilder().encodedUsername + (encodedUser).encodedPassword(encodedPassword).build().url()); + credentialsCheck(b, encodedUser, encodedPassword); + } + + private void credentialsCheck(ClientBuilder b, String encodedUser, String encodedPassword) + throws Exception { + CloudantClient c = b.build(); + + server.enqueue(MockWebServerResources.OK_COOKIE); + server.enqueue(MockWebServerResources.JSON_OK); + + HttpConnection conn = c.executeRequest(Http.GET(c.getBaseUri())); + // Consume response stream and assert ok: true + String responseStr = conn.responseAsString(); + assertNotNull(responseStr); + + // One request to _session then one to get info + assertEquals(2, server.getRequestCount(), "There should be two requests"); + + // Get the _session request + RecordedRequest request = server.takeRequest(); + String body = request.getBody().readUtf8(); + // body should be of form: + // name=YourUserName&password=YourPassword + Matcher m = CREDENTIALS.matcher(body); + assertTrue(m.matches(), "The _session request should match the regex"); + assertEquals(2, m.groupCount(), "There should be a username group and a password group in" + + " the creds"); + assertEquals(encodedUser, m.group(1), "The username should match"); + assertEquals(encodedPassword, m.group(2), "The password should match"); + + //ensure that building a URL from it does not throw any exceptions + new URL(c.getBaseUri().toString()); + } + + @Test + public void sessionDeleteOnShutdown() throws Exception { + // Mock a 200 OK for the _session DELETE request + server.enqueue(new MockResponse().setResponseCode(200).setBody("{\"ok\":\"true\"}")); + + CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(server).build(); + c.shutdown(); + + RecordedRequest request = server.takeRequest(10, TimeUnit.SECONDS); + assertEquals("DELETE", request.getMethod(), "The request method should be DELETE"); + assertEquals("/_session", request.getPath(), "The request should be to the _session path"); + } + + /** + * Test that adding the Basic Authentication interceptor to CloudantClient works. + */ + @Test + @RequiresCloudant + public void testBasicAuth() throws IOException { + BasicAuthInterceptor interceptor = + new BasicAuthInterceptor(CloudantClientHelper.COUCH_USERNAME + + ":" + CloudantClientHelper.COUCH_PASSWORD); + + CloudantClient client = ClientBuilder.account(CloudantClientHelper.COUCH_USERNAME) + .interceptors(interceptor).build(); + + // Test passes if there are no exceptions + client.getAllDbs(); + } + + /** + * Test that configuring the Basic Authentication interceptor from credentials and adding to + * the CloudantClient works. + */ + @Test + public void testBasicAuthFromCredentials() throws Exception { + BasicAuthInterceptor interceptor = + BasicAuthInterceptor.createFromCredentials("username", "password"); + + // send back a mock OK 200 + server.enqueue(new MockResponse()); + + CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(server) + .interceptors(interceptor).build(); + + client.getAllDbs(); + + // expected 'username:password' + assertEquals("Basic dXNlcm5hbWU6cGFzc3dvcmQ=", server.takeRequest().getHeader + ("Authorization")); + } + + @Test + public void gatewayStyleURL() throws Exception { + + final String gatewayPath = "/gateway"; + + // Set a dispatcher that returns 200 if the requests have the correct path /gateway/_all_dbs + // Otherwise return 400. + server.setDispatcher(new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + if (request.getPath().equals(gatewayPath + "/_all_dbs")) { + return new MockResponse(); + } else { + return new MockResponse().setResponseCode(400); + } + } + }); + + // Build a client with a URL that includes a path + CloudantClient c = ClientBuilder.url(new URL(server.url(gatewayPath).toString())).build(); + // If the request path is wrong this call will return 400 and throw an exception failing the + // test. + c.getAllDbs(); + + // Build a client with a URL that includes a path with a trailing / + c = ClientBuilder.url(new URL(server.url(gatewayPath + "/").toString())).build(); + // If the request path is wrong this call will return 400 and throw an exception failing the + // test. + c.getAllDbs(); + } + + /** + * Assert that a {@code null} URL causes an IllegalArgumentException to be thrown. + * + * @throws Exception + */ + @Test + public void nullURLThrowsIAE() throws Exception { + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + ClientBuilder.url(null); + } + }); + } +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ComplexKeySerializationTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ComplexKeySerializationTest.java index 34adbc5ad..c7768e92c 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ComplexKeySerializationTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ComplexKeySerializationTest.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 key copy of the License at + * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -14,13 +14,13 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.cloudant.client.api.views.Key; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ComplexKeySerializationTest { diff --git a/cloudant-client/src/test/java/com/cloudant/tests/CouchDbUtilTest.java b/cloudant-client/src/test/java/com/cloudant/tests/CouchDbUtilTest.java index c0a8c02bb..f1f374d24 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/CouchDbUtilTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/CouchDbUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -15,14 +15,14 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.jsonToObject; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class CouchDbUtilTest { diff --git a/cloudant-client/src/test/java/com/cloudant/tests/DBServerTest.java b/cloudant-client/src/test/java/com/cloudant/tests/DBServerTest.java index 1019859ef..c82e58b0f 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/DBServerTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/DBServerTest.java @@ -16,42 +16,20 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; import com.cloudant.client.api.model.DbInfo; import com.cloudant.client.api.model.MetaInformation; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.Test; import java.util.List; -@Category(RequiresDB.class) -public class DBServerTest { - - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - private static CloudantClient account; - private static Database db; - - @BeforeClass - public static void setUp() { - account = clientResource.get(); - db = dbResource.get(); - } - +@RequiresDB +public class DBServerTest extends TestWithDbPerClass { @Test public void dbInfo() { diff --git a/cloudant-client/src/test/java/com/cloudant/tests/DatabaseTest.java b/cloudant-client/src/test/java/com/cloudant/tests/DatabaseTest.java index aac8db689..5b7b21481 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/DatabaseTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/DatabaseTest.java @@ -1,199 +1,201 @@ -/* - * Copyright © 2015, 2016 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; -import com.cloudant.client.api.model.ApiKey; -import com.cloudant.client.api.model.Permissions; -import com.cloudant.client.api.model.Shard; -import com.cloudant.client.org.lightcouch.CouchDbException; -import com.cloudant.test.main.RequiresCloudant; -import com.cloudant.test.main.RequiresCloudantLocal; -import com.cloudant.test.main.RequiresCloudantService; -import com.cloudant.test.main.RequiresCouch; -import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; -import com.cloudant.tests.util.MockWebServerResources; -import com.google.gson.GsonBuilder; - -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; - -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; - -import java.net.MalformedURLException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Category(RequiresDB.class) -public class DatabaseTest { - - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - @ClassRule - public static MockWebServer mockWebServer = new MockWebServer(); - - private static Database db; - private static CloudantClient account; - - @BeforeClass - public static void setUp() throws Exception { - account = clientResource.get(); - db = dbResource.get(); - - //replicate animaldb for tests - com.cloudant.client.api.Replication r = account.replication(); - r.source("http://clientlibs-test.cloudant.com/animaldb"); - r.createTarget(true); - r.target(dbResource.getDbURIWithUserInfo()); - r.trigger(); - } - - @Test - @Category(RequiresCloudantService.class) - public void permissions() { - Map> userPerms = db.getPermissions(); - assertNotNull(userPerms); - ApiKey key = account.generateApiKey(); - EnumSet p = EnumSet.of(Permissions._reader, Permissions._writer); - db.setPermissions(key.getKey(), p); - userPerms = db.getPermissions(); - assertNotNull(userPerms); - assertEquals(1, userPerms.size()); - assertEquals(p, userPerms.get(key.getKey())); - - p = EnumSet.noneOf(Permissions.class); - db.setPermissions(key.getKey(), p); - userPerms = db.getPermissions(); - assertNotNull(userPerms); - assertEquals(1, userPerms.size()); - assertEquals(p, userPerms.get(key.getKey())); - } - - /** - * Test that when called against a DB that is not a Cloudant service - * an UnsupportedOperationException is thrown - */ - @Test(expected = UnsupportedOperationException.class) - @Category({RequiresCouch.class, RequiresCloudantLocal.class}) - public void testPermissionsException() { - Map> userPerms = db.getPermissions(); - } - - @Test - public void permissionsParsing() throws Exception { - CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) - .build(); - Database db = client.database("notarealdb", false); - - // Mock up a request of all permissions - mockWebServer.enqueue(MockWebServerResources.PERMISSIONS); // for GET _security - mockWebServer.enqueue(MockWebServerResources.JSON_OK); // for PUT _security - db.setPermissions("testUsername", EnumSet.allOf(Permissions.class)); - - // Mock up a failing request - String testError = "test error"; - String testReason = "test reason"; - mockWebServer.enqueue(MockWebServerResources.PERMISSIONS); // for GET _security - mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody("{\"reason\":\"" + - testReason + "\", \"error\":\"" + testError + "\"}")); - try { - db.setPermissions("testUsername", EnumSet.allOf(Permissions.class)); - } catch (CouchDbException e) { - assertEquals("", testError, e.getError()); - assertEquals("", testReason, e.getReason()); - } - } - - @Test - @Category(RequiresCloudant.class) - public void shards() { - List shards = db.getShards(); - assert (shards.size() > 0); - for (Shard s : shards) { - assertNotNull(s.getRange()); - assertNotNull(s.getNodes()); - assertNotNull(s.getNodes().hasNext()); - } - } - - @Test - @Category(RequiresCloudant.class) - public void shard() { - Shard s = db.getShard("snipe"); - assertNotNull(s); - assertNotNull(s.getRange()); - assertNotNull(s.getNodes()); - assert (s.getNodes().hasNext()); - } - - - @Test - @Category(RequiresCloudant.class) - public void QuorumTests() { - - db.save(new Animal("human"), 2); - Animal h = db.find(Animal.class, "human", new com.cloudant.client.api.model.Params() - .readQuorum(2)); - assertNotNull(h); - assertEquals("human", h.getId()); - - db.update(h.setClass("inhuman"), 2); - h = db.find(Animal.class, "human", new com.cloudant.client.api.model.Params().readQuorum - (2)); - assertEquals("inhuman", h.getclass()); - - db.post(new Animal("test"), 2); - h = db.find(Animal.class, "test", new com.cloudant.client.api.model.Params().readQuorum(3)); - assertEquals("test", h.getId()); - - - } - - //Test case for issue #31 - @Test - public void customGsonDeserializerTest() throws MalformedURLException { - GsonBuilder builder = new GsonBuilder(); - builder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - - CloudantClient account = CloudantClientHelper.getClientBuilder() - .gsonBuilder(builder) - .build(); - - Database db = account.database(dbResource.getDatabaseName(), false); - - Map h = new HashMap(); - h.put("_id", "serializertest"); - h.put("date", "2015-01-23T18:25:43.511Z"); - db.save(h); - - db.find(Foo.class, "serializertest"); // should not throw a JsonSyntaxException - - } -} +/* + * Copyright © 2015, 2018 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; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.cloudant.client.api.CloudantClient; +import com.cloudant.client.api.Database; +import com.cloudant.client.api.model.ApiKey; +import com.cloudant.client.api.model.Permissions; +import com.cloudant.client.api.model.Shard; +import com.cloudant.client.org.lightcouch.CouchDbException; +import com.cloudant.test.main.RequiresCloudant; +import com.cloudant.test.main.RequiresCloudantLocal; +import com.cloudant.test.main.RequiresCloudantService; +import com.cloudant.test.main.RequiresCouch; +import com.cloudant.test.main.RequiresDB; +import com.cloudant.tests.base.TestWithDbPerClass; +import com.cloudant.tests.extensions.MockWebServerExtension; +import com.cloudant.tests.util.MockWebServerResources; +import com.google.gson.GsonBuilder; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.function.Executable; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; + +import java.net.MalformedURLException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequiresDB +public class DatabaseTest extends TestWithDbPerClass { + + @RegisterExtension + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + + private static MockWebServer mockWebServer; + + @BeforeEach + public void beforeEach() { + mockWebServer = mockWebServerExt.get(); + } + + @BeforeAll + public static void beforeAll() throws Exception { + //replicate animaldb for tests + com.cloudant.client.api.Replication r = account.replication(); + r.source("https://clientlibs-test.cloudant.com/animaldb"); + r.createTarget(true); + r.target(dbResource.getDbURIWithUserInfo()); + r.trigger(); + } + + @Test + @RequiresCloudantService + public void permissions() { + Map> userPerms = db.getPermissions(); + assertNotNull(userPerms); + ApiKey key = account.generateApiKey(); + EnumSet p = EnumSet.of(Permissions._reader, Permissions._writer); + db.setPermissions(key.getKey(), p); + userPerms = db.getPermissions(); + assertNotNull(userPerms); + assertEquals(1, userPerms.size()); + assertEquals(p, userPerms.get(key.getKey())); + + p = EnumSet.noneOf(Permissions.class); + db.setPermissions(key.getKey(), p); + userPerms = db.getPermissions(); + assertNotNull(userPerms); + assertEquals(1, userPerms.size()); + assertEquals(p, userPerms.get(key.getKey())); + } + + /** + * Test that when called against a DB that is not a Cloudant service + * an UnsupportedOperationException is thrown + */ + @RequiresCouch + @RequiresCloudantLocal + public void testPermissionsException() { + assertThrows(UnsupportedOperationException.class, new Executable() { + @Override + public void execute() throws Throwable { + Map> userPerms = db.getPermissions(); + } + }); + } + + @Test + public void permissionsParsing() throws Exception { + CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) + .build(); + Database db = client.database("notarealdb", false); + + // Mock up a request of all permissions + mockWebServer.enqueue(MockWebServerResources.PERMISSIONS); // for GET _security + mockWebServer.enqueue(MockWebServerResources.JSON_OK); // for PUT _security + db.setPermissions("testUsername", EnumSet.allOf(Permissions.class)); + + // Mock up a failing request + String testError = "test error"; + String testReason = "test reason"; + mockWebServer.enqueue(MockWebServerResources.PERMISSIONS); // for GET _security + mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody("{\"reason\":\"" + + testReason + "\", \"error\":\"" + testError + "\"}")); + try { + db.setPermissions("testUsername", EnumSet.allOf(Permissions.class)); + } catch (CouchDbException e) { + assertEquals(testError, e.getError()); + assertEquals(testReason, e.getReason()); + } + } + + @Test + @RequiresCloudant + public void shards() { + List shards = db.getShards(); + assert (shards.size() > 0); + for (Shard s : shards) { + assertNotNull(s.getRange()); + assertNotNull(s.getNodes()); + assertNotNull(s.getNodes().hasNext()); + } + } + + @Test + @RequiresCloudant + public void shard() { + Shard s = db.getShard("snipe"); + assertNotNull(s); + assertNotNull(s.getRange()); + assertNotNull(s.getNodes()); + assert (s.getNodes().hasNext()); + } + + + @Test + @RequiresCloudant + public void QuorumTests() { + + db.save(new Animal("human"), 2); + Animal h = db.find(Animal.class, "human", new com.cloudant.client.api.model.Params() + .readQuorum(2)); + assertNotNull(h); + assertEquals("human", h.getId()); + + db.update(h.setClass("inhuman"), 2); + h = db.find(Animal.class, "human", new com.cloudant.client.api.model.Params().readQuorum + (2)); + assertEquals("inhuman", h.getclass()); + + db.post(new Animal("test"), 2); + h = db.find(Animal.class, "test", new com.cloudant.client.api.model.Params().readQuorum(3)); + assertEquals("test", h.getId()); + + + } + + //Test case for issue #31 + @Test + public void customGsonDeserializerTest() throws MalformedURLException { + GsonBuilder builder = new GsonBuilder(); + builder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + CloudantClient account = CloudantClientHelper.getClientBuilder() + .gsonBuilder(builder) + .build(); + + Database db = account.database(dbResource.getDatabaseName(), false); + + Map h = new HashMap(); + h.put("_id", "serializertest"); + h.put("date", "2015-01-23T18:25:43.511Z"); + db.save(h); + + db.find(Foo.class, "serializertest"); // should not throw a JsonSyntaxException + + } +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/DatabaseURIHelperTest.java b/cloudant-client/src/test/java/com/cloudant/tests/DatabaseURIHelperTest.java index d4cf83f95..1f732557c 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/DatabaseURIHelperTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/DatabaseURIHelperTest.java @@ -1,5 +1,6 @@ -/** +/* * Copyright (c) 2015 Cloudant, Inc. All rights reserved. + * Copyright © 2018 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 @@ -17,39 +18,85 @@ import com.cloudant.client.internal.DatabaseURIHelper; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import java.net.URI; import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.stream.Stream; -@RunWith(Parameterized.class) +@ExtendWith(DatabaseURIHelperTest.ParameterProvider.class) public class DatabaseURIHelperTest { - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new Object[][]{ - {""}, {"/api/couch/account_2128459498a75498"} - }); + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } + + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + return Stream.of(invocationContext(""), invocationContext + ("/api/couch/account_2128459498a75498")); + } + + public static TestTemplateInvocationContext invocationContext(final String path) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("path:%s", path); + } + + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals + (String.class); + } + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return path; + } + return null; + } + }); + } + }; + } } String protocol = "http"; String hostname = "127.0.0.1"; int port = 5984; - @Parameterized.Parameter - public String path; String uriBase; - @Before - public void setup() { + @BeforeEach + public void setup(String path) { uriBase = protocol + "://" + hostname + ":" + port + path; } @@ -58,139 +105,141 @@ private DatabaseURIHelper helper(String dbName) throws URISyntaxException { return new DatabaseURIHelper(new URI(protocol, null, hostname, port, dbName, null, null)); } - @Test - public void _localDocumentURI() throws Exception { + @TestTemplate + public void _localDocumentURI(String path) throws Exception { final String expected = uriBase + "/db_name/_local/mylocaldoc"; DatabaseURIHelper helper = helper(path + "/db_name"); URI localDoc = helper.documentUri("_local/mylocaldoc"); - Assert.assertEquals(expected,localDoc.toString()); + Assertions.assertEquals(expected, localDoc.toString()); } - @Test - public void buildDbUri() throws Exception { + @TestTemplate + public void buildDbUri(String path) throws Exception { URI expected = new URI(uriBase + "/db_name"); URI actual = helper(path + "/db_name").getDatabaseUri(); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } // this test shows that non-ascii characters will be represented correctly // in the url but that we don't escape characters like / - @Test - public void buildEscapedDbUri() throws Exception { + @TestTemplate + public void buildEscapedDbUri(String path) throws Exception { URI expected = new URI(uriBase + "/SDF@%23%25$%23)DFGKLDfdffdg%C3%A9"); URI actual = helper(path + "/SDF@#%$#)DFGKLDfdffdg\u00E9").getDatabaseUri(); - Assert.assertEquals(expected.toASCIIString(), actual.toASCIIString()); + Assertions.assertEquals(expected.toASCIIString(), actual.toASCIIString()); } - @Test - public void buildChangesUri_options_optionsEncoded() throws Exception { + @TestTemplate + public void buildChangesUri_options_optionsEncoded(String path) throws Exception { URI expected = new URI(uriBase + "/test/_changes?limit=100&since=%22%5B%5D%22"); Map options = new HashMap(); options.put("since", "\"[]\""); options.put("limit", 100); URI actual = helper(path + "/test").changesUri(options); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildChangesUri_woOptions() throws Exception { + @TestTemplate + public void buildChangesUri_woOptions(String path) throws Exception { URI expected = new URI(uriBase + "/test/_changes"); URI actual = helper(path + "/test").changesUri(new HashMap()); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildBulkDocsUri() throws Exception { + @TestTemplate + public void buildBulkDocsUri(String path) throws Exception { URI expected = new URI(uriBase + "/test/_bulk_docs"); URI actual = helper(path + "/test").bulkDocsUri(); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void revsDiffUri() throws Exception { + @TestTemplate + public void revsDiffUri(String path) throws Exception { URI expected = new URI(uriBase + "/test/_revs_diff"); URI actual = helper(path + "/test").revsDiffUri(); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } // get a document with a db 'mounted' at / - @Test - public void buildDocumentUri_emptyDb() throws Exception { + @TestTemplate + public void buildDocumentUri_emptyDb(String path) throws Exception { URI expected = new URI(uriBase + "/documentId"); URI actual = helper(path).documentUri("documentId"); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_woOptions() throws Exception { + @TestTemplate + public void buildDocumentUri_woOptions(String path) throws Exception { URI expected = new URI(uriBase + "/test/documentId"); URI actual = helper(path + "/test").documentUri("documentId"); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_slashInDocumentId() throws Exception { + @TestTemplate + public void buildDocumentUri_slashInDocumentId(String path) throws Exception { URI expected = new URI(uriBase + "/test/path1%2Fpath2"); URI actual = helper(path + "/test").documentUri("path1/path2"); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_specialCharsInDocumentId() throws Exception { + @TestTemplate + public void buildDocumentUri_specialCharsInDocumentId(String path) throws Exception { URI expected = new URI(uriBase + "/test/SDF@%23%25$%23)DFGKLDfdffdg%C3%A9"); URI actual = helper(path + "/test").documentUri("SDF@#%$#)DFGKLDfdffdg\u00E9"); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_colonInDocumentId() throws Exception { + @TestTemplate + public void buildDocumentUri_colonInDocumentId(String path) throws Exception { URI expected = new URI(uriBase + "/test/:this:has:colons:"); URI actual = helper(path + "/test").documentUri(":this:has:colons:"); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_options_optionsEncoded() throws Exception { + @TestTemplate + public void buildDocumentUri_options_optionsEncoded(String path) throws Exception { URI expected = new URI(uriBase + "/test/path1%2Fpath2?detail=true&revs=%5B1-2%5D"); Map options = new TreeMap(); options.put("revs", "[1-2]"); options.put("detail", true); URI actual = helper(path + "/test").documentId("path1/path2").query(options).build(); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_options_encodeSeparators() throws Exception { - URI expected = new URI(uriBase + "/test/path1%2Fpath2?d%26etail%3D=%26%3D%3Dds%26&revs=%5B1-2%5D"); + @TestTemplate + public void buildDocumentUri_options_encodeSeparators(String path) throws Exception { + URI expected = new URI(uriBase + + "/test/path1%2Fpath2?d%26etail%3D=%26%3D%3Dds%26&revs=%5B1-2%5D"); TreeMap options = new TreeMap(); options.put("revs", "[1-2]"); options.put("d&etail=", "&==ds&"); URI actual = helper(path + "/test").documentId("path1/path2").query(options).build(); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } - @Test - public void buildDocumentUri_options_hasPlus() throws Exception { + @TestTemplate + public void buildDocumentUri_options_hasPlus(String path) throws Exception { URI expected = new URI(uriBase + "/test/path1%2Fpath2?q=class:mammal%2Bwith%2Bplusses"); TreeMap options = new TreeMap(); options.put("q", "class:mammal+with+plusses"); URI actual = helper(path + "/test").documentId("path1/path2").query(options).build(); - Assert.assertEquals(expected, actual); + Assertions.assertEquals(expected, actual); } // this test shows that non-ascii characters will be represented correctly // in the url but that we don't escape characters like / in the root url, but that they are // correctly escaped in the document part of the url - @Test - public void buildVeryEscapedUri() throws Exception { - URI expected = new URI(uriBase + "/SDF@%23%25$%23)KLDfdffdg%C3%A9/%2FSF@%23%25$%23)DFGKLDfdffdg%C3%A9%2Fpath2?detail=/SDF@%23%25$%23)%C3%A9&revs=%5B1-2%5D"); + @TestTemplate + public void buildVeryEscapedUri(String path) throws Exception { + URI expected = new URI(uriBase + "/SDF@%23%25$%23)KLDfdffdg%C3%A9/%2FSF@%23%25$%23)" + + "DFGKLDfdffdg%C3%A9%2Fpath2?detail=/SDF@%23%25$%23)%C3%A9&revs=%5B1-2%5D"); Map options = new TreeMap(); options.put("revs", "[1-2]"); @@ -198,33 +247,34 @@ public void buildVeryEscapedUri() throws Exception { URI actual = helper(path + "/SDF@#%$#)KLDfdffdg\u00E9").documentId("/SF@#%$#)" + "DFGKLDfdffdg\u00E9/path2").query(options).build(); - Assert.assertEquals(expected.toASCIIString(), actual.toASCIIString()); + Assertions.assertEquals(expected.toASCIIString(), actual.toASCIIString()); } - @Test - public void encodePathComponent_slashShouldBeEncoded() throws Exception { + @TestTemplate + public void encodePathComponent_slashShouldBeEncoded(String path) throws Exception { String in = "/path1/path2"; - Assert.assertEquals("%2Fpath1%2Fpath2", helper(path + "/test").encodeId(in)); + Assertions.assertEquals("%2Fpath1%2Fpath2", helper(path + "/test").encodeId(in)); } - @Test - public void encodeQueryParameter_noLeadingQuestionMark() throws Exception { + @TestTemplate + public void encodeQueryParameter_noLeadingQuestionMark(String path) throws Exception { String in = "a"; - Assert.assertTrue(helper(path + "/test").documentUri(in).toString().charAt(0) != '?'); + Assertions.assertTrue(helper(path + "/test").documentUri(in).toString().charAt(0) != '?'); } - @Test - public void buildQuery_joinTwoQueries() throws Exception { + @TestTemplate + public void buildQuery_joinTwoQueries(String path) throws Exception { Map mapOptions = new TreeMap(); mapOptions.put("revs", "[1-2]"); mapOptions.put("detail", "/SDF@#%$#)"); String query = "boolean=true"; - URI expectedQuery = new URI(uriBase + "?detail=/SDF@%23%25$%23)&revs=%5B1-2%5D&boolean=true"); + URI expectedQuery = new URI(uriBase + "?detail=/SDF@%23%25$%23)" + + "&revs=%5B1-2%5D&boolean=true"); URI actualQuery = helper(path).query(mapOptions).query(query).build(); - Assert.assertEquals(expectedQuery.toASCIIString(), actualQuery.toASCIIString()); + Assertions.assertEquals(expectedQuery.toASCIIString(), actualQuery.toASCIIString()); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentTest.java b/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentTest.java index e69e26389..f76414a5f 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -21,24 +21,91 @@ import com.google.gson.JsonObject; import org.junit.Assert; -import org.junit.Test; -import org.junit.experimental.runners.Enclosed; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import java.util.ArrayList; -import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * Created by Rhys Short on 24/02/2016. */ -@RunWith(Enclosed.class) +@ExtendWith(DesignDocumentTest.ParameterProvider.class) public class DesignDocumentTest { + + // TODO might be easier just to pass in fields to each test, + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } + + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + return StreamSupport.stream(data().spliterator(), false); + } + + public static TestTemplateInvocationContext invocationContext(final Field field) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("Field:%s", field); + } + + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals(Field + .class); + } + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return field; + } + return null; + } + }); + } + }; + } + } + + public static Iterable data() { + List contexts = new + ArrayList(); + for (Field f : EnumSet.allOf(Field.class)) { + contexts.add(ParameterProvider.invocationContext(f)); + } + return contexts; + } + + static Gson gson = new GsonBuilder().create(); enum Field { @@ -154,61 +221,44 @@ private static DesignDocument getDesignDocumentWithDifferent(Field f) { return designDocument; } - public static class OneOffEqualityTests { - @Test - public void testDesignDocEqualsForAllFields() { - Assert.assertEquals(getDesignDocument(), getDesignDocument()); - } + @Test + public void testDesignDocEqualsForAllFields() { + Assert.assertEquals(getDesignDocument(), getDesignDocument()); } - @RunWith(Parameterized.class) - public static final class ParameterizedEqualityTests { - - /** - * Parameters for these tests so we run each test multiple times. - * We run with a single key or a complex key and both ascending and descending. - */ - @Parameterized.Parameters(name = "Field:{0}") - public static Collection data() { - return EnumSet.allOf(Field.class); - } - - @Parameterized.Parameter - public Field field; - - /** - * Tests the design docs are equal for each field in turn. - */ - @Test - public void testDesignDocEqualsForEachField() { - Assert.assertEquals(getDesignDocumentWithFields(EnumSet.of(field)), - getDesignDocumentWithFields(EnumSet.of(field))); - } - /** - * Tests the design docs are not equal when each field is empty in one of the compared docs. - * - * @throws Exception - */ - @Test - public void testDesignDocNotEqualEmpty() throws Exception { - Assert.assertNotEquals(getDesignDocument(), getDesignDocumentWithFields(EnumSet - .complementOf(EnumSet.of(field)))); - } + /** + * Tests the design docs are equal for each field in turn. + */ + @TestTemplate + public void testDesignDocEqualsForEachField(Field field) { + Assert.assertEquals(getDesignDocumentWithFields(EnumSet.of(field)), + getDesignDocumentWithFields(EnumSet.of(field))); + } - /** - * Tests the design docs are not equal when each field is different in one of the - * compared docs. - * - * @throws Exception - */ - @Test - public void testDesignDocNotEqualDifferent() throws Exception { - Assert.assertNotEquals(getDesignDocument(), getDesignDocumentWithDifferent(field)); - } + /** + * Tests the design docs are not equal when each field is empty in one of the compared docs. + * + * @throws Exception + */ + @TestTemplate + public void testDesignDocNotEqualEmpty(Field field) throws Exception { + Assert.assertNotEquals(getDesignDocument(), getDesignDocumentWithFields(EnumSet + .complementOf(EnumSet.of(field)))); + } + /** + * Tests the design docs are not equal when each field is different in one of the + * compared docs. + * + * @throws Exception + */ + @TestTemplate + public void testDesignDocNotEqualDifferent(Field field) throws Exception { + Assert.assertNotEquals(getDesignDocument(), getDesignDocumentWithDifferent(field)); } + private static void indexes(DesignDocument designDocument) { Map> indexes = new HashMap>(); diff --git a/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentsTest.java b/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentsTest.java index 6e2386dec..441bab87d 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentsTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/DesignDocumentsTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,34 +14,32 @@ */ package com.cloudant.tests; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +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.Replication; import com.cloudant.client.api.model.DesignDocument; import com.cloudant.client.api.model.Response; -import com.cloudant.client.api.views.AllDocsRequest; -import com.cloudant.client.api.views.Key; import com.cloudant.client.org.lightcouch.CouchDbException; -import com.cloudant.test.main.RequiresCloudant; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; +import com.cloudant.tests.extensions.MockWebServerExtension; import com.cloudant.tests.util.Utils; import com.google.gson.JsonObject; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.function.Executable; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -55,26 +53,25 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -@Category(RequiresDB.class) -public class DesignDocumentsTest { - - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); - @Rule - public DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static MockWebServer mockWebServer = new MockWebServer(); - - private Database db; - private CloudantClient account; - private File rootDesignDir; - private DesignDocument designDocExample; - private DesignDocumentManager designManager; - - @Before - public void setUp() throws Exception { - account = clientResource.get(); - db = dbResource.get(); +@RequiresDB +public class DesignDocumentsTest extends TestWithDbPerClass { + + @RegisterExtension + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + + private static MockWebServer mockWebServer; + + private static File rootDesignDir; + private static DesignDocument designDocExample; + private static DesignDocumentManager designManager; + + @BeforeEach + public void beforeEach() { + mockWebServer = mockWebServerExt.get(); + } + + @BeforeAll + public static void beforeAll() throws Exception { rootDesignDir = new File(System.getProperty("user.dir") + "/src/test/resources/design-files"); designManager = db.getDesignDocumentManager(); @@ -90,8 +87,9 @@ public void setUp() throws Exception { * @return the DesignDocument object generated from the file. * @throws Exception */ - private DesignDocument fileToDesignDocument(String name) throws Exception { - File testDesignDocFile = new File(String.format("%s/%s_design_doc.js", rootDesignDir, name)); + private static DesignDocument fileToDesignDocument(String name) throws Exception { + File testDesignDocFile = new File(String.format("%s/%s_design_doc.js", rootDesignDir, + name)); return designManager.fromFile(testDesignDocFile); } @@ -109,7 +107,7 @@ public void designDocCompare() throws Exception { DesignDocument designDoc11 = db.getDesignDocumentManager().get("_design/example"); - assertEquals("The design document retrieved should equal ", exampleDoc, designDoc11); + assertEquals(exampleDoc, designDoc11, "The design document retrieved should equal "); } @Test @@ -148,7 +146,7 @@ public void updateDesignDocs() throws Exception { designManager.put(docArray); for (String id : new String[]{"_design/conflicts", "_design/example", "_design/views101"}) { - assertNotNull("", designManager.get(id)); + assertNotNull(designManager.get(id), ""); } } @@ -163,8 +161,8 @@ public void designDocGetNoPrefix() throws Exception { designManager.put(designDocExample); // Retrieve it without a prefix - assertNotNull("The design doc should be retrieved without a _design prefix", - designManager.get("example")); + assertNotNull(designManager.get("example"), "The design doc should be retrieved without a" + + " _design prefix"); } /** @@ -179,8 +177,8 @@ public void designDocGetNoPrefixWithRevision() throws Exception { Response r = designManager.put(designDocExample); // Retrieve it without a prefix - assertNotNull("The design doc should be retrieved without a _design prefix", - designManager.get("example", r.getRev())); + assertNotNull(designManager.get("example", r.getRev()), "The design doc should be " + + "retrieved without a _design prefix"); } /** @@ -245,12 +243,13 @@ public void designDocPutNoPrefix() throws Exception { Utils.assertOKResponse(designManager.put(designDocExampleNoPrefix)); // Retrieve it with a prefix - assertNotNull("The design doc should be retrievable with a _design prefix", - designManager.get("_design/example")); + assertNotNull(designManager.get("_design/example"), "The design doc should be retrievable" + + " with a _design prefix"); } /** * Test that a design document with an index can be deleted. + * * @throws Exception */ @Test @@ -269,15 +268,21 @@ public void deleteDesignDocWithIndex() throws Exception { * * @throws Exception */ - @Test(expected = CouchDbException.class) + @Test public void couchDbExceptionIfIOExceptionDuringDDocRemove() throws Exception { - CloudantClient mockClient = CloudantClientHelper.newMockWebServerClientBuilder - (mockWebServer).readTimeout(50, TimeUnit.MILLISECONDS).build(); - // Cause a read timeout to generate an IOException - mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE)); - Database database = mockClient.database(dbResource.getDatabaseName(), false); - // Try to remove a design document by id only, generates a HEAD request for revision info - database.getDesignDocumentManager().remove("example"); + assertThrows(CouchDbException.class, new Executable() { + @Override + public void execute() throws Throwable { + CloudantClient mockClient = CloudantClientHelper.newMockWebServerClientBuilder + (mockWebServer).readTimeout(50, TimeUnit.MILLISECONDS).build(); + // Cause a read timeout to generate an IOException + mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE)); + Database database = mockClient.database(dbResource.getDatabaseName(), false); + // Try to remove a design document by id only, generates a HEAD request for + // revision info + database.getDesignDocumentManager().remove("example"); + } + }); } /** @@ -285,14 +290,19 @@ public void couchDbExceptionIfIOExceptionDuringDDocRemove() throws Exception { * * @throws Exception */ - @Test(expected = CouchDbException.class) + @Test public void couchDbExceptionIfNoETagOnDDocRemove() throws Exception { - CloudantClient mockClient = CloudantClientHelper.newMockWebServerClientBuilder - (mockWebServer).build(); - Database database = mockClient.database(dbResource.getDatabaseName(), false); - // Queue a mock response with no "ETag" header - mockWebServer.enqueue(new MockResponse()); - database.getDesignDocumentManager().remove("example"); + assertThrows(CouchDbException.class, new Executable() { + @Override + public void execute() throws Throwable { + CloudantClient mockClient = CloudantClientHelper.newMockWebServerClientBuilder + (mockWebServer).build(); + Database database = mockClient.database(dbResource.getDatabaseName(), false); + // Queue a mock response with no "ETag" header + mockWebServer.enqueue(new MockResponse()); + database.getDesignDocumentManager().remove("example"); + } + }); } /** @@ -320,8 +330,8 @@ public int compare(DesignDocument doc1, DesignDocument doc2) { doc.setRevision(designManager.put(doc).getRev()); } - assertEquals("The retrieved list of design documents should match the expected list", - designDocs, designManager.list()); + assertEquals(designDocs, designManager.list(), "The retrieved list of design documents " + + "should match the expected list"); } /** @@ -342,9 +352,9 @@ public void deserializeJavascriptView() throws Exception { DesignDocument queryDDoc = fileToDesignDocument("example"); Map views = queryDDoc.getViews(); for (DesignDocument.MapReduce mrView : views.values()) { - assertFalse("The map function should not start with \"", mrView.getMap().startsWith - ("\"")); - assertFalse("The map function should not end with \"", mrView.getMap().endsWith("\"")); + assertFalse(mrView.getMap().startsWith("\""), "The map function should not start with" + + " \""); + assertFalse(mrView.getMap().endsWith("\""), "The map function should not end with \""); } } @@ -375,13 +385,13 @@ public void serializeJavascriptView() throws Exception { // Retrieve the doc and check that the javascript function is correct DesignDocument retrievedDDoc = designManager.get(testDDocName, r.getRev()); - assertNotNull("There should be a retrieved design doc", retrievedDDoc); + assertNotNull(retrievedDDoc, "There should be a retrieved design doc"); Map retrievedViews = retrievedDDoc.getViews(); - assertNotNull("There should be views defined on the design doc", retrievedViews); + assertNotNull(retrievedViews, "There should be views defined on the design doc"); DesignDocument.MapReduce mrView = retrievedViews.get("testView"); - assertNotNull("There should be a testView in the retrieved design doc", mrView); - assertEquals("The map function string should be the expected string", - mapFunction, mrView.getMap()); + assertNotNull(mrView, "There should be a testView in the retrieved design doc"); + assertEquals(mapFunction, mrView.getMap(), "The map function string should be the " + + "expected string"); } /** @@ -397,14 +407,14 @@ public void serializeJavascriptView() throws Exception { public void serializeQueryDesignDoc() throws Exception { DesignDocument queryDDoc = fileToDesignDocument("query"); Map views = queryDDoc.getViews(); - assertEquals("There should be one view", 1, views.size()); + assertEquals(1, views.size(), "There should be one view"); for (DesignDocument.MapReduce mrView : views.values()) { - assertTrue("The map function should be a javascript function in a JSON form, " + - "so start with {", mrView.getMap().startsWith("{")); - assertTrue("The map function should be a javascript function in a JSON form, " + - "so end with }", mrView.getMap().endsWith("}")); - assertEquals("The map function string should be an object form", - "{\"fields\":{\"Person_dob\":\"asc\"}}", mrView.getMap()); + assertTrue(mrView.getMap().startsWith("{"), "The map function should be a javascript " + + "function in a JSON form, " + "so start with {"); + assertTrue(mrView.getMap().endsWith("}"), "The map function should be a javascript " + + "function in a JSON form, " + "so end with }"); + assertEquals("{\"fields\":{\"Person_dob\":\"asc\"}}", mrView.getMap(), "The map " + + "function string should be an object form"); } } @@ -424,14 +434,14 @@ public void deserializeQueryDesignDoc() throws Exception { // Get the query design document DesignDocument queryDDoc = designManager.get("testQuery"); Map views = queryDDoc.getViews(); - assertEquals("There should be one view", 1, views.size()); + assertEquals(1, views.size(), "There should be one view"); for (DesignDocument.MapReduce mrView : views.values()) { - assertTrue("The map function should be a javascript function in a JSON form, " + - "so start with {", mrView.getMap().startsWith("{")); - assertTrue("The map function should be a javascript function in a JSON form, " + - "so end with }", mrView.getMap().endsWith("}")); - assertEquals("The map function string should be an object form", - "{\"fields\":{\"Person_dob\":\"asc\"}}", mrView.getMap()); + assertTrue(mrView.getMap().startsWith("{"), "The map function should be a javascript " + + "function in a JSON form, " + "so start with {"); + assertTrue(mrView.getMap().endsWith("}"), "The map function should be a javascript " + + "function in a JSON form, " + "so end with }"); + assertEquals("{\"fields\":{\"Person_dob\":\"asc\"}}", mrView.getMap(), "The map " + + "function string should be an object form"); } } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/DocumentsCRUDTest.java b/cloudant-client/src/test/java/com/cloudant/tests/DocumentsCRUDTest.java index d7a4c841e..45e67be12 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/DocumentsCRUDTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/DocumentsCRUDTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,29 +14,23 @@ */ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +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.model.Params; import com.cloudant.client.api.model.Response; import com.cloudant.client.org.lightcouch.DocumentConflictException; import com.cloudant.client.org.lightcouch.NoDocumentException; -import com.cloudant.test.main.RequiresCouch; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import java.io.IOException; import java.io.InputStream; @@ -44,21 +38,8 @@ import java.util.Map; import java.util.UUID; -@Category(RequiresDB.class) -public class DocumentsCRUDTest { - - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - private static CloudantClient account = clientResource.get(); - private static Database db; - - @BeforeClass - public static void setUp() { - db = dbResource.get(); - } +@RequiresDB +public class DocumentsCRUDTest extends TestWithDbPerClass { // Find @@ -94,11 +75,10 @@ public void findJsonObject() { } @Test - @Category(RequiresCouch.class) public void findAny() { - String uri = account.getBaseUri() + "_stats"; - JsonObject jsonObject = db.findAny(JsonObject.class, uri); - assertNotNull(jsonObject); + String uri = account.getBaseUri() + "/_all_dbs"; + JsonArray jsonArray = db.findAny(JsonArray.class, uri); + assertNotNull(jsonArray); } @Test @@ -116,14 +96,24 @@ public void findWithParams() { assertNotNull(foo); } - @Test(expected = IllegalArgumentException.class) + @Test public void findWithInvalidId_throwsIllegalArgumentException() { - db.find(Foo.class, ""); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + db.find(Foo.class, ""); + } + }); } - @Test(expected = NoDocumentException.class) + @Test public void findWithUnknownId_throwsNoDocumentException() { - db.find(Foo.class, generateUUID()); + assertThrows(NoDocumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + db.find(Foo.class, generateUUID()); + } + }); } @Test @@ -173,23 +163,38 @@ public void saveObjectPost() { assertNotNull(response.getId()); } - @Test(expected = IllegalArgumentException.class) + @Test public void saveInvalidObject_throwsIllegalArgumentException() { - db.save(null); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + db.save(null); + } + }); } - @Test(expected = IllegalArgumentException.class) + @Test public void saveNewDocWithRevision_throwsIllegalArgumentException() { - Bar bar = new Bar(); - bar.setRevision("unkown"); - db.save(bar); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + Bar bar = new Bar(); + bar.setRevision("unkown"); + db.save(bar); + } + }); } - @Test(expected = DocumentConflictException.class) + @Test public void saveDocWithDuplicateId_throwsDocumentConflictException() { - String id = generateUUID(); - db.save(new Foo(id)); - db.save(new Foo(id)); + assertThrows(DocumentConflictException.class, new Executable() { + @Override + public void execute() throws Throwable { + String id = generateUUID(); + db.save(new Foo(id)); + db.save(new Foo(id)); + } + }); } // Update @@ -211,17 +216,27 @@ public void updateWithIdContainSlash() { assertEquals(idWithSlash, responseUpdate.getId()); } - @Test(expected = IllegalArgumentException.class) + @Test public void updateWithoutIdAndRev_throwsIllegalArgumentException() { - db.update(new Foo()); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + db.update(new Foo()); + } + }); } - @Test(expected = DocumentConflictException.class) + @Test public void updateWithInvalidRev_throwsDocumentConflictException() { - Response response = db.save(new Foo()); - Foo foo = db.find(Foo.class, response.getId()); - db.update(foo); - db.update(foo); + assertThrows(DocumentConflictException.class, new Executable() { + @Override + public void execute() throws Throwable { + Response response = db.save(new Foo()); + Foo foo = db.find(Foo.class, response.getId()); + db.update(foo); + db.update(foo); + } + }); } // Delete diff --git a/cloudant-client/src/test/java/com/cloudant/tests/Foo.java b/cloudant-client/src/test/java/com/cloudant/tests/Foo.java index a6b659cbf..45210546c 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/Foo.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/Foo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,9 +14,8 @@ package com.cloudant.tests; -import com.google.gson.JsonArray; - import com.cloudant.client.org.lightcouch.Attachment; +import com.google.gson.JsonArray; import java.util.Arrays; import java.util.Date; diff --git a/cloudant-client/src/test/java/com/cloudant/tests/HierarchicalUriComponentsTest.java b/cloudant-client/src/test/java/com/cloudant/tests/HierarchicalUriComponentsTest.java index 6cb6f6708..8445743ae 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/HierarchicalUriComponentsTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/HierarchicalUriComponentsTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017 IBM Corp. All rights reserved. + * Copyright © 2017, 2018 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 @@ -14,11 +14,11 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.cloudant.client.internal.HierarchicalUriComponents; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.UnsupportedEncodingException; diff --git a/cloudant-client/src/test/java/com/cloudant/tests/HttpIamTest.java b/cloudant-client/src/test/java/com/cloudant/tests/HttpIamTest.java index ab197998e..c7b267129 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/HttpIamTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/HttpIamTest.java @@ -25,19 +25,20 @@ import static com.cloudant.tests.util.MockWebServerResources.iamSessionUnquoted; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import com.cloudant.client.api.CloudantClient; import com.cloudant.client.org.lightcouch.CouchDbException; import com.cloudant.http.Http; +import com.cloudant.tests.extensions.MockWebServerExtension; import com.cloudant.tests.util.IamSystemPropertyMock; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -53,20 +54,24 @@ public class HttpIamTest { public static IamSystemPropertyMock iamSystemPropertyMock; - @Rule - public MockWebServer mockWebServer = new MockWebServer(); + @RegisterExtension + public MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); - @Rule - public MockWebServer mockIamServer = new MockWebServer(); + @RegisterExtension + public MockWebServerExtension mockIamServerExt = new MockWebServerExtension(); final static String hello = "{\"hello\":\"world\"}\r\n"; final static String iamApiKey = "iam"; final static String iamTokenEndpoint = "/identity/token"; + public MockWebServer mockWebServer; + public MockWebServer mockIamServer; + + /** * Before running this test class setup the property mock. */ - @BeforeClass + @BeforeAll public static void setupIamSystemPropertyMock() { iamSystemPropertyMock = new IamSystemPropertyMock(); } @@ -74,8 +79,10 @@ public static void setupIamSystemPropertyMock() { /** * Before each test set the value of the endpoint in the property mock */ - @Before + @BeforeEach public void setIAMMockEndpoint() { + mockWebServer = mockWebServerExt.get(); + mockIamServer = mockIamServerExt.get(); iamSystemPropertyMock.setMockIamTokenEndpointUrl(mockIamServer.url(iamTokenEndpoint) .toString()); } @@ -85,6 +92,7 @@ public void setIAMMockEndpoint() { * - GET a resource on the cloudant server * - Cookie jar empty, so get IAM token followed by session cookie * - GET now proceeds as normal, expected cookie value is sent in header + * * @throws Exception */ @Test @@ -104,20 +112,20 @@ public void iamTokenAndCookieSuccessful() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response); + assertEquals(hello, response, "The expected response should be received"); // cloudant mock server // assert that there were 2 calls RecordedRequest[] recordedRequests = takeN(mockWebServer, 2); - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[0].getPath()); + assertEquals("/_iam_session", + recordedRequests[0].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[0].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN)); // the request should have the cookie header - assertEquals("The request should have been for /", "/", - recordedRequests[1].getPath()); + assertEquals("/", + recordedRequests[1].getPath(), "The request should have been for /"); // The cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", recordedRequests[1].getHeader("Cookie"), @@ -127,16 +135,18 @@ public void iamTokenAndCookieSuccessful() throws Exception { // asserts on the IAM server // assert that there was 1 call RecordedRequest[] recordedIamRequests = takeN(mockIamServer, 1); - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[0].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[0].getPath(), "The request should have been for " + + "/identity/token"); assertThat("The request body should contain the IAM API key", recordedIamRequests[0].getBody().readString(Charset.forName("UTF-8")), - containsString("apikey="+iamApiKey)); + containsString("apikey=" + iamApiKey)); } /** * Assert that the IAM API key is preferred to username/password if both are supplied. * As above test but with different builder arguments. + * * @throws Exception */ @Test @@ -158,20 +168,20 @@ public void iamApiKeyPreferredToUsernamePassword() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response); + assertEquals(hello, response, "The expected response should be received"); // cloudant mock server // assert that there were 2 calls RecordedRequest[] recordedRequests = takeN(mockWebServer, 2); - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[0].getPath()); + assertEquals("/_iam_session", + recordedRequests[0].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[0].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN)); // the request should have the cookie header - assertEquals("The request should have been for /", "/", - recordedRequests[1].getPath()); + assertEquals("/", + recordedRequests[1].getPath(), "The request should have been for /"); // The cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", recordedRequests[1].getHeader("Cookie"), @@ -181,11 +191,12 @@ public void iamApiKeyPreferredToUsernamePassword() throws Exception { // asserts on the IAM server // assert that there was 1 call RecordedRequest[] recordedIamRequests = takeN(mockIamServer, 1); - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[0].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[0].getPath(), "The request should have been for " + + "/identity/token"); assertThat("The request body should contain the IAM API key", recordedIamRequests[0].getBody().readString(Charset.forName("UTF-8")), - containsString("apikey="+iamApiKey)); + containsString("apikey=" + iamApiKey)); } /** @@ -195,7 +206,8 @@ public void iamApiKeyPreferredToUsernamePassword() throws Exception { * - GET now proceeds as normal, expected cookie value is sent in header * - second GET on cloudant server, re-using session cookie * - third GET on cloudant server, cookie expired, get IAM token and session cookie and replay - * request + * request + * * @throws Exception */ @Test @@ -208,7 +220,8 @@ public void iamTokenAndCookieWithExpirySuccessful() throws Exception { mockWebServer.enqueue(new MockResponse().setResponseCode(200) .setBody(hello)); // cookie expired - mockWebServer.enqueue(new MockResponse().setResponseCode(401).setBody("{\"error\":\"credentials_expired\"}")); + mockWebServer.enqueue(new MockResponse().setResponseCode(401).setBody + ("{\"error\":\"credentials_expired\"}")); // response with new cookie mockWebServer.enqueue(OK_IAM_COOKIE_2); mockWebServer.enqueue(new MockResponse().setResponseCode(200) @@ -222,47 +235,47 @@ public void iamTokenAndCookieWithExpirySuccessful() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response); + assertEquals(hello, response, "The expected response should be received"); String response2 = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response2); + assertEquals(hello, response2, "The expected response should be received"); String response3 = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response3); + assertEquals(hello, response3, "The expected response should be received"); // cloudant mock server // assert that there were 6 calls RecordedRequest[] recordedRequests = takeN(mockWebServer, 6); - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[0].getPath()); + assertEquals("/_iam_session", + recordedRequests[0].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[0].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN)); // first request - assertEquals("The request should have been for /", "/", - recordedRequests[1].getPath()); + assertEquals("/", + recordedRequests[1].getPath(), "The request should have been for /"); // The cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", recordedRequests[1].getHeader("Cookie"), anyOf(containsString(iamSession(EXPECTED_OK_COOKIE)), containsString(iamSessionUnquoted(EXPECTED_OK_COOKIE)))); // second request - assertEquals("The request should have been for /", "/", - recordedRequests[2].getPath()); + assertEquals("/", + recordedRequests[2].getPath(), "The request should have been for /"); // third request, will be rejected due to cookie expiry - assertEquals("The request should have been for /", "/", - recordedRequests[3].getPath()); + assertEquals("/", + recordedRequests[3].getPath(), "The request should have been for /"); // renew cookie after third request fails - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[4].getPath()); + assertEquals("/_iam_session", + recordedRequests[4].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[4].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN_2)); // replay of third request - assertEquals("The request should have been for /", "/", - recordedRequests[5].getPath()); + assertEquals("/", + recordedRequests[5].getPath(), "The request should have been for /"); // The (new) cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", recordedRequests[5].getHeader("Cookie"), @@ -274,14 +287,16 @@ public void iamTokenAndCookieWithExpirySuccessful() throws Exception { // assert that there were 2 calls RecordedRequest[] recordedIamRequests = takeN(mockIamServer, 2); // first time, automatically fetch because cookie jar is empty - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[0].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[0].getPath(), "The request should have been for " + + "/identity/token"); assertThat("The request body should contain the IAM API key", recordedIamRequests[0].getBody().readString(Charset.forName("UTF-8")), - containsString("apikey="+iamApiKey)); + containsString("apikey=" + iamApiKey)); // second time, refresh because the cloudant session cookie has expired - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[1].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[1].getPath(), "The request should have been for " + + "/identity/token"); } /** @@ -291,7 +306,8 @@ public void iamTokenAndCookieWithExpirySuccessful() throws Exception { * - GET now proceeds as normal, expected cookie value is sent in header * - second GET on cloudant server, re-using session cookie * - third GET on cloudant server, cookie expired, subsequent IAM token fails, no more requests - * are made + * are made + * * @throws Exception */ @Test @@ -316,10 +332,10 @@ public void iamRenewalFailureOnIamToken() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response); + assertEquals(hello, response, "The expected response should be received"); String response2 = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response2); + assertEquals(hello, response2, "The expected response should be received"); // this never gets a response because the token failure stops the playback - this is // correct because the underlying stream has now been closed but the exception is a bit @@ -335,39 +351,41 @@ public void iamRenewalFailureOnIamToken() throws Exception { // assert that there were 4 calls RecordedRequest[] recordedRequests = takeN(mockWebServer, 4); - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[0].getPath()); + assertEquals("/_iam_session", + recordedRequests[0].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[0].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN)); // first request - assertEquals("The request should have been for /", "/", - recordedRequests[1].getPath()); + assertEquals("/", + recordedRequests[1].getPath(), "The request should have been for /"); // The cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", recordedRequests[1].getHeader("Cookie"), anyOf(containsString(iamSession(EXPECTED_OK_COOKIE)), containsString(iamSessionUnquoted(EXPECTED_OK_COOKIE)))); // second request - assertEquals("The request should have been for /", "/", - recordedRequests[2].getPath()); + assertEquals("/", + recordedRequests[2].getPath(), "The request should have been for /"); // third request, will be rejected due to cookie expiry - assertEquals("The request should have been for /", "/", - recordedRequests[3].getPath()); + assertEquals("/", + recordedRequests[3].getPath(), "The request should have been for /"); // iam mock server // assert that there were 2 calls RecordedRequest[] recordedIamRequests = takeN(mockIamServer, 2); // first time, automatically fetch because cookie jar is empty - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[0].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[0].getPath(), "The request should have been for " + + "/identity/token"); assertThat("The request body should contain the IAM API key", recordedIamRequests[0].getBody().readString(Charset.forName("UTF-8")), - containsString("apikey="+iamApiKey)); + containsString("apikey=" + iamApiKey)); // second time, refresh (but gets 500) because the cloudant session cookie has expired - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[1].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[1].getPath(), "The request should have been for " + + "/identity/token"); } /** @@ -377,7 +395,8 @@ public void iamRenewalFailureOnIamToken() throws Exception { * - GET now proceeds as normal, expected cookie value is sent in header * - second GET on cloudant server, re-using session cookie * - third GET on cloudant server, cookie expired, get IAM token, subsequent session cookie - * request fails, no more requests are made + * request fails, no more requests are made + * * @throws Exception */ @Test @@ -403,10 +422,10 @@ public void iamRenewalFailureOnSessionCookie() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response); + assertEquals(hello, response, "The expected response should be received"); String response2 = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response2); + assertEquals(hello, response2, "The expected response should be received"); // this never gets a response because the token failure stops the playback - this is // correct because the underlying stream has now been closed but the exception is a bit @@ -422,28 +441,28 @@ public void iamRenewalFailureOnSessionCookie() throws Exception { // assert that there were 5 calls RecordedRequest[] recordedRequests = takeN(mockWebServer, 5); - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[0].getPath()); + assertEquals("/_iam_session", + recordedRequests[0].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[0].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN)); // first request - assertEquals("The request should have been for /", "/", - recordedRequests[1].getPath()); + assertEquals("/", + recordedRequests[1].getPath(), "The request should have been for /"); // The cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", recordedRequests[1].getHeader("Cookie"), anyOf(containsString(iamSession(EXPECTED_OK_COOKIE)), containsString(iamSessionUnquoted(EXPECTED_OK_COOKIE)))); // second request - assertEquals("The request should have been for /", "/", - recordedRequests[2].getPath()); + assertEquals("/", + recordedRequests[2].getPath(), "The request should have been for /"); // third request, will be rejected due to cookie expiry - assertEquals("The request should have been for /", "/", - recordedRequests[3].getPath()); + assertEquals("/", + recordedRequests[3].getPath(), "The request should have been for /"); // try to renew cookie but get 500 - assertEquals("The request should have been for /_iam_session", "/_iam_session", - recordedRequests[4].getPath()); + assertEquals("/_iam_session", + recordedRequests[4].getPath(), "The request should have been for /_iam_session"); assertThat("The request body should contain the IAM token", recordedRequests[4].getBody().readString(Charset.forName("UTF-8")), containsString(IAM_TOKEN_2)); @@ -453,14 +472,16 @@ public void iamRenewalFailureOnSessionCookie() throws Exception { // assert that there were 2 calls RecordedRequest[] recordedIamRequests = takeN(mockIamServer, 2); // first time, automatically fetch because cookie jar is empty - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[0].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[0].getPath(), "The request should have been for " + + "/identity/token"); assertThat("The request body should contain the IAM API key", recordedIamRequests[0].getBody().readString(Charset.forName("UTF-8")), - containsString("apikey="+iamApiKey)); + containsString("apikey=" + iamApiKey)); // second time, refresh because the cloudant session cookie has expired - assertEquals("The request should have been for /identity/token", iamTokenEndpoint, - recordedIamRequests[1].getPath()); + assertEquals(iamTokenEndpoint, + recordedIamRequests[1].getPath(), "The request should have been for " + + "/identity/token"); } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/HttpProxyTest.java b/cloudant-client/src/test/java/com/cloudant/tests/HttpProxyTest.java index 93ef2a03d..f1330e8a0 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/HttpProxyTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/HttpProxyTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,20 +14,27 @@ package com.cloudant.tests; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.cloudant.client.api.ClientBuilder; import com.cloudant.client.api.CloudantClient; import com.cloudant.http.Http; -import com.cloudant.tests.util.MockWebServerResources; +import com.cloudant.tests.extensions.MockWebServerExtension; import com.cloudant.tests.util.HttpFactoryParameterizedTest; +import com.cloudant.tests.util.MockWebServerResources; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.HttpProxyServerBootstrap; import org.littleshoot.proxy.ProxyAuthenticator; @@ -43,52 +50,106 @@ import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.URL; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import javax.net.ssl.SSLEngine; - +@ExtendWith(HttpProxyTest.ParameterProvider.class) public class HttpProxyTest extends HttpFactoryParameterizedTest { - @Parameterized.Parameters(name = "okhttp: {0}; secure proxy: {1}; https server: {2}; proxy " + - "auth: {3}") - public static List combinations() { - boolean[] tf = new boolean[]{true, false}; - List combos = new ArrayList(); - for (boolean okUsable : tf) { - for (boolean secureProxy : new boolean[]{false}) { - // AFAICT there is no way to instruct HttpURLConnection to connect via SSL to a - // proxy server - so for now we just test an unencrypted proxy. - // Note this is independent of the SSL tunnelling to an https server and influences - // only requests between client and proxy. With an https server client requests are - // tunnelled directly to the https server, other than the original HTTP CONNECT - // request. The reason for using a SSL proxy would be to encrypt proxy auth creds - // but it appears this scenario is not readily supported. - for (boolean httpsServer : tf) { - for (boolean proxyAuth : tf) { - combos.add(new Object[]{okUsable, secureProxy, httpsServer, proxyAuth}); - } - } - } + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; } - return combos; - } - - // Note Parameter(0) okUsable is inherited - @Parameterized.Parameter(1) - public boolean secureProxy; + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + + // AFAICT there is no way to instruct HttpURLConnection to connect via SSL to a + // proxy server - so for now we just test an unencrypted proxy. + // Note this is independent of the SSL tunnelling to an https server and influences + // only requests between client and proxy. With an https server client requests are + // tunnelled directly to the https server, other than the original HTTP CONNECT + // request. The reason for using a SSL proxy would be to encrypt proxy auth creds + // but it appears this scenario is not readily supported. + // TODO is better to list all of these explicitly or not? + return Stream.of( + invocationContext(true, false, true, true), + invocationContext(true, false, true, false), + invocationContext(true, false, false, true), + // see also https://github.com/cloudant/java-cloudant/issues/423 - these + // tests current fail regardless of ordering + //invocationContext(true, false, false, false), + //invocationContext(false, false, true, true), + invocationContext(false, false, true, false), + invocationContext(false, false, false, true), + invocationContext(false, false, false, false)); + } - @Parameterized.Parameter(2) - public boolean useHttpsServer; + public static TestTemplateInvocationContext invocationContext(final boolean okUsable, + final boolean useSecureProxy, + final boolean useHttpsServer, + final boolean useProxyAuth) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("okhttp: %s; secure proxy: %s; https server %s: proxy " + + "auth: %s", + okUsable, useSecureProxy, useHttpsServer, useProxyAuth); + } - @Parameterized.Parameter(3) - public boolean useProxyAuth; + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals + (boolean.class); + case 1: + return parameterContext.getParameter().getType().equals + (boolean.class); + case 2: + return parameterContext.getParameter().getType().equals + (boolean.class); + case 3: + return parameterContext.getParameter().getType().equals + (boolean.class); + } + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return okUsable; + case 1: + return useSecureProxy; + case 2: + return useHttpsServer; + case 3: + return useProxyAuth; + } + return null; + } + }); + } + }; + } + } - @Rule - public MockWebServer server = new MockWebServer(); + @RegisterExtension + public MockWebServerExtension serverExt = new MockWebServerExtension(); + public MockWebServer server; HttpProxyServer proxy; String mockProxyUser = "alpha"; @@ -106,8 +167,12 @@ public static List combinations() { * * @throws Exception */ - @Before - public void setupMockServerSSLIfNeeded() throws Exception { + @BeforeEach + public void setupMockServerSSLIfNeeded(final boolean okUsable, + final boolean useSecureProxy, + final boolean useHttpsServer, + final boolean useProxyAuth) throws Exception { + server = serverExt.get(); if (useHttpsServer) { server.useHttps(MockWebServerResources.getSSLSocketFactory(), false); } @@ -119,8 +184,11 @@ public void setupMockServerSSLIfNeeded() throws Exception { * * @throws Exception */ - @Before - public void setupAndStartProxy() throws Exception { + @BeforeEach + public void setupAndStartProxy(final boolean okUsable, + final boolean useSecureProxy, + final boolean useHttpsServer, + final boolean useProxyAuth) throws Exception { HttpProxyServerBootstrap proxyBoostrap = DefaultHttpProxyServer.bootstrap() .withAllowLocalOnly(true) // only run on localhost @@ -142,7 +210,7 @@ public String getRealm() { proxyBoostrap.withProxyAuthenticator(pa); } - if (secureProxy) { + if (useSecureProxy) { proxyBoostrap.withSslEngineSource(new SslEngineSource() { @Override public SSLEngine newSslEngine() { @@ -166,7 +234,7 @@ public SSLEngine newSslEngine(String peerHost, int peerPort) { * * @throws Exception */ - @After + @AfterEach public void shutdownProxy() throws Exception { proxy.stop(); } @@ -180,18 +248,22 @@ public void shutdownProxy() throws Exception { * administrators in accordance with their environment. For the purposes of this test we can * add and remove the Authenticator before and after testing. */ - @Before - public void setAuthenticatorIfNeeded() { + @BeforeEach + public void setAuthenticatorIfNeeded(final boolean okUsable, + final boolean useSecureProxy, + final boolean useHttpsServer, + final boolean useProxyAuth) { // If we are not using okhttp and we have an https server and a proxy that needs auth then // we need to set the default Authenticator - if (useProxyAuth && useHttpsServer && !okUsable) { + if (useProxyAuth && useHttpsServer && !isOkUsable) { // Allow https tunnelling through http proxy for the duration of the test System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { if (getRequestorType() == RequestorType.PROXY) { - return new PasswordAuthentication(mockProxyUser, mockProxyPass.toCharArray()); + return new PasswordAuthentication(mockProxyUser, mockProxyPass + .toCharArray()); } else { return null; } @@ -203,13 +275,16 @@ protected PasswordAuthentication getPasswordAuthentication() { /** * Reset the Authenticator after the test. * - * @see #setAuthenticatorIfNeeded() + * @see #setAuthenticatorIfNeeded(boolean, boolean, boolean, boolean) */ - @After - public void resetAuthenticator() { + @AfterEach + public void resetAuthenticator(final boolean okUsable, + final boolean useSecureProxy, + final boolean useHttpsServer, + final boolean useProxyAuth) { // If we are not using okhttp and we have an https server and a proxy that needs auth then // we need to set the default Authenticator - if (useProxyAuth && useHttpsServer && !okUsable) { + if (useProxyAuth && useHttpsServer && !isOkUsable) { Authenticator.setDefault(null); // Reset the disabled schemes property System.setProperty("jdk.http.auth.tunneling.disabledSchemes", defaultDisabledList); @@ -219,8 +294,11 @@ public void resetAuthenticator() { /** * This test validates that a request can successfully traverse a proxy to our mock server. */ - @Test - public void proxiedRequest() throws Exception { + @TestTemplate + public void proxiedRequest(final boolean okUsable, + final boolean useSecureProxy, + final boolean useHttpsServer, + final boolean useProxyAuth) throws Exception { //mock a 200 OK server.setDispatcher(new Dispatcher() { @@ -231,7 +309,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio }); InetSocketAddress address = proxy.getListenAddress(); - URL proxyUrl = new URL((secureProxy) ? "https" : "http", address.getHostName(), address + URL proxyUrl = new URL((useSecureProxy) ? "https" : "http", address.getHostName(), address .getPort(), "/"); ClientBuilder builder = CloudantClientHelper.newMockWebServerClientBuilder(server) .proxyURL(proxyUrl); @@ -244,7 +322,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio String response = client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); //if it wasn't a 20x then an exception should have been thrown by now RecordedRequest request = server.takeRequest(10, TimeUnit.SECONDS); diff --git a/cloudant-client/src/test/java/com/cloudant/tests/HttpTest.java b/cloudant-client/src/test/java/com/cloudant/tests/HttpTest.java index 38e14782d..7e85e1f99 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/HttpTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/HttpTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017 IBM Corp. All rights reserved. + * Copyright © 2017, 2018 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 @@ -16,14 +16,15 @@ import static com.cloudant.tests.util.MockWebServerResources.EXPECTED_OK_COOKIE; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.cloudant.client.api.ClientBuilder; import com.cloudant.client.api.CloudantClient; @@ -38,8 +39,10 @@ import com.cloudant.http.interceptors.Replay429Interceptor; import com.cloudant.http.internal.interceptors.CookieInterceptor; import com.cloudant.test.main.RequiresCloudant; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.extensions.CloudantClientExtension; +import com.cloudant.tests.extensions.DatabaseExtension; +import com.cloudant.tests.extensions.MockWebServerExtension; +import com.cloudant.tests.extensions.MultiExtension; import com.cloudant.tests.util.HttpFactoryParameterizedTest; import com.cloudant.tests.util.MockWebServerResources; import com.cloudant.tests.util.TestTimer; @@ -51,12 +54,17 @@ import com.google.gson.JsonPrimitive; import org.apache.commons.io.IOUtils; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.junit.jupiter.api.function.Executable; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; @@ -74,30 +82,87 @@ import java.net.URLClassLoader; import java.net.URLDecoder; import java.nio.charset.Charset; +import java.util.Collections; +import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import java.util.stream.Stream; +@ExtendWith(HttpTest.ParameterProvider.class) public class HttpTest extends HttpFactoryParameterizedTest { + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } + + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + return Stream.of(invocationContext(false), + invocationContext(true)); + } + + public static TestTemplateInvocationContext invocationContext(final boolean okUsable) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("okUsable:%s", okUsable); + } + + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals + (boolean.class); + } + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return okUsable; + } + return null; + } + }); + } + }; + } + } + private String data = "{\"hello\":\"world\"}"; - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - @Rule - public MockWebServer mockWebServer = new MockWebServer(); + public static CloudantClientExtension clientResource = new CloudantClientExtension(); + public static DatabaseExtension.PerClass dbResource = new DatabaseExtension.PerClass + (clientResource); + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + + @RegisterExtension + public static MultiExtension extensions = new MultiExtension(clientResource, dbResource, + mockWebServerExt); - @Parameterized.Parameters(name = "Using okhttp: {0}") - public static Object[] okUsable() { - return new Object[]{true, false}; + public MockWebServer mockWebServer; + + @BeforeEach + public void beforeEach() { + mockWebServer = mockWebServerExt.get(); } /* * Basic test that we can write a document body by POSTing to a known database */ - @Test + @TestTemplate public void testWriteToServerOk() throws Exception { HttpConnection conn = new HttpConnection("POST", new URL(dbResource.getDbURIWithUserInfo()), "application/json"); @@ -111,21 +176,22 @@ public void testWriteToServerOk() throws Exception { // Consume response stream String responseStr = response.responseAsString(); String okPattern = ".*\"ok\"\\s*:\\s*true.*"; - assertTrue("There should be an ok response: " + responseStr, Pattern.compile(okPattern, - Pattern.DOTALL).matcher(responseStr).matches()); + assertTrue(Pattern.compile(okPattern, + Pattern.DOTALL).matcher(responseStr).matches(), "There should be an ok response: " + + "" + responseStr); // stream was read to end assertEquals(0, bis.available()); - assertEquals("Should be a 2XX response code", 2, response.getConnection().getResponseCode - () / 100); + assertEquals(2, response.getConnection().getResponseCode + () / 100, "Should be a 2XX response code"); } /* * Basic test to check that an IOException is thrown when we attempt to get the response * without first calling execute() */ - @Test + @TestTemplate public void testReadBeforeExecute() throws Exception { HttpConnection conn = new HttpConnection("POST", new URL(dbResource.getDbURIWithUserInfo()), "application/json"); @@ -154,8 +220,8 @@ public void testReadBeforeExecute() throws Exception { // security settings, the database *must* not be public, it *must* // be named cookie_test // - @Test - @Category(RequiresCloudant.class) + @TestTemplate + @RequiresCloudant public void testCookieAuthWithoutRetry() throws IOException { CookieInterceptor interceptor = new CookieInterceptor(CloudantClientHelper.COUCH_USERNAME, CloudantClientHelper.COUCH_PASSWORD, clientResource.get().getBaseUri().toString()); @@ -190,7 +256,7 @@ public void testCookieAuthWithoutRetry() throws IOException { } } - @Test + @TestTemplate public void testCookieAuthWithPath() throws Exception { MockWebServer mockWebServer = new MockWebServer(); mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); @@ -211,8 +277,8 @@ public void testCookieAuthWithPath() throws Exception { * will complete with a response code of 200. The response input stream * is expected to hold the newly created document's id and rev. */ - @Test - @Category(RequiresCloudant.class) + @TestTemplate + @RequiresCloudant public void testBasicAuth() throws IOException { BasicAuthInterceptor interceptor = new BasicAuthInterceptor(CloudantClientHelper.COUCH_USERNAME @@ -255,7 +321,7 @@ public void testBasicAuth() throws IOException { * * @throws Exception */ - @Test + @TestTemplate public void cookieInterceptorURLEncoding() throws Exception { final String mockUser = "myStrangeUsername=&?"; String mockPass = "?&=NotAsStrangeInAPassword"; @@ -270,20 +336,17 @@ public void cookieInterceptorURLEncoding() throws Exception { .build(); //the GET request will try to get a session, then perform the GET String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); RecordedRequest r = MockWebServerResources.takeRequestWithTimeout(mockWebServer); String sessionRequestContent = r.getBody().readString(Charset.forName("UTF-8")); - assertNotNull("The _session request should have non-null content", - sessionRequestContent); + assertNotNull(sessionRequestContent, "The _session request should have non-null content"); //expecting name=...&password=... String[] parts = Utils.splitAndAssert(sessionRequestContent, "&", 1); String username = URLDecoder.decode(Utils.splitAndAssert(parts[0], "=", 1)[1], "UTF-8"); - assertEquals("The username URL decoded username should match", mockUser, - username); + assertEquals(mockUser, username, "The username URL decoded username should match"); String password = URLDecoder.decode(Utils.splitAndAssert(parts[1], "=", 1)[1], "UTF-8"); - assertEquals("The username URL decoded password should match", mockPass, - password); + assertEquals(mockPass, password, "The username URL decoded password should match"); } /** @@ -292,7 +355,7 @@ public void cookieInterceptorURLEncoding() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void cookieRenewal() throws Exception { final String hello = "{\"hello\":\"world\"}\r\n"; final String renewalCookieToken = @@ -314,28 +377,29 @@ public void cookieRenewal() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response should be received", hello, response); + assertEquals(hello, response, "The expected response should be received"); // assert that there were 2 calls - assertEquals("The server should have received 2 requests", 2, mockWebServer - .getRequestCount()); + assertEquals(2, mockWebServer + .getRequestCount(), "The server should have received 2 requests"); - assertEquals("The request should have been for /_session", "/_session", - MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath()); - assertEquals("The request should have been for /", "/", - MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath()); + assertEquals("/_session", MockWebServerResources.takeRequestWithTimeout(mockWebServer) + .getPath(), "The request should have been for /_session"); + assertEquals("/", MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath(), + "The request should have been for /"); String secondResponse = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response" + secondResponse, - secondResponse.isEmpty()); + assertTrue( + secondResponse.isEmpty(), "There should be no response body on the mock response" + + secondResponse); // also assert that there were 3 calls - assertEquals("The server should have received 3 requests", 3, mockWebServer - .getRequestCount()); + assertEquals(3, mockWebServer + .getRequestCount(), "The server should have received 3 requests"); // this is the request that should have the new cookie. RecordedRequest request = MockWebServerResources.takeRequestWithTimeout(mockWebServer); - assertEquals("The request should have been for path /", "/", request.getPath()); + assertEquals("/", request.getPath(), "The request should have been for path /"); String headerValue = request.getHeader("Cookie"); // The cookie may or may not have the session id quoted, so check both assertThat("The Cookie header should contain the expected session value", headerValue, @@ -350,7 +414,7 @@ public void cookieRenewal() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void cookie403Renewal() throws Exception { // Test for a 403 with expired credentials, should result in 4 requests @@ -364,7 +428,7 @@ public void cookie403Renewal() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void handleNonExpiry403() throws Exception { // Test for a non-expiry 403, expect 2 requests @@ -376,7 +440,7 @@ public void handleNonExpiry403() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void handleNonExpiry403NoReason() throws Exception { // Test for a non-expiry 403, expect 2 requests @@ -388,7 +452,7 @@ public void handleNonExpiry403NoReason() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void handleNonExpiry403NullReason() throws Exception { // Test for a non-expiry 403, expect 2 requests @@ -438,23 +502,24 @@ private void basic403Test(String error, String reason, int expectedRequests) thr // _session followed by a replay of GET try { String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); if (!error.equals("credentials_expired")) { fail("A 403 not due to cookie expiry should result in a CouchDbException"); } } catch (CouchDbException e) { - assertEquals("The exception error should be the expected message", error, e.getError()); - assertEquals("The exception reason should be the expected message", reason, e - .getReason()); + assertEquals(error, e.getError(), "The exception error should be the expected message"); + assertEquals(reason, e + .getReason(), "The exception reason should be the expected message"); } // also assert that there were the correct number of calls - assertEquals("The server should receive the expected number of requests", + assertEquals( expectedRequests, mockWebServer - .getRequestCount()); + .getRequestCount(), "The server should receive the expected number of " + + "requests"); } - @Test + @TestTemplate public void inputStreamRetryString() throws Exception { HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); String content = "abcde"; @@ -462,7 +527,7 @@ public void inputStreamRetryString() throws Exception { testInputStreamRetry(request, content.getBytes("UTF-8")); } - @Test + @TestTemplate public void inputStreamRetryBytes() throws Exception { HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); byte[] content = "abcde".getBytes("UTF-8"); @@ -500,7 +565,7 @@ public boolean markSupported() { } } - @Test + @TestTemplate public void inputStreamRetry() throws Exception { HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); final byte[] content = "abcde".getBytes("UTF-8"); @@ -509,7 +574,7 @@ public void inputStreamRetry() throws Exception { testInputStreamRetry(request, content); } - @Test + @TestTemplate public void inputStreamRetryWithLength() throws Exception { HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); final byte[] content = "abcde".getBytes("UTF-8"); @@ -532,7 +597,7 @@ public InputStream getInputStream() throws IOException { } } - @Test + @TestTemplate public void inputStreamRetryGenerator() throws Exception { HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); byte[] content = "abcde".getBytes("UTF-8"); @@ -540,7 +605,7 @@ public void inputStreamRetryGenerator() throws Exception { testInputStreamRetry(request, content); } - @Test + @TestTemplate public void inputStreamRetryGeneratorWithLength() throws Exception { HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); byte[] content = "abcde".getBytes("UTF-8"); @@ -565,7 +630,9 @@ private void testInputStreamRetry(HttpConnection request, byte[] expectedContent context.replayRequest = true; // Close the error stream InputStream errors = context.connection.getConnection().getErrorStream(); - if (errors != null) IOUtils.closeQuietly(errors); + if (errors != null) { + IOUtils.closeQuietly(errors); + } } } catch (IOException e) { e.printStackTrace(); @@ -576,23 +643,24 @@ private void testInputStreamRetry(HttpConnection request, byte[] expectedContent }).build().executeRequest(request); String responseStr = response.responseAsString(); - assertTrue("There should be no response body on the mock response", responseStr.isEmpty()); - assertEquals("The final response code should be 200", 200, response.getConnection() - .getResponseCode()); + assertTrue(responseStr.isEmpty(), "There should be no response body on the mock response"); + assertEquals(200, response.getConnection() + .getResponseCode(), "The final response code should be 200"); // We want the second request - assertEquals("There should have been two requests", 2, mockWebServer.getRequestCount()); + assertEquals(2, mockWebServer.getRequestCount(), "There should have been two requests"); MockWebServerResources.takeRequestWithTimeout(mockWebServer); RecordedRequest rr = MockWebServerResources.takeRequestWithTimeout(mockWebServer); - assertNotNull("The request should have been recorded", rr); + assertNotNull(rr, "The request should have been recorded"); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream((int) rr .getBodySize()); rr.getBody().copyTo(byteArrayOutputStream); - assertArrayEquals("The body bytes should have matched after a retry", expectedContent, - byteArrayOutputStream.toByteArray()); + assertArrayEquals(expectedContent, + byteArrayOutputStream.toByteArray(), "The body bytes should have matched after a " + + "retry"); } - @Test + @TestTemplate public void testCookieRenewOnPost() throws Exception { mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); @@ -610,11 +678,11 @@ public void testCookieRenewOnPost() throws Exception { request.setRequestBody("{\"some\": \"json\"}"); HttpConnection response = c.executeRequest(request); String responseStr = response.responseAsString(); - assertTrue("There should be no response body on the mock response", responseStr.isEmpty()); + assertTrue(responseStr.isEmpty(), "There should be no response body on the mock response"); response.getConnection().getResponseCode(); } - @Test + @TestTemplate public void testCustomHeader() throws Exception { mockWebServer.enqueue(new MockResponse()); final String headerName = "Test-Header"; @@ -630,12 +698,12 @@ public void testCustomHeader() throws Exception { } }).build(); client.getAllDbs(); - assertEquals("There should have been 1 request", 1, mockWebServer.getRequestCount()); + assertEquals(1, mockWebServer.getRequestCount(), "There should have been 1 request"); RecordedRequest request = MockWebServerResources.takeRequestWithTimeout(mockWebServer); - assertNotNull("The recorded request should not be null", request); - assertNotNull("The custom header should have been present", request.getHeader(headerName)); - assertEquals("The custom header should have the specified value", headerValue, request - .getHeader(headerName)); + assertNotNull(request, "The recorded request should not be null"); + assertNotNull(request.getHeader(headerName), "The custom header should have been present"); + assertEquals(headerValue, request + .getHeader(headerName), "The custom header should have the specified value"); } /** @@ -643,7 +711,7 @@ public void testCustomHeader() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void testChunking() throws Exception { mockWebServer.enqueue(new MockResponse()); final int chunkSize = 1024 * 8; @@ -655,20 +723,20 @@ public void testChunking() throws Exception { "text/plain") .setRequestBody(new RandomInputStreamGenerator(chunks * chunkSize))) .responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); - assertEquals("There should have been 1 request", 1, mockWebServer - .getRequestCount()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); + assertEquals(1, mockWebServer + .getRequestCount(), "There should have been 1 request"); RecordedRequest request = MockWebServerResources.takeRequestWithTimeout(mockWebServer); - assertNotNull("The recorded request should not be null", request); - assertNull("There should be no Content-Length header", request.getHeader("Content-Length")); - assertEquals("The Transfer-Encoding should be chunked", "chunked", request.getHeader - ("Transfer-Encoding")); + assertNotNull(request, "The recorded request should not be null"); + assertNull(request.getHeader("Content-Length"), "There should be no Content-Length header"); + assertEquals("chunked", request.getHeader + ("Transfer-Encoding"), "The Transfer-Encoding should be chunked"); // It would be nice to assert that we got the chunk sizes we were expecting, but sadly the // HttpURLConnection and ChunkedOutputStream only use the chunkSize as a suggestion and seem // to use the buffer size instead. The best assertion we can make is that we did receive // multiple chunks. - assertTrue("There should have been at least 2 chunks", request.getChunkSizes().size() > 1); + assertTrue(request.getChunkSizes().size() > 1, "There should have been at least 2 chunks"); } private static final class RandomInputStreamGenerator implements HttpConnection @@ -692,7 +760,7 @@ public InputStream getInputStream() throws IOException { * * @throws Exception */ - @Test + @TestTemplate public void test429Backoff() throws Exception { mockWebServer.enqueue(MockWebServerResources.get429()); mockWebServer.enqueue(new MockResponse()); @@ -701,9 +769,9 @@ public void test429Backoff() throws Exception { .interceptors(Replay429Interceptor.WITH_DEFAULTS) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); - assertEquals("There should be 2 requests", 2, mockWebServer.getRequestCount()); + assertEquals(2, mockWebServer.getRequestCount(), "There should be 2 requests"); } /** @@ -712,7 +780,7 @@ public void test429Backoff() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void test429BackoffMaxDefault() throws Exception { // Always respond 429 for this test @@ -728,10 +796,10 @@ public void test429BackoffMaxDefault() throws Exception { } catch (TooManyRequestsException e) { long duration = t.stopTimer(TimeUnit.MILLISECONDS); // 3 backoff periods for 4 attempts: 250 + 500 + 1000 = 1750 ms - assertTrue("The duration should be at least 1750 ms, but was " + duration, duration >= - 1750); - assertEquals("There should be 4 request attempts", 4, mockWebServer - .getRequestCount()); + assertTrue(duration >= + 1750, "The duration should be at least 1750 ms, but was " + duration); + assertEquals(4, mockWebServer + .getRequestCount(), "There should be 4 request attempts"); } } @@ -741,7 +809,7 @@ public void test429BackoffMaxDefault() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void test429BackoffMaxConfigured() throws Exception { // Always respond 429 for this test @@ -758,10 +826,10 @@ public void test429BackoffMaxConfigured() throws Exception { } catch (TooManyRequestsException e) { long duration = t.stopTimer(TimeUnit.MILLISECONDS); // 9 backoff periods for 10 attempts: 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 = 511 ms - assertTrue("The duration should be at least 511 ms, but was " + duration, duration >= - 511); - assertEquals("There should be 10 request attempts", 10, mockWebServer - .getRequestCount()); + assertTrue(duration >= + 511, "The duration should be at least 511 ms, but was " + duration); + assertEquals(10, mockWebServer + .getRequestCount(), "There should be 10 request attempts"); } } @@ -770,7 +838,7 @@ public void test429BackoffMaxConfigured() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void test429BackoffMaxMoreThanRetriesAllowed() throws Exception { // Always respond 429 for this test @@ -784,8 +852,8 @@ public void test429BackoffMaxMoreThanRetriesAllowed() throws Exception { .responseAsString(); fail("There should be a TooManyRequestsException instead had response " + response); } catch (TooManyRequestsException e) { - assertEquals("There should be 3 request attempts", 3, mockWebServer - .getRequestCount()); + assertEquals(3, mockWebServer + .getRequestCount(), "There should be 3 request attempts"); } } @@ -795,7 +863,7 @@ public void test429BackoffMaxMoreThanRetriesAllowed() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void test429BackoffRetryAfter() throws Exception { mockWebServer.enqueue(MockWebServerResources.get429().addHeader("Retry-After", "1")); @@ -806,16 +874,16 @@ public void test429BackoffRetryAfter() throws Exception { .interceptors(Replay429Interceptor.WITH_DEFAULTS) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); long duration = t.stopTimer(TimeUnit.MILLISECONDS); - assertTrue("The duration should be at least 1000 ms, but was " + duration, duration >= - 1000); - assertEquals("There should be 2 request attempts", 2, mockWebServer - .getRequestCount()); + assertTrue(duration >= + 1000, "The duration should be at least 1000 ms, but was " + duration); + assertEquals(2, mockWebServer + .getRequestCount(), "There should be 2 request attempts"); } - @Test + @TestTemplate public void test429IgnoreRetryAfter() throws Exception { mockWebServer.enqueue(MockWebServerResources.get429().addHeader("Retry-After", "1")); mockWebServer.enqueue(new MockResponse()); @@ -826,13 +894,13 @@ public void test429IgnoreRetryAfter() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); long duration = t.stopTimer(TimeUnit.MILLISECONDS); - assertTrue("The duration should be less than 1000 ms, but was " + duration, duration < - 1000); - assertEquals("There should be 2 request attempts", 2, mockWebServer - .getRequestCount()); + assertTrue(duration < + 1000, "The duration should be less than 1000 ms, but was " + duration); + assertEquals(2, mockWebServer + .getRequestCount(), "There should be 2 request attempts"); } /** @@ -840,7 +908,7 @@ public void test429IgnoreRetryAfter() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void testHttpConnectionRetries() throws Exception { // Just return 200 OK mockWebServer.setDispatcher(new MockWebServerResources.ConstantResponseDispatcher(200)); @@ -865,10 +933,10 @@ public void testHttpConnectionRetries() throws Exception { String response = c.executeRequest(Http.GET(c.getBaseUri()).setNumberOfRetries(5)) .responseAsString(); - assertTrue("There should be no response body on the mock response", response.isEmpty()); + assertTrue(response.isEmpty(), "There should be no response body on the mock response"); - assertEquals("There should be 5 request attempts", 5, mockWebServer - .getRequestCount()); + assertEquals(5, mockWebServer + .getRequestCount(), "There should be 5 request attempts"); } /** @@ -877,15 +945,23 @@ public void testHttpConnectionRetries() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @TestTemplate public void httpsProxyIllegalArgumentException() throws Exception { - // Get a client pointing to an https proxy - CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) - .proxyURL(new URL("https://192.0.2.0")).build(); + assertThrows(IllegalArgumentException.class, new Executable() { + @Override + public void execute() throws Throwable { + + // Get a client pointing to an https proxy + CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder + (mockWebServer) + .proxyURL(new URL("https://192.0.2.0")).build(); - String response = client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); - fail("There should be an IllegalStateException for an https proxy."); + String response = client.executeRequest(Http.GET(client.getBaseUri())) + .responseAsString(); + fail("There should be an IllegalStateException for an https proxy."); + } + }); } /** @@ -894,7 +970,7 @@ public void httpsProxyIllegalArgumentException() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void cookieAppliedToDifferentURL() throws Exception { mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); mockWebServer.enqueue(new MockResponse().setBody("first")); @@ -909,36 +985,37 @@ public void cookieAppliedToDifferentURL() throws Exception { URI baseURI = c.getBaseUri(); URL first = new URL(baseURI.getScheme(), baseURI.getHost(), baseURI.getPort(), "/testdb"); String response = c.executeRequest(Http.GET(first)).responseAsString(); - assertEquals("The correct response body should be present", "first", response); + assertEquals("first", response, "The correct response body should be present"); // There should be a request for a cookie followed by a the real request - assertEquals("There should be 2 requests", 2, mockWebServer.getRequestCount()); + assertEquals(2, mockWebServer.getRequestCount(), "There should be 2 requests"); - assertEquals("The first request should have been for a cookie", "/_session", - MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath()); + assertEquals("/_session", MockWebServerResources.takeRequestWithTimeout(mockWebServer) + .getPath(), "The first request should have been for a cookie"); RecordedRequest request = MockWebServerResources.takeRequestWithTimeout(mockWebServer); - assertEquals("The second request should have been for /testdb", "/testdb", - request.getPath()); - assertNotNull("There should be a cookie on the request", request.getHeader("Cookie")); + assertEquals("/testdb", + request.getPath(), "The second request should have been for /testdb"); + assertNotNull(request.getHeader("Cookie"), "There should be a cookie on the request"); // Now make a request to another URL URL second = new URL(baseURI.getScheme(), baseURI.getHost(), baseURI.getPort(), "/_all_dbs"); response = c.executeRequest(Http.GET(second)).responseAsString(); - assertEquals("The correct response body should be present", "second", response); + assertEquals("second", response, "The correct response body should be present"); // There should now be an additional request - assertEquals("There should be 3 requests", 3, mockWebServer.getRequestCount()); + assertEquals(3, mockWebServer.getRequestCount(), "There should be 3 requests"); request = MockWebServerResources.takeRequestWithTimeout(mockWebServer); - assertEquals("The second request should have been for /_all_dbs", "/_all_dbs", request - .getPath()); + assertEquals("/_all_dbs", request.getPath(), "The second request should have been for " + + "/_all_dbs"); String cookieHeader = request.getHeader("Cookie"); - assertNotNull("There should be a cookie on the request", cookieHeader); - assertTrue("The cookie header " + cookieHeader + " should contain the expected value.", - request.getHeader("Cookie").contains(EXPECTED_OK_COOKIE)); + assertNotNull(cookieHeader, "There should be a cookie on the request"); + assertTrue( + request.getHeader("Cookie").contains(EXPECTED_OK_COOKIE), "The cookie header " + + cookieHeader + " should contain the expected value."); } /** @@ -946,7 +1023,7 @@ public void cookieAppliedToDifferentURL() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void badCredsDisablesCookie() throws Exception { mockWebServer.setDispatcher(new Dispatcher() { private int counter = 0; @@ -969,25 +1046,27 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response body should be received", "TEST", response); + assertEquals("TEST", response, "The expected response body should be received"); // There should only be two requests: an initial auth failure followed by an ok response. // If the cookie interceptor keeps trying then there will be more _session requests. - assertEquals("There should be 2 requests", 2, mockWebServer.getRequestCount()); + assertEquals(2, mockWebServer.getRequestCount(), "There should be 2 requests"); - assertEquals("The first request should have been for a cookie", "/_session", - MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath()); - assertEquals("The second request should have been for /", "/", - MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath()); + assertEquals("/_session", + MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath(), "The " + + "first request should have been for a cookie"); + assertEquals("/", + MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath(), "The " + + "second request should have been for /"); response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response body should be received", "TEST", response); + assertEquals("TEST", response, "The expected response body should be received"); // Make another request, the cookie interceptor should not try again so there should only be // one more request. - assertEquals("There should be 3 requests", 3, mockWebServer.getRequestCount()); - assertEquals("The third request should have been for /", "/", - MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath()); + assertEquals(3, mockWebServer.getRequestCount(), "There should be 3 requests"); + assertEquals("/", MockWebServerResources.takeRequestWithTimeout(mockWebServer).getPath(), + "The third request should have been for /"); } /** @@ -996,20 +1075,26 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio * * @throws Exception */ - @Test(expected = CouchDbException.class) + @TestTemplate public void noErrorStream403() throws Exception { - // Respond with a cookie init to the first request to _session - mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); - // Respond to the executeRequest GET of / with a 403 with no body - mockWebServer.enqueue(new MockResponse().setResponseCode(403)); - CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) - .username("a") - .password("b") - .build(); - - String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - fail("There should be an exception, but received response " + response); + assertThrows(CouchDbException.class, new Executable() { + @Override + public void execute() throws Throwable { + + // Respond with a cookie init to the first request to _session + mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); + // Respond to the executeRequest GET of / with a 403 with no body + mockWebServer.enqueue(new MockResponse().setResponseCode(403)); + CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) + .username("a") + .password("b") + .build(); + + String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); + fail("There should be an exception, but received response " + response); + } + }); } /** @@ -1018,7 +1103,7 @@ public void noErrorStream403() throws Exception { * * @throws Exception */ - @Test + @TestTemplate public void noErrorStream401() throws Exception { // Respond with a cookie init to the first request to _session @@ -1036,7 +1121,7 @@ public void noErrorStream401() throws Exception { .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); - assertEquals("The expected response body should be received", "TEST", response); + assertEquals("TEST", response, "The expected response body should be received"); } /** @@ -1047,15 +1132,15 @@ public void noErrorStream401() throws Exception { * @throws ClassNotFoundException if the load tries to use any classes it shouldn't * @throws Exception if there is another issue in the test */ - @Test + @TestTemplate public void okUsableClassLoad() throws ClassNotFoundException, Exception { // Point to the built classes, it's a bit awkward but we need to load the class cleanly - File f = new File("../cloudant-http/build/classes/main/"); + File f = new File("../cloudant-http/build/classes/java/main/"); ClassLoader loader = new CloudantHttpIsolationClassLoader(new URL[]{f.toURI().toURL()}); Class okHelperClass = Class.forName("com.cloudant.http.internal.ok.OkHelper" , true, loader); - assertEquals("The isOkUsable value should be correct", okUsable, okHelperClass.getMethod - ("isOkUsable").invoke(null)); + assertEquals(isOkUsable, okHelperClass.getMethod + ("isOkUsable").invoke(null), "The isOkUsable value should be correct"); } /** @@ -1068,7 +1153,8 @@ public class CloudantHttpIsolationClassLoader extends URLClassLoader { public CloudantHttpIsolationClassLoader(URL[] urls) { // If we are testing okhttp then allow the parent classloader, otherwise use null // to isolate okhttp classes from the test load - super(urls, okUsable ? CloudantHttpIsolationClassLoader.class.getClassLoader() : null); + super(urls, isOkUsable ? CloudantHttpIsolationClassLoader.class.getClassLoader() : + null); } @Override @@ -1079,10 +1165,11 @@ protected Class findClass(String name) throws ClassNotFoundException { // helper - assert that _n_ requests were made on the mock server and return them in an array public static RecordedRequest[] takeN(MockWebServer server, int n) throws Exception { - assertEquals(String.format(Locale.ENGLISH, "The server should have %d received requests", n), n, - server.getRequestCount()); + assertEquals(n, + server.getRequestCount(), String.format(Locale.ENGLISH, "The server should have " + + "%d received requests", n)); RecordedRequest[] recordedRequests = new RecordedRequest[n]; - for (int i=0; iIssue 137 */ @Test - public void testDeprecatedApiNotNullIndexMovieNameAndYearWithCompleteJsonObjectStringSelector() { + public void testDeprecatedApiNotNullIndexMovieNameAndYearWithCompleteJsonObjectStringSelector + () { List movies = db.findByIndex("{\"selector\": { \"Movie_year\": {\"$gt\": 1960}, " + "\"Person_name\": \"Alec Guinness\" } }", Movie.class, @@ -134,16 +123,26 @@ public void testDeprecatedApiNotNullIndexMovieNameAndYearWithCompleteJsonObjectS } - @Test(expected = JsonParseException.class) + @Test public void invalidSelectorObjectThrowsJsonParseException() { - db.findByIndex("\"selector\"invalid", Movie.class); + assertThrows(JsonParseException.class, new Executable() { + @Override + public void execute() throws Throwable { + db.findByIndex("\"selector\"invalid", Movie.class); + } + }); } - @Test(expected = JsonParseException.class) + @Test public void invalidFieldThrowsJsonParseException() { - FindByIndexOptions findByIndexOptions = new FindByIndexOptions(); - findByIndexOptions.fields("\""); - db.findByIndex("{\"type\":\"subscription\"}", Movie.class, findByIndexOptions); + assertThrows(JsonParseException.class, new Executable() { + @Override + public void execute() throws Throwable { + FindByIndexOptions findByIndexOptions = new FindByIndexOptions(); + findByIndexOptions.fields("\""); + db.findByIndex("{\"type\":\"subscription\"}", Movie.class, findByIndexOptions); + } + }); } /* Current API tests */ @@ -180,10 +179,10 @@ public void testNotNullIndexNamesAndFields() { assertNotNull(i.getName()); assertNotNull(i.getFields()); List flds = i.getFields(); - assertTrue("The fields should not be empty", flds.size() > 0); + assertTrue(flds.size() > 0, "The fields should not be empty"); for (JsonIndex.Field field : flds) { - assertNotNull("The field name should not be null", field.getName()); - assertNotNull("The sort order should not be null", field.getOrder()); + assertNotNull(field.getName(), "The field name should not be null"); + assertNotNull(field.getOrder(), "The sort order should not be null"); } } } @@ -272,7 +271,7 @@ public void testBookmarks() { pageCount++; } } while (!moviesPage.getDocs().isEmpty()); - Assert.assertEquals(3, pageCount); + assertEquals(3, pageCount); } @@ -289,12 +288,12 @@ public void useIndexDesignDocJsonTypeIsString() throws Exception { } private void assertUseIndexString(JsonElement useIndex) throws Exception { - assertNotNull("The use_index property should not be null", useIndex); - assertTrue("The use_index property should be a JsonPrimitive", useIndex.isJsonPrimitive()); + assertNotNull(useIndex, "The use_index property should not be null"); + assertTrue(useIndex.isJsonPrimitive(), "The use_index property should be a JsonPrimitive"); JsonPrimitive useIndexPrimitive = useIndex.getAsJsonPrimitive(); - assertTrue("The use_index property should be a string", useIndexPrimitive.isString()); + assertTrue(useIndexPrimitive.isString(), "The use_index property should be a string"); String useIndexString = useIndexPrimitive.getAsString(); - assertEquals("The use_index property should be Movie_year", "Movie_year", useIndexString); + assertEquals("Movie_year", useIndexString, "The use_index property should be Movie_year"); } /** @@ -306,14 +305,14 @@ private void assertUseIndexString(JsonElement useIndex) throws Exception { public void useIndexDesignDocAndIndexNameJsonTypeIsArray() throws Exception { JsonElement useIndex = getUseIndexFromRequest(new QueryBuilder(empty()). useIndex("Movie_year", "Person_name")); - assertNotNull("The use_index property should not be null", useIndex); - assertTrue("The use_index property should be a JsonArray", useIndex.isJsonArray()); + assertNotNull(useIndex, "The use_index property should not be null"); + assertTrue(useIndex.isJsonArray(), "The use_index property should be a JsonArray"); JsonArray useIndexArray = useIndex.getAsJsonArray(); - assertEquals("The use_index array should have two elements", 2, useIndexArray.size()); - assertEquals("The use_index design document should be Movie_year", "Movie_year", - useIndexArray.get(0).getAsString()); - assertEquals("The use_index index name should be Person_name", "Person_name", - useIndexArray.get(1).getAsString()); + assertEquals(2, useIndexArray.size(), "The use_index array should have two elements"); + assertEquals("Movie_year", useIndexArray.get(0).getAsString(), "The use_index design " + + "document should be Movie_year"); + assertEquals("Person_name", useIndexArray.get(1).getAsString(), "The use_index index name" + + " should be Person_name"); } /** @@ -324,7 +323,7 @@ public void useIndexDesignDocAndIndexNameJsonTypeIsArray() throws Exception { @Test public void useIndexNotSpecified() throws Exception { JsonElement useIndex = getUseIndexFromRequest(new QueryBuilder(empty())); - assertNull("The use_index property should be null (i.e. was not specified)", useIndex); + assertNull(useIndex, "The use_index property should be null (i.e. was not specified)"); } /** diff --git a/cloudant-client/src/test/java/com/cloudant/tests/LoggingTest.java b/cloudant-client/src/test/java/com/cloudant/tests/LoggingTest.java index 77083489c..ac3c33918 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/LoggingTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/LoggingTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -14,26 +14,22 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.cloudant.client.api.ClientBuilder; import com.cloudant.client.api.CloudantClient; import com.cloudant.http.Http; import com.cloudant.http.HttpConnection; +import com.cloudant.tests.extensions.MockWebServerExtension; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import mockit.Expectations; import mockit.Mocked; -import mockit.StrictExpectations; -import mockit.Verifications; - import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -56,15 +52,17 @@ public class LoggingTest { private static final String logPrefixPattern = "[0-9,a-f]+-\\d+ "; - @ClassRule - public static MockWebServer mockWebServer = new MockWebServer(); + @RegisterExtension + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + public static MockWebServer mockWebServer; private static CloudantClient client; private volatile Logger logger; private VerificationLogHandler handler; - @BeforeClass - public static void setupMockWebServer() throws Exception { + @BeforeEach + public void setupMockWebServer() throws Exception { + mockWebServer = mockWebServerExt.get(); // Set a dispatcher that always returns 200 OK mockWebServer.setDispatcher(new Dispatcher() { @Override @@ -76,12 +74,12 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio .build(); } - @Before + @BeforeEach public void createHandler() { handler = new VerificationLogHandler(); } - @After + @AfterEach public void teardown() throws Exception { // Remove the handler from the logger logger.removeHandler(handler); @@ -94,7 +92,7 @@ public void teardown() throws Exception { public void httpLoggingEnabled() throws Exception { logger = setupLogger(HttpConnection.class, Level.ALL); client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); - assertTrue("There should be at least 1 log entry", handler.logEntries.size() > 0); + assertTrue(handler.logEntries.size() > 0, "There should be at least 1 log entry"); } @Test @@ -113,7 +111,7 @@ public void urlRegexLogging() throws Exception { .responseAsString(); // Check there were two log messages one for request and one for response - assertEquals("There should be 2 log messages", 2, handler.logEntries.size()); + assertEquals(2, handler.logEntries.size(), "There should be 2 log messages"); // Check the messages were the ones we expected assertHttpMessage("GET .*/testdb request", 0); assertHttpMessage("GET .*/testdb response 200 OK", 1); @@ -123,11 +121,12 @@ public void urlRegexLogging() throws Exception { // Make a second request to a different URL and check that nothing else was logged client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); - assertEquals("There should have been no more log entries", logsize, handler.logEntries - .size()); + assertEquals(logsize, handler.logEntries.size(), "There should have been no more log " + + "entries"); } private String methodFilterPropName = "com.cloudant.http.filter.method"; + @Test public void httpMethodFilterLogging() throws Exception { setAndAssertLogProperty(methodFilterPropName, "GET"); @@ -137,7 +136,7 @@ public void httpMethodFilterLogging() throws Exception { client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); // Check there were two log messages one for request and one for response - assertEquals("There should be 2 log messages", 2, handler.logEntries.size()); + assertEquals(2, handler.logEntries.size(), "There should be 2 log messages"); // Check the messages were the ones we expected assertHttpMessage("GET .* request", 0); assertHttpMessage("GET .* response 200 OK", 1); @@ -148,8 +147,8 @@ public void httpMethodFilterLogging() throws Exception { // Make a PUT request to a different URL and check that nothing else was logged client.executeRequest(Http.PUT(client.getBaseUri(), "text/plain").setRequestBody("")) .responseAsString(); - assertEquals("There should have been no more log entries", logsize, handler.logEntries - .size()); + assertEquals(logsize, handler.logEntries.size(), "There should have been no more log " + + "entries"); } @Test @@ -161,7 +160,7 @@ public void httpMethodFilterLoggingList() throws Exception { client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); // Check there were two log messages one for request and one for response - assertEquals("There should be 2 log messages", 2, handler.logEntries.size()); + assertEquals(2, handler.logEntries.size(), "There should be 2 log messages"); // Check the messages were the ones we expected assertHttpMessage("GET .* request", 0); assertHttpMessage("GET .* response 200 OK", 1); @@ -170,7 +169,7 @@ public void httpMethodFilterLoggingList() throws Exception { client.executeRequest(Http.PUT(client.getBaseUri(), "text/plain").setRequestBody("")) .responseAsString(); - assertEquals("There should now be 4 log messages", 4, handler.logEntries.size()); + assertEquals(4, handler.logEntries.size(), "There should now be 4 log messages"); // Check the messages were the ones we expected assertHttpMessage("PUT .* request", 2); assertHttpMessage("PUT .* response 200 OK", 3); @@ -180,7 +179,7 @@ public void httpMethodFilterLoggingList() throws Exception { public void clientBuilderLogging() throws Exception { logger = setupLogger(ClientBuilder.class, Level.CONFIG); CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer).build(); - assertEquals("There should be 5 log entries", 5, handler.logEntries.size()); + assertEquals(5, handler.logEntries.size(), "There should be 5 log entries"); // Validate each of the 5 entries are what we expect assertLogMessage("URL: .*", 0); assertLogMessage("Building client using URL: .*", 1); @@ -193,7 +192,6 @@ public void clientBuilderLogging() throws Exception { * A basic DNS log test that can be called with different values. * * @param cacheValue the value to set for the cache lifetime - * * @throws Exception if the test fails or errors */ private void basicDnsLogTest(String cacheValue) throws Exception { @@ -218,7 +216,7 @@ private void basicDnsLogTest(String cacheValue) throws Exception { public void dnsNoWarningLessThan30() throws Exception { basicDnsLogTest("29"); // Assert no warning was received - assertEquals("There should be no log entry", 0, handler.logEntries.size()); + assertEquals(0, handler.logEntries.size(), "There should be no log entry"); } /** @@ -230,7 +228,7 @@ public void dnsNoWarningLessThan30() throws Exception { public void dnsNoWarning0() throws Exception { basicDnsLogTest("0"); // Assert no warning was received - assertEquals("There should be no log entry", 0, handler.logEntries.size()); + assertEquals(0, handler.logEntries.size(), "There should be no log entry"); } /** @@ -242,7 +240,7 @@ public void dnsNoWarning0() throws Exception { public void dnsWarningForever() throws Exception { basicDnsLogTest("-1"); // Assert a warning was received - assertEquals("There should be 1 log entry", 1, handler.logEntries.size()); + assertEquals(1, handler.logEntries.size(), "There should be 1 log entry"); // Assert that it matches the expected pattern assertLogMessage("DNS cache lifetime may be too long\\. .*", 0); } @@ -256,7 +254,7 @@ public void dnsWarningForever() throws Exception { public void dnsNoWarning30() throws Exception { basicDnsLogTest("30"); // Assert no warning was received - assertEquals("There should be no log entry", 0, handler.logEntries.size()); + assertEquals(0, handler.logEntries.size(), "There should be no log entry"); } @@ -269,7 +267,7 @@ public void dnsNoWarning30() throws Exception { public void dnsWarning31() throws Exception { basicDnsLogTest("31"); // Assert a warning was received - assertEquals("There should be 1 log entry", 1, handler.logEntries.size()); + assertEquals(1, handler.logEntries.size(), "There should be 1 log entry"); // Assert that it matches the expected pattern assertLogMessage("DNS cache lifetime may be too long\\. .*", 0); } @@ -302,7 +300,7 @@ public void dnsWarningPermissionDenied(@Mocked final SecurityManager mockSecurit System.setSecurityManager(null); } // Assert a warning was received - assertEquals("There should be 1 log entry", 1, handler.logEntries.size()); + assertEquals(1, handler.logEntries.size(), "There should be 1 log entry"); // Assert that it matches the expected pattern assertLogMessage("Permission denied to check Java DNS cache TTL\\. .*", 0); } @@ -336,20 +334,19 @@ public void dnsWarningDefaultWithSecurityManager(@Mocked final SecurityManager System.setSecurityManager(null); } // Assert a warning was received - assertEquals("There should be 1 log entry", 1, handler.logEntries.size()); + assertEquals(1, handler.logEntries.size(), "There should be 1 log entry"); // Assert that it matches the expected pattern assertLogMessage("DNS cache lifetime may be too long\\. .*", 0); } /** * Set a LogManager configuration property and assert it was set correctly - * */ private void setAndAssertLogProperty(String name, String value) throws Exception { LogManager.getLogManager().readConfiguration(new ByteArrayInputStream((name + "=" + value).getBytes())); - assertEquals("The log property should be the test value", value, LogManager - .getLogManager().getProperty(name)); + assertEquals(value, LogManager.getLogManager().getProperty(name), "The log property " + + "should be the test value"); } /** @@ -361,8 +358,8 @@ private void setAndAssertLogProperty(String name, String value) throws Exception private void assertLogMessage(String pattern, int index) { Pattern p = Pattern.compile(pattern); String msg = handler.logEntries.get(index).getMessage(); - assertTrue("The log entry \"" + msg + "\" should match pattern " + pattern, p.matcher - (msg).matches()); + assertTrue(p.matcher(msg).matches(), "The log entry \"" + msg + "\" should match pattern " + + "" + pattern); } /** diff --git a/cloudant-client/src/test/java/com/cloudant/tests/QueryTests.java b/cloudant-client/src/test/java/com/cloudant/tests/QueryTests.java index fd1c99703..430c343be 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/QueryTests.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/QueryTests.java @@ -34,8 +34,8 @@ import com.cloudant.client.api.query.Sort; import com.cloudant.client.api.query.Type; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class QueryTests { @@ -43,7 +43,8 @@ public class QueryTests { @Test public void basicSelector1() { QueryBuilder qb = new QueryBuilder(eq("director", "Lars von Trier")); - Assert.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}}", + qb.build()); } // "Selector with two fields" @@ -52,21 +53,23 @@ public void basicSelector2() { QueryBuilder qb = new QueryBuilder(and( eq("name", "Paul"), eq("location", "Boston"))); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"name\": {\"$eq\": \"Paul\"}}, {\"location\": {\"$eq\": \"Boston\"}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"name\": {\"$eq\": \"Paul\"}}, " + + "{\"location\": {\"$eq\": \"Boston\"}}]}}", qb.build()); } // "SUBFIELDS" @Test public void basicSelector3() { QueryBuilder qb = new QueryBuilder(eq("imdb.rating", 8)); - Assert.assertEquals("{\"selector\": {\"imdb.rating\": {\"$eq\": 8}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"imdb.rating\": {\"$eq\": 8}}}", qb.build()); } - // "Example selector using an operator to match any document, where the age field has a value greater than 20:" + // "Example selector using an operator to match any document, where the age field has a value + // greater than 20:" @Test public void basicSelector4() { QueryBuilder qb = new QueryBuilder(gt("year", 2018)); - Assert.assertEquals("{\"selector\": {\"year\": {\"$gt\": 2018}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"year\": {\"$gt\": 2018}}}", qb.build()); } // "$and operator used with full text indexing" @@ -76,7 +79,8 @@ public void basicSelector5() { eq("$text", "Schwarzenegger"), in("year", 1984, 1991) )); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"$text\": {\"$eq\": \"Schwarzenegger\"}}, {\"year\": {\"$in\": [1984, 1991]}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"$text\": {\"$eq\": " + + "\"Schwarzenegger\"}}, {\"year\": {\"$in\": [1984, 1991]}}]}}", qb.build()); } // "$and operator used with full text indexing" @@ -86,7 +90,8 @@ public void basicSelector5_single() { eq("$text", "Schwarzenegger"), in("year", 1984) )); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"$text\": {\"$eq\": \"Schwarzenegger\"}}, {\"year\": {\"$in\": [1984]}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"$text\": {\"$eq\": " + + "\"Schwarzenegger\"}}, {\"year\": {\"$in\": [1984]}}]}}", qb.build()); } // "$or operator used with full text indexing" @@ -96,7 +101,8 @@ public void basicSelector6() { eq("director", "George Lucas"), eq("director", "Steven Spielberg") )); - Assert.assertEquals("{\"selector\": {\"$or\": [{\"director\": {\"$eq\": \"George Lucas\"}}, {\"director\": {\"$eq\": \"Steven Spielberg\"}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$or\": [{\"director\": {\"$eq\": \"George " + + "Lucas\"}}, {\"director\": {\"$eq\": \"Steven Spielberg\"}}]}}", qb.build()); } // "$or operator used with database indexed on the field "year" @@ -109,7 +115,9 @@ public void basicSelector7() { eq("director", "Steven Spielberg") ) )); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$eq\": 1977}}, {\"$or\": [{\"director\": {\"$eq\": \"George Lucas\"}}, {\"director\": {\"$eq\": \"Steven Spielberg\"}}]}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$eq\": 1977}}, " + + "{\"$or\": [{\"director\": {\"$eq\": \"George Lucas\"}}, {\"director\": {\"$eq\":" + + " \"Steven Spielberg\"}}]}]}}", qb.build()); } // "$not operator used with database indexed on the field "year"" @@ -120,7 +128,9 @@ public void basicSelector8() { lte("year", 1903), not(eq("year", 1901)) )); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gte\": 1900}}, {\"year\": {\"$lte\": 1903}}, {\"$not\": {\"year\": {\"$eq\": 1901}}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gte\": 1900}}, " + + "{\"year\": {\"$lte\": 1903}}, {\"$not\": {\"year\": {\"$eq\": 1901}}}]}}", qb + .build()); } // "$nor operator used with database indexed on the field "year"" @@ -134,28 +144,33 @@ public void basicSelector9() { eq("year", 1905), eq("year", 1907)) )); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gte\": 1900}}, {\"year\": {\"$lte\": 1910}}, {\"$nor\": [{\"year\": {\"$eq\": 1901}}, {\"year\": {\"$eq\": 1905}}, {\"year\": {\"$eq\": 1907}}]}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gte\": 1900}}, " + + "{\"year\": {\"$lte\": 1910}}, {\"$nor\": [{\"year\": {\"$eq\": 1901}}, " + + "{\"year\": {\"$eq\": 1905}}, {\"year\": {\"$eq\": 1907}}]}]}}", qb.build()); } // "$all operator used with full text indexing" @Test public void basicSelector10() { QueryBuilder qb = new QueryBuilder(all("genre", "Comedy", "Short")); - Assert.assertEquals("{\"selector\": {\"genre\": {\"$all\": [\"Comedy\", \"Short\"]}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"genre\": {\"$all\": [\"Comedy\", " + + "\"Short\"]}}}", qb.build()); } // "$all operator used with full text indexing" @Test public void basicSelector10_single() { QueryBuilder qb = new QueryBuilder(all("genre", "Comedy")); - Assert.assertEquals("{\"selector\": {\"genre\": {\"$all\": [\"Comedy\"]}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"genre\": {\"$all\": [\"Comedy\"]}}}", qb.build + ()); } // "elemMatch operator used with full text indexing" @Test public void basicSelector11() { QueryBuilder qb = new QueryBuilder(elemMatch("genre", PredicateExpression.eq("Horror"))); - Assert.assertEquals("{\"selector\": {\"genre\": {\"$elemMatch\": {\"$eq\": \"Horror\"}}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"genre\": {\"$elemMatch\": {\"$eq\": " + + "\"Horror\"}}}}", qb.build()); } // "$lt operator used with database indexed on the field "year"" @@ -163,43 +178,45 @@ public void basicSelector11() { @Test public void basicSelector12() { QueryBuilder qb = new QueryBuilder(lt("year", 1900)); - Assert.assertEquals("{\"selector\": {\"year\": {\"$lt\": 1900}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"year\": {\"$lt\": 1900}}}", qb.build()); } // "$exists operator used with database indexed on the field "year"" @Test public void basicSelector13() { QueryBuilder qb = new QueryBuilder(and(eq("year", 2015), exists("title", true))); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$eq\": 2015}}, {\"title\": {\"$exists\": true}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$eq\": 2015}}, " + + "{\"title\": {\"$exists\": true}}]}}", qb.build()); } // "$type operator used with full text indexing" @Test public void basicSelector14() { QueryBuilder qb = new QueryBuilder(type("year", Type.NUMBER)); - Assert.assertEquals("{\"selector\": {\"year\": {\"$type\": \"number\"}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"year\": {\"$type\": \"number\"}}}", qb.build()); } // "$in operator used with full text indexing" @Test public void basicSelector15() { QueryBuilder qb = new QueryBuilder(in("year", 2010, 2015)); - Assert.assertEquals("{\"selector\": {\"year\": {\"$in\": [2010, 2015]}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"year\": {\"$in\": [2010, 2015]}}}", qb.build()); } // "$in operator used with full text indexing" @Test public void basicSelector15_single() { QueryBuilder qb = new QueryBuilder(in("year", 2010)); - Assert.assertEquals("{\"selector\": {\"year\": {\"$in\": [2010]}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"year\": {\"$in\": [2010]}}}", qb.build()); } // "$nin operator used with full text indexing" @Test public void basicSelector16() { QueryBuilder qb = new QueryBuilder(and(gt("year", 2009), - nin("year", 2010, 2015))); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gt\": 2009}}, {\"year\": {\"$nin\": [2010, 2015]}}]}}", qb.build()); + nin("year", 2010, 2015))); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gt\": 2009}}, " + + "{\"year\": {\"$nin\": [2010, 2015]}}]}}", qb.build()); } // "$nin operator used with full text indexing" @@ -207,14 +224,16 @@ public void basicSelector16() { public void basicSelector16_single() { QueryBuilder qb = new QueryBuilder(and(gt("year", 2009), nin("year", 2010))); - Assert.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gt\": 2009}}, {\"year\": {\"$nin\": [2010]}}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$and\": [{\"year\": {\"$gt\": 2009}}, " + + "{\"year\": {\"$nin\": [2010]}}]}}", qb.build()); } @Test public void complexSelector1() { QueryBuilder qb = new QueryBuilder(not(and(gt("year", 2009), nin("year", 2010, 2015)))); - Assert.assertEquals("{\"selector\": {\"$not\": {\"$and\": [{\"year\": {\"$gt\": 2009}}, {\"year\": {\"$nin\": [2010, 2015]}}]}}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$not\": {\"$and\": [{\"year\": {\"$gt\": " + + "2009}}, {\"year\": {\"$nin\": [2010, 2015]}}]}}}", qb.build()); } @Test @@ -222,27 +241,31 @@ public void complexSelector2() { QueryBuilder qb = new QueryBuilder(or( and( eq("Actor", "Schwarzenegger"), - eq("Year",2012)), + eq("Year", 2012)), and( - eq("Actor","de Vito"), + eq("Actor", "de Vito"), eq("Year", 2001)) )); - Assert.assertEquals("{\"selector\": {\"$or\": [{\"$and\": [{\"Actor\": {\"$eq\": \"Schwarzenegger\"}}, {\"Year\": {\"$eq\": 2012}}]}, {\"$and\": [{\"Actor\": {\"$eq\": \"de Vito\"}}, {\"Year\": {\"$eq\": 2001}}]}]}}", qb.build()); + Assertions.assertEquals("{\"selector\": {\"$or\": [{\"$and\": [{\"Actor\": {\"$eq\": " + + "\"Schwarzenegger\"}}, {\"Year\": {\"$eq\": 2012}}]}, {\"$and\": [{\"Actor\": " + + "{\"$eq\": \"de Vito\"}}, {\"Year\": {\"$eq\": 2001}}]}]}}", qb.build()); } // "Selector basics" @Test public void basicSelector1WithFields() { - QueryBuilder qb = new QueryBuilder(eq("director", "Lars von Trier")).fields("_id", "_rev", "year", "title"); - Assert.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}, " + + QueryBuilder qb = new QueryBuilder(eq("director", "Lars von Trier")).fields("_id", + "_rev", "year", "title"); + Assertions.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}, " + "\"fields\": [\"_id\", \"_rev\", \"year\", \"title\"]}", qb.build()); } // "Selector basics" @Test public void basicSelector1WithSort() { - QueryBuilder qb = new QueryBuilder(eq("director", "Lars von Trier")).sort(Sort.asc("year"), Sort.desc("director")); - Assert.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}, " + + QueryBuilder qb = new QueryBuilder(eq("director", "Lars von Trier")).sort(Sort.asc + ("year"), Sort.desc("director")); + Assertions.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}, " + "\"sort\": [{\"year\": \"asc\"}, {\"director\": \"desc\"}]}", qb.build()); } @@ -254,12 +277,11 @@ public void basicSelector1WithAllOptions() { sort(Sort.asc("year"), Sort.desc("director")). limit(10). skip(0); - Assert.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}, " + - "\"fields\": [\"_id\", \"_rev\", \"year\", \"title\"], " + + Assertions.assertEquals("{\"selector\": {\"director\": {\"$eq\": \"Lars von Trier\"}}, " + + "\"fields\": [\"_id\", \"_rev\", \"year\", \"title\"], " + "\"sort\": [{\"year\": \"asc\"}, {\"director\": \"desc\"}], \"limit\": 10, " + "\"skip\": 0}", qb.build()); } - } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ReplicationTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ReplicationTest.java index 7d77dc8b3..3d02f333c 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ReplicationTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ReplicationTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -15,24 +15,24 @@ package com.cloudant.tests; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.cloudant.client.api.model.ReplicationResult; import com.cloudant.client.api.model.ReplicationResult.ReplicationHistory; import com.cloudant.test.main.RequiresDB; +import com.cloudant.tests.base.TestWithReplication; import com.cloudant.tests.util.Utils; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.List; import java.util.Map; -@Category(RequiresDB.class) -public class ReplicationTest extends ReplicateBaseTest { +@RequiresDB +public class ReplicationTest extends TestWithReplication { @Test public void replication() { @@ -42,7 +42,7 @@ public void replication() { .target(db2URI) .trigger(); - assertTrue("The replication should complete ok", result.isOk()); + assertTrue(result.isOk(), "The replication should complete ok"); List histories = result.getHistories(); assertThat(histories.size(), not(0)); @@ -61,7 +61,7 @@ public void replication_filteredWithQueryParams() { .queryParams(queryParams) .trigger(); - assertTrue("The replication should complete ok", result.isOk()); + assertTrue(result.isOk(), "The replication should complete ok"); } @Test @@ -75,10 +75,10 @@ public void replicateDatabaseUsingReplicationTrigger() throws Exception { ReplicationResult result = account.replication().source(db1URI) .target(db2URI).createTarget(true).trigger(); - assertTrue("The replication should complete ok", result.isOk()); + assertTrue(result.isOk(), "The replication should complete ok"); Foo fooOnDb2 = Utils.findDocumentWithRetries(db2, docId, Foo.class, 20); - assertNotNull("The document should have been replicated", fooOnDb2); + assertNotNull(fooOnDb2, "The document should have been replicated"); } @Test @@ -96,7 +96,7 @@ public void replication_conflict() throws Exception { ReplicationResult result = account.replication().source(db1URI) .target(db2URI).trigger(); - assertTrue("The replication should complete ok", result.isOk()); + assertTrue(result.isOk(), "The replication should complete ok"); //we replicated with a doc with the same ID but different content in each DB, we should get //a conflict diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ReplicatorTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ReplicatorTest.java index 34eea6710..33c4a7a79 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ReplicatorTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ReplicatorTest.java @@ -1,130 +1,130 @@ -/* - * 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; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import com.cloudant.client.api.model.ReplicatorDocument; -import com.cloudant.client.api.model.Response; -import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.Utils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Category(RequiresDB.class) -public class ReplicatorTest extends ReplicateBaseTest { - - private String repDocId; - - @Before - public void generateReplicatorDocId() { - repDocId = Utils.generateUUID(); - } - - @After - public void cleanUpReplicatorDoc() throws Exception { - Utils.removeReplicatorTestDoc(account, repDocId); - } - - @Test - public void replication() throws Exception { - Response response = account.replicator() - .replicatorDocId(repDocId) - .createTarget(true) - .source(db1URI) - .target(db2URI) - .save(); - - // find and remove replicator doc - ReplicatorDocument repDoc = Utils.waitForReplicatorToComplete(account, response.getId()); - assertTrue("The replicator should reach completed state", "completed".equalsIgnoreCase - (repDoc.getReplicationState())); - } - - @Test - public void replication_filteredWithQueryParams() throws Exception { - Map queryParams = new HashMap(); - queryParams.put("somekey1", "value 1"); - - Response response = account.replicator() - .createTarget(true) - .replicatorDocId(repDocId) - .source(db1URI) - .target(db2URI) - .filter("example/example_filter") - .queryParams(queryParams) - .save(); - - // find and remove replicator doc - ReplicatorDocument repDoc = Utils.waitForReplicatorToComplete(account, response.getId()); - assertTrue("The replicator should reach completed state", "completed".equalsIgnoreCase - (repDoc.getReplicationState())); - } - - @Test - public void replicatorDB() throws Exception { - - // trigger a replication - Response response = account.replicator() - .replicatorDocId(repDocId) - .source(db1URI) - .target(db2URI).continuous(true) - .createTarget(true) - .save(); - - // we need the replication to start before continuing - Utils.waitForReplicatorToStart(account, response.getId()); - - // find all replicator docs - List replicatorDocs = account.replicator() - .findAll(); - assertThat(replicatorDocs.size(), is(not(0))); - - } - - @Test - public void replication_conflict() throws Exception { - String docId = Utils.generateUUID(); - Foo foodb1 = new Foo(docId, "titleX"); - Foo foodb2 = new Foo(docId, "titleY"); - - //save Foo(X) in DB1 - db1.save(foodb1); - //save Foo(Y) in DB2 - db2.save(foodb2); - - //replicate with DB1 with DB2 - Response response = account.replicator().source(db1URI) - .target(db2URI).replicatorDocId(repDocId) - .save(); - - // we need the replication to finish before continuing - Utils.waitForReplicatorToComplete(account, response.getId()); - - //we replicated with a doc with the same ID but different content in each DB, we should get - //a conflict - assertConflictsNotZero(db2); - } -} +/* + * Copyright © 2015, 2018 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; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.cloudant.client.api.model.ReplicatorDocument; +import com.cloudant.client.api.model.Response; +import com.cloudant.test.main.RequiresDB; +import com.cloudant.tests.base.TestWithReplication; +import com.cloudant.tests.util.Utils; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequiresDB +public class ReplicatorTest extends TestWithReplication { + + private String repDocId; + + @BeforeEach + public void generateReplicatorDocId() { + repDocId = Utils.generateUUID(); + } + + @AfterEach + public void cleanUpReplicatorDoc() throws Exception { + Utils.removeReplicatorTestDoc(account, repDocId); + } + + @Test + public void replication() throws Exception { + Response response = account.replicator() + .replicatorDocId(repDocId) + .createTarget(true) + .source(db1URI) + .target(db2URI) + .save(); + + // find and remove replicator doc + ReplicatorDocument repDoc = Utils.waitForReplicatorToComplete(account, response.getId()); + assertTrue("completed".equalsIgnoreCase(repDoc.getReplicationState()), "The replicator " + + "should reach completed state"); + } + + @Test + public void replication_filteredWithQueryParams() throws Exception { + Map queryParams = new HashMap(); + queryParams.put("somekey1", "value 1"); + + Response response = account.replicator() + .createTarget(true) + .replicatorDocId(repDocId) + .source(db1URI) + .target(db2URI) + .filter("example/example_filter") + .queryParams(queryParams) + .save(); + + // find and remove replicator doc + ReplicatorDocument repDoc = Utils.waitForReplicatorToComplete(account, response.getId()); + assertTrue("completed".equalsIgnoreCase(repDoc.getReplicationState()), "The replicator " + + "should reach completed state"); + } + + @Test + public void replicatorDB() throws Exception { + + // trigger a replication + Response response = account.replicator() + .replicatorDocId(repDocId) + .source(db1URI) + .target(db2URI).continuous(true) + .createTarget(true) + .save(); + + // we need the replication to start before continuing + Utils.waitForReplicatorToStart(account, response.getId()); + + // find all replicator docs + List replicatorDocs = account.replicator() + .findAll(); + assertThat(replicatorDocs.size(), is(not(0))); + + } + + @Test + public void replication_conflict() throws Exception { + String docId = Utils.generateUUID(); + Foo foodb1 = new Foo(docId, "titleX"); + Foo foodb2 = new Foo(docId, "titleY"); + + //save Foo(X) in DB1 + db1.save(foodb1); + //save Foo(Y) in DB2 + db2.save(foodb2); + + //replicate with DB1 with DB2 + Response response = account.replicator().source(db1URI) + .target(db2URI).replicatorDocId(repDocId) + .save(); + + // we need the replication to finish before continuing + Utils.waitForReplicatorToComplete(account, response.getId()); + + //we replicated with a doc with the same ID but different content in each DB, we should get + //a conflict + assertConflictsNotZero(db2); + } +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ResponseTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ResponseTest.java index 8178f4704..187ca2a18 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ResponseTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ResponseTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -13,10 +13,10 @@ */ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +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 static org.junit.jupiter.api.Assertions.fail; import com.cloudant.client.api.CloudantClient; import com.cloudant.client.api.Database; @@ -29,15 +29,11 @@ import com.cloudant.http.HttpConnectionInterceptorContext; import com.cloudant.http.HttpConnectionRequestInterceptor; import com.cloudant.test.main.RequiresCloudant; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; import com.cloudant.tests.util.Utils; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; @@ -47,19 +43,13 @@ * Test cases to verify status code from Response object. * Assert status codes in CouchDbException and its subclasses. */ -public class ResponseTest { +public class ResponseTest extends TestWithDbPerClass { - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); private Foo foo; - private static Database db; - @Before + @BeforeEach public void setup() { - db = dbResource.get(); foo = new Foo(Utils.generateUUID()); } @@ -141,7 +131,7 @@ public void verifyBulkDocumentRequest() { * handling path, but it is worth checking that we do work with these error types. *

*/ - @Category(RequiresCloudant.class) + @RequiresCloudant @Test public void testJsonErrorStreamFromLB() throws Exception { final AtomicBoolean badHeaderEnabled = new AtomicBoolean(false); @@ -165,7 +155,8 @@ public void testJsonErrorStreamFromLB() throws Exception { // Make a good request, which will set up the session etc HttpConnection d = c.executeRequest(Http.GET(c.getBaseUri())); d.responseAsString(); - assertTrue("The first request should succeed", d.getConnection().getResponseCode() / 100 == 2); + assertTrue(d.getConnection().getResponseCode() / 100 == 2, "The first request should " + + "succeed"); // Enable the bad headers and expect the exception on the next request badHeaderEnabled.set(true); @@ -174,10 +165,10 @@ public void testJsonErrorStreamFromLB() throws Exception { } catch (CouchDbException e) { //we expect a CouchDbException - assertEquals("The exception should be for a bad request", 400, e.getStatusCode()); + assertEquals(400, e.getStatusCode(), "The exception should be for a bad request"); - assertNotNull("The exception should have an error set", e.getError()); - assertEquals("The exception error should be bad request", "bad_request", e.getError()); + assertNotNull(e.getError(), "The exception should have an error set"); + assertEquals("bad_request", e.getError(), "The exception error should be bad request"); } finally { // Disable the bad header to allow a clean shutdown badHeaderEnabled.set(false); @@ -195,16 +186,16 @@ public void testJsonErrorStreamFromLB() throws Exception { */ private void exceptionAsserts(CouchDbException e, int expectedCode, String expectedReason) { assertExceptionStatusCode(e, expectedCode); - assertNotNull("The error should not be null", e.getError()); + assertNotNull(e.getError(), "The error should not be null"); if ("".equals(expectedReason)) { - assertNotNull("The reason should not be null", e.getReason()); + assertNotNull(e.getReason(), "The reason should not be null"); } else { - assertEquals("The reason should be " + expectedReason, expectedReason, e.getReason()); + assertEquals(expectedReason, e.getReason(), "The reason should be " + expectedReason); } } private void assertExceptionStatusCode(CouchDbException e, int expectedCode) { - assertEquals("The HTTP status code should be " + expectedCode, expectedCode, e - .getStatusCode()); + assertEquals(expectedCode, e.getStatusCode(), "The HTTP status code should be " + + expectedCode); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/SearchTests.java b/cloudant-client/src/test/java/com/cloudant/tests/SearchTests.java index d8f1ddfda..d3e1dc895 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/SearchTests.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/SearchTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,12 +14,10 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +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.Search; import com.cloudant.client.api.model.DesignDocument; @@ -27,14 +25,10 @@ import com.cloudant.client.api.model.SearchResult.SearchResultRow; import com.cloudant.client.internal.URIBase; import com.cloudant.test.main.RequiresCloudant; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.File; import java.net.URI; @@ -45,24 +39,12 @@ import java.util.Map; import java.util.Map.Entry; -@Category(RequiresCloudant.class) -public class SearchTests { +@RequiresCloudant +public class SearchTests extends TestWithDbPerClass { - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - private static Database db; - private static CloudantClient account; - - @BeforeClass + @BeforeAll public static void setUp() throws Exception { - account = clientResource.get(); - db = dbResource.get(); - - // replciate the animals db for search tests + // replicate the animals db for search tests com.cloudant.client.api.Replication r = account.replication(); r.source("https://clientlibs-test.cloudant.com/animaldb"); r.createTarget(true); @@ -269,7 +251,8 @@ private void escapingTest(String expectedResult, String query) { String uriBaseString = account.getBaseUri().toASCIIString(); String expectedUriString = uriBaseString - + "/animaldb/_design/views101/_search/animals?include_docs=true&q=" + expectedResult; + + "/animaldb/_design/views101/_search/animals?include_docs=true&q=" + + expectedResult; String uriString = uri.toASCIIString(); assertEquals(expectedUriString, uriString); diff --git a/cloudant-client/src/test/java/com/cloudant/tests/SessionInterceptorExpiryTests.java b/cloudant-client/src/test/java/com/cloudant/tests/SessionInterceptorExpiryTests.java index 243e333c5..dc95cc97a 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/SessionInterceptorExpiryTests.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/SessionInterceptorExpiryTests.java @@ -14,11 +14,11 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.cloudant.http.Http; import com.cloudant.http.HttpConnection; @@ -28,47 +28,100 @@ import com.cloudant.http.internal.interceptors.CookieInterceptor; import com.cloudant.http.internal.interceptors.IamCookieInterceptor; import com.cloudant.http.internal.ok.OkHttpClientHttpUrlConnectionFactory; +import com.cloudant.tests.extensions.MockWebServerExtension; import com.cloudant.tests.util.HttpFactoryParameterizedTest; import com.cloudant.tests.util.IamSystemPropertyMock; import com.cloudant.tests.util.MockWebServerResources; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Stream; +@ExtendWith(SessionInterceptorExpiryTests.ParameterProvider.class) public class SessionInterceptorExpiryTests extends HttpFactoryParameterizedTest { - public static IamSystemPropertyMock iamSystemPropertyMock; + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } - @Parameterized.Parameters(name = "Using okhttp: {0} for session path {1}") - public static List testParams() { - List tests = new ArrayList(4); - tests.add(new Object[]{false, "/_session"}); - tests.add(new Object[]{true, "/_session"}); - tests.add(new Object[]{false, "/_iam_session"}); - tests.add(new Object[]{true, "/_iam_session"}); - return tests; - } + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + return Stream.of(invocationContext(false, "/_iam_session"), + invocationContext(false, "/_session"), + invocationContext(true, "/_iam_session"), + invocationContext(true, "/_session")); + } - // Note Parameter(0) okUsable is inherited + public static TestTemplateInvocationContext invocationContext(final boolean okUsable, + final String sessionPath) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("path:%s,okUsable:%s", sessionPath, okUsable); + } - @Parameterized.Parameter(1) - public String sessionPath; + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals + (boolean.class); + case 1: + return parameterContext.getParameter().getType().equals + (String.class); + } + return false; + } - @Rule - public MockWebServer mockWebServer = new MockWebServer(); - @Rule - public MockWebServer mockIamServer = new MockWebServer(); + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return okUsable; + case 1: + return sessionPath; + } + return null; + } + }); + } + }; + } + } + + public static IamSystemPropertyMock iamSystemPropertyMock; + + @RegisterExtension + public MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + public MockWebServer mockWebServer; + @RegisterExtension + public MockWebServerExtension mockIamServerExt = new MockWebServerExtension(); + public MockWebServer mockIamServer; private HttpConnectionRequestInterceptor rqInterceptor; private HttpConnectionResponseInterceptor rpInterceptor; @@ -76,13 +129,16 @@ public static List testParams() { /** * Before running this test class setup the property mock. */ - @BeforeClass + @BeforeAll public static void setupIamSystemPropertyMock() { iamSystemPropertyMock = new IamSystemPropertyMock(); } - @Before - public void setupSessionInterceptor() { + @BeforeEach + public void setupSessionInterceptor(boolean okUsable, String sessionPath) { + this.mockWebServer = mockWebServerExt.get(); + this.mockIamServer = mockIamServerExt.get(); + String baseUrl = mockWebServer.url("").toString(); if (sessionPath.equals("/_session")) { @@ -102,7 +158,10 @@ public void setupSessionInterceptor() { } - private void queueResponses(Long expiry, String cookieValue) { + private void queueResponses(boolean okUsable, + String sessionPath, + Long expiry, + String cookieValue) { // Queue up the session response String cookieString; if (sessionPath.equals("/_session")) { @@ -123,10 +182,13 @@ private void queueResponses(Long expiry, String cookieValue) { mockWebServer.enqueue(MockWebServerResources.JSON_OK); } - private void executeTest(Long expiryTime, String cookieValue) throws Exception { - queueResponses(expiryTime, cookieValue); + private void executeTest(boolean okUsable, + String sessionPath, + Long expiryTime, + String cookieValue) throws Exception { + queueResponses(okUsable, sessionPath, expiryTime, cookieValue); HttpConnection conn = Http.GET(mockWebServer.url("/").url()); - conn.connectionFactory = (okUsable) ? new OkHttpClientHttpUrlConnectionFactory() : + conn.connectionFactory = (isOkUsable) ? new OkHttpClientHttpUrlConnectionFactory() : new DefaultHttpUrlConnectionFactory(); conn.requestInterceptors.add(rqInterceptor); conn.responseInterceptors.add(rpInterceptor); @@ -135,36 +197,38 @@ private void executeTest(Long expiryTime, String cookieValue) throws Exception { // Consume response stream and assert ok: true String responseStr = conn.responseAsString(); String okPattern = ".*\"ok\"\\s*:\\s*true.*"; - assertTrue("There should be an ok response: " + responseStr, Pattern.compile(okPattern, - Pattern.DOTALL).matcher(responseStr).matches()); + assertTrue(Pattern.compile(okPattern, Pattern.DOTALL).matcher(responseStr).matches(), + "There should be an ok response: " + responseStr); // Assert the _session request RecordedRequest sessionRequest = mockWebServer.takeRequest(MockWebServerResources .TIMEOUT, MockWebServerResources.TIMEOUT_UNIT); - assertEquals("The interceptor should make a session request", sessionPath, - sessionRequest.getPath()); - assertNull("There should be no existing cookie on the session request", sessionRequest - .getHeader("Cookie")); + assertEquals(sessionPath, sessionRequest.getPath(), "The interceptor should make a " + + "session request"); + assertNull(sessionRequest.getHeader("Cookie"), "There should be no existing cookie on the" + + " session request"); // Assert the GET request RecordedRequest getRequest = mockWebServer.takeRequest(MockWebServerResources.TIMEOUT, MockWebServerResources.TIMEOUT_UNIT); - assertEquals("The request path should be correct", "/", getRequest.getPath()); - assertNotNull("There should be a cookie on the request", getRequest.getHeader("Cookie")); - String expectedCookie = ((sessionPath.equals("/_session")) ? MockWebServerResources.AUTH_COOKIE_NAME : + assertEquals("/", getRequest.getPath(), "The request path should be correct"); + assertNotNull(getRequest.getHeader("Cookie"), "There should be a cookie on the request"); + String expectedCookie = ((sessionPath.equals("/_session")) ? MockWebServerResources + .AUTH_COOKIE_NAME : MockWebServerResources.IAM_COOKIE_NAME) + "=" + cookieValue; - assertEquals("The cookie should be the correct session type", expectedCookie, - getRequest.getHeader("Cookie")); + assertEquals(expectedCookie, getRequest.getHeader("Cookie"), "The cookie should be the " + + "correct session type"); } /** * Test the non-expiry case just to validate that things work normally + * * @throws Exception */ - @Test - public void testMakesCookieRequest() throws Exception { - executeTest(null, MockWebServerResources.EXPECTED_OK_COOKIE); + @TestTemplate + public void testMakesCookieRequest(boolean okUsable, String sessionPath) throws Exception { + executeTest(okUsable, sessionPath, null, MockWebServerResources.EXPECTED_OK_COOKIE); } /** @@ -174,10 +238,12 @@ public void testMakesCookieRequest() throws Exception { * * @throws Exception */ - @Test - public void testNewCookieRequestMadeAfterExpiry() throws Exception { + @TestTemplate + public void testNewCookieRequestMadeAfterExpiry(boolean okUsable, String sessionPath) throws + Exception { // Make a GET request and get a cookie valid for 2 seconds - executeTest(System.currentTimeMillis() + 2000, MockWebServerResources.EXPECTED_OK_COOKIE); + executeTest(okUsable, sessionPath, System.currentTimeMillis() + 2000, + MockWebServerResources.EXPECTED_OK_COOKIE); // Sleep 2 seconds and make another request // Note 1 second appears to be insufficient probably due to rounding to the nearest second @@ -186,7 +252,7 @@ public void testNewCookieRequestMadeAfterExpiry() throws Exception { // Since the Cookie is expired it should follow the same sequence of POST /_session GET / // If the expired Cookie was retrieved it would only do GET / and the test would fail. - executeTest(null, MockWebServerResources.EXPECTED_OK_COOKIE_2); + executeTest(okUsable, sessionPath, null, MockWebServerResources.EXPECTED_OK_COOKIE_2); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/SslAuthenticationTest.java b/cloudant-client/src/test/java/com/cloudant/tests/SslAuthenticationTest.java index 40651323e..302dc86c3 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/SslAuthenticationTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/SslAuthenticationTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,46 +14,101 @@ package com.cloudant.tests; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.cloudant.client.api.CloudantClient; import com.cloudant.client.org.lightcouch.CouchDbException; import com.cloudant.test.main.RequiresCloudantService; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.MockWebServerResources; +import com.cloudant.tests.extensions.MockWebServerExtension; import com.cloudant.tests.util.HttpFactoryParameterizedTest; +import com.cloudant.tests.util.MockWebServerResources; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.junit.jupiter.api.function.Executable; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocketFactory; +@ExtendWith(SslAuthenticationTest.ParameterProvider.class) public class SslAuthenticationTest extends HttpFactoryParameterizedTest { - @ClassRule - public static CloudantClientResource dbClientResource = new CloudantClientResource(); - private static CloudantClient dbClient = dbClientResource.get(); + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } - @Rule - public MockWebServer server = new MockWebServer(); + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + return Stream.of(invocationContext(false), + invocationContext(true)); + } - @Parameterized.Parameters(name = "Using okhttp: {0}") - public static Object[] okUsable() { - return new Object[]{true, false}; + public static TestTemplateInvocationContext invocationContext(final boolean okUsable) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("okUsable:%s", okUsable); + } + + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals + (boolean.class); + } + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return okUsable; + } + return null; + } + }); + } + }; + } } - @Before - public void getMockWebServer() { + + @RegisterExtension + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + + protected MockWebServer server; + + @BeforeEach + public void beforeEach() { + server = mockWebServerExt.get(); server.useHttps(MockWebServerResources.getSSLSocketFactory(), false); } @@ -64,21 +119,20 @@ public void getMockWebServer() { * @param e the exception. */ private static void validateClientAuthenticationException(CouchDbException e) { - assertNotNull("Expected CouchDbException but got null", e); + assertNotNull(e, "Expected CouchDbException but got null"); Throwable t = e.getCause(); - assertTrue("Expected SSLHandshakeException caused by client certificate check but got " + - t.getClass(), - t instanceof SSLHandshakeException); + assertTrue(t instanceof SSLHandshakeException, "Expected SSLHandshakeException caused by " + + "client certificate check but got " + t.getClass()); } /** * Connect to the local simple https server with SSL authentication disabled. */ - @Test + @TestTemplate public void localSslAuthenticationDisabled() throws Exception { // Build a client that connects to the mock server with SSL authentication disabled - dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server) + CloudantClient dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server) .disableSSLAuthentication() .build(); @@ -95,12 +149,12 @@ public void localSslAuthenticationDisabled() throws Exception { * Connect to the local simple https server with SSL authentication enabled explicitly. * This should throw an exception because the SSL authentication fails. */ - @Test + @TestTemplate public void localSslAuthenticationEnabled() throws Exception { CouchDbException thrownException = null; try { - dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server) + CloudantClient dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server) .build(); // Queue a 200 OK response @@ -119,11 +173,11 @@ public void localSslAuthenticationEnabled() throws Exception { * This shouldn't throw an exception as the Cloudant server has a valid * SSL certificate, so should be authenticated. */ - @Test - @Category(RequiresCloudantService.class) + @TestTemplate + @RequiresCloudantService public void remoteSslAuthenticationEnabledTest() { - dbClient = CloudantClientHelper.getClientBuilder().build(); + CloudantClient dbClient = CloudantClientHelper.getClientBuilder().build(); // Make an arbitrary connection to the DB. dbClient.getAllDbs(); @@ -134,11 +188,11 @@ public void remoteSslAuthenticationEnabledTest() { /** * Connect to the remote Cloudant server with SSL Authentication disabled. */ - @Test - @Category(RequiresCloudantService.class) + @TestTemplate + @RequiresCloudantService public void remoteSslAuthenticationDisabledTest() { - dbClient = CloudantClientHelper.getClientBuilder() + CloudantClient dbClient = CloudantClientHelper.getClientBuilder() .disableSSLAuthentication() .build(); @@ -152,34 +206,44 @@ public void remoteSslAuthenticationDisabledTest() { * Assert that building a client with a custom SSL factory first, then setting the * SSL Authentication disabled will throw an IllegalStateException. */ - @Test(expected = IllegalStateException.class) + @TestTemplate public void testCustomSSLFactorySSLAuthDisabled() { + assertThrows(IllegalStateException.class, new Executable() { + @Override + public void execute() throws Throwable { - dbClient = CloudantClientHelper.getClientBuilder() - .customSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()) - .disableSSLAuthentication() - .build(); + CloudantClient dbClient = CloudantClientHelper.getClientBuilder() + .customSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()) + + .disableSSLAuthentication() + .build(); + } + }); } /** * Assert that building a client with SSL Authentication disabled first, then setting * a custom SSL factory will throw an IllegalStateException. */ - @Test(expected = IllegalStateException.class) + @TestTemplate public void testSSLAuthDisabledWithCustomSSLFactory() { - - dbClient = CloudantClientHelper.getClientBuilder() - .disableSSLAuthentication() - .customSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()) - .build(); - + assertThrows(IllegalStateException.class, new Executable() { + @Override + public void execute() throws Throwable { + + CloudantClient dbClient = CloudantClientHelper.getClientBuilder() + .disableSSLAuthentication() + .customSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()) + .build(); + } + }); } /** * Repeat the localSSLAuthenticationDisabled, but with the cookie auth enabled. * This test validates that the SSL settings also get applied to the cookie interceptor. */ - @Test + @TestTemplate public void localSSLAuthenticationDisabledWithCookieAuth() throws Exception { // Mock up an OK cookie response then an OK response for the getAllDbs() @@ -187,7 +251,8 @@ public void localSSLAuthenticationDisabledWithCookieAuth() throws Exception { server.enqueue(new MockResponse()); //OK 200 // Use a username and password to enable the cookie auth interceptor - dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server).username("user") + CloudantClient dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server) + .username("user") .password("password") .disableSSLAuthentication() .build(); @@ -199,7 +264,7 @@ public void localSSLAuthenticationDisabledWithCookieAuth() throws Exception { * Repeat the localSSLAuthenticationEnabled, but with the cookie auth enabled. * This test validates that the SSL settings also get applied to the cookie interceptor. */ - @Test + @TestTemplate public void localSSLAuthenticationEnabledWithCookieAuth() throws Exception { // Mock up an OK cookie response then an OK response for the getAllDbs() @@ -207,16 +272,18 @@ public void localSSLAuthenticationEnabledWithCookieAuth() throws Exception { server.enqueue(new MockResponse()); //OK 200 // Use a username and password to enable the cookie auth interceptor - dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server).username("user") + CloudantClient dbClient = CloudantClientHelper.newMockWebServerClientBuilder(server) + .username("user") .password("password") .build(); try { dbClient.getAllDbs(); fail("The SSL authentication failure should result in a CouchDbException"); - } catch(CouchDbException e) { + } catch (CouchDbException e) { validateClientAuthenticationException(e); } } + } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/URIBaseTest.java b/cloudant-client/src/test/java/com/cloudant/tests/URIBaseTest.java index 5b6338ce8..add92b3c4 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/URIBaseTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/URIBaseTest.java @@ -1,36 +1,48 @@ +/* + * Copyright © 2018 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; import com.cloudant.client.api.ClientBuilder; import com.cloudant.client.api.CloudantClient; import com.cloudant.client.internal.URIBase; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; +import com.cloudant.tests.extensions.CloudantClientExtension; -import org.junit.Assert; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.net.URI; -@Category(RequiresDB.class) +@RequiresDB public class URIBaseTest { - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); + @RegisterExtension + public static CloudantClientExtension clientResource = new CloudantClientExtension(); @Test public void buildAccountUri_noTrailingPathSeparator() throws Exception { CloudantClient client = ClientBuilder.url(clientResource.get().getBaseUri().toURL()) .build(); - Assert.assertFalse(client.getBaseUri().toString().endsWith("/")); + Assertions.assertFalse(client.getBaseUri().toString().endsWith("/")); URI clientUri = new URIBase(client.getBaseUri()).build(); - Assert.assertFalse(clientUri.toString().endsWith("/")); + Assertions.assertFalse(clientUri.toString().endsWith("/")); //Check that path is not missing / separators clientUri = new URIBase(client.getBaseUri()).path("").path("api").path("couch").build(); URI expectedAccountUri = new URI(clientResource.get().getBaseUri().toString() + "/api/couch"); - Assert.assertEquals(expectedAccountUri, clientUri); + Assertions.assertEquals(expectedAccountUri, clientUri); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/UnicodeTest.java b/cloudant-client/src/test/java/com/cloudant/tests/UnicodeTest.java index c328e8b85..9881ba469 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/UnicodeTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/UnicodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,11 +14,8 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import com.cloudant.client.api.Database; -import com.cloudant.client.api.query.Field; -import com.cloudant.client.api.query.Index; import com.cloudant.client.api.query.JsonIndex; import com.cloudant.client.api.views.Key; import com.cloudant.client.internal.DatabaseURIHelper; @@ -26,18 +23,12 @@ import com.cloudant.http.HttpConnection; import com.cloudant.test.main.RequiresCloudant; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; -import com.cloudant.tests.util.TestLog; +import com.cloudant.tests.base.TestWithDbPerTest; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Test; import java.io.BufferedInputStream; import java.io.IOException; @@ -52,31 +43,21 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -@Category(RequiresDB.class) -public class UnicodeTest { - - @ClassRule - public static final TestLog TEST_LOG = new TestLog(); - @ClassRule - public static final CloudantClientResource clientResource = new CloudantClientResource(); - @Rule - public final DatabaseResource dbResource = new DatabaseResource(clientResource); +@RequiresDB +public class UnicodeTest extends TestWithDbPerTest { // According to JSON (ECMA-404, section 9 "Strings"): // - All Unicode characters except those that must be escaped // (U+0000..U+001F, U+0022, U+005C) may be placed in a string. // - All Unicode characters may be included as Unicode escapes // (after conversion to UTF-16). + private static final String TESTSTRING_KEY = "teststring"; private static final String TESTSTRING = "Gr\u00fc\u00dfe \u65e5\u672c\u8a9e \uD834\uDD1E."; private static final String TESTSTRING_ESCAPED = "Gr\\u00fc\\u00dfe \\u65e5\\u672c\\u8a9e " + "\\uD834\\uDD1E."; - - private Database db; - - @Before - public void setup() { - db = dbResource.get(); - } + private static final String EXPECTED_JSON = "{\"_id\":\"" + TESTSTRING_KEY + "\"," + + "\"_rev\":\"1-39933759c7250133b6039d94ea09134f\",\"foo\":\"Gr\u00fc\u00dfe " + + "\u65e5\u672c\u8a9e \uD834\uDD1E.\"}\n"; // ======================================================================== // REST request utilities. @@ -262,7 +243,7 @@ private static void closeResponse(HttpConnection response) throws Exception { */ @Test public void testLiteralUnicode() throws Exception { - URI uri = new DatabaseURIHelper(db.getDBUri()).path("literal").build(); + URI uri = new DatabaseURIHelper(db.getDBUri()).path(TESTSTRING_KEY).build(); { HttpConnection conn = Http.PUT(uri, "application/json"); conn.requestProperties.put("Accept", "application/json"); @@ -277,7 +258,7 @@ public void testLiteralUnicode() throws Exception { clientResource.get().executeRequest(conn); assertEquals(200, conn.getConnection().getResponseCode()); String result = getPlainTextEntityAsString(conn, uri); - TEST_LOG.logger.info("testLiteralUnicode: Result as returned in entity: " + result); + assertEquals(EXPECTED_JSON, result); closeResponse(conn); } { @@ -297,7 +278,7 @@ public void testLiteralUnicode() throws Exception { */ @Test public void testEscapedUnicode() throws Exception { - URI uri = new DatabaseURIHelper(db.getDBUri()).path("escaped").build(); + URI uri = new DatabaseURIHelper(db.getDBUri()).path(TESTSTRING_KEY).build(); { HttpConnection conn = Http.PUT(uri, "application/json"); conn.requestProperties.put("Accept", "application/json"); @@ -312,7 +293,7 @@ public void testEscapedUnicode() throws Exception { clientResource.get().executeRequest(conn); assertEquals(200, conn.getConnection().getResponseCode()); String result = getPlainTextEntityAsString(conn, uri); - TEST_LOG.logger.info("testEscapedUnicode: Result as returned in entity: " + result); + assertEquals(EXPECTED_JSON, result); closeResponse(conn); } { @@ -338,7 +319,7 @@ public static class MyObject { // To reproduce: In Eclipse, use "Run > Run Configurations...", tab "Common", // panel "Encoding", set the encoding to ISO-8859-1. @Test - @Category(RequiresCloudant.class) + @RequiresCloudant public void testUnicodeInObject() throws Exception { db.createIndex(JsonIndex.builder() .name("myview") @@ -346,10 +327,6 @@ public void testUnicodeInObject() throws Exception { .asc("foo") .definition()); - // Show the indices. - for (Index index : db.listIndexes().allIndexes()) { - TEST_LOG.logger.info(index.toString()); - } // Create an object. MyObject object = new MyObject(); object.foo = TESTSTRING; diff --git a/cloudant-client/src/test/java/com/cloudant/tests/UpdateHandlerTest.java b/cloudant-client/src/test/java/com/cloudant/tests/UpdateHandlerTest.java index 890a714cd..3e3ec4de3 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/UpdateHandlerTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/UpdateHandlerTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,37 +14,24 @@ */ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +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.Database; import com.cloudant.client.api.model.Params; import com.cloudant.client.api.model.Response; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.base.TestWithDbPerClass; import com.cloudant.tests.util.Utils; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -@Category(RequiresDB.class) -public class UpdateHandlerTest { +@RequiresDB +public class UpdateHandlerTest extends TestWithDbPerClass { - public static CloudantClientResource clientResource = new CloudantClientResource(); - public static DatabaseResource dbResource = new DatabaseResource(clientResource); - @ClassRule - public static RuleChain chain = RuleChain.outerRule(clientResource).around(dbResource); - - private static Database db; - - @BeforeClass - public static void setUp() throws Exception { - db = dbResource.get(); + @BeforeAll + public static void beforeAll() throws Exception { Utils.putDesignDocs(db); } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ViewPaginationTests.java b/cloudant-client/src/test/java/com/cloudant/tests/ViewPaginationTests.java index 7275d6091..52529ec37 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ViewPaginationTests.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ViewPaginationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -14,68 +14,115 @@ package com.cloudant.tests; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import com.cloudant.client.api.Database; import com.cloudant.client.api.views.Key; import com.cloudant.client.api.views.ViewRequest; import com.cloudant.client.api.views.ViewResponse; +import com.cloudant.test.main.RequiresDB; +import com.cloudant.tests.base.TestWithDbPerTest; import com.cloudant.tests.util.CheckPagination; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; import com.cloudant.tests.util.Utils; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; -@RunWith(Parameterized.class) -public class ViewPaginationTests { +@RequiresDB +@ExtendWith(ViewPaginationTests.ParameterProvider.class) +public class ViewPaginationTests extends TestWithDbPerTest { - /** - * Parameters for these tests so we run each test multiple times. - * We run with a single key or a complex key and both ascending and descending. - */ - @Parameterized.Parameters(name = "Key:{0},Descending:{1},Stateless:{2}") - public static Iterable data() { + static class ParameterProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } + + @Override + public Stream provideTestTemplateInvocationContexts + (ExtensionContext context) { + return StreamSupport.stream(data().spliterator(), false); + } + + public static TestTemplateInvocationContext invocationContext(final CheckPagination.Type + type, + final boolean descending, + final boolean stateless) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("Key:%s,Descending:%s,Stateless:%s", type, + descending, stateless); + } + + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return parameterContext.getParameter().getType().equals + (CheckPagination.Type.class); + case 1: + return parameterContext.getParameter().getType().equals + (boolean.class); + case 2: + return parameterContext.getParameter().getType().equals + (boolean.class); + } + return false; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, + ExtensionContext extensionContext) { + switch (parameterContext.getIndex()) { + case 0: + return type; + case 1: + return descending; + case 2: + return stateless; + } + return null; + } + }); + } + }; + } + } + public static Iterable data() { + + List contexts = new + ArrayList(); boolean[] tf = new boolean[]{true, false}; - List parameters = new ArrayList(); for (CheckPagination.Type type : CheckPagination.Type.values()) { for (boolean descending : tf) { for (boolean stateless : tf) { - parameters.add(new Object[]{type, descending, stateless}); + contexts.add(ParameterProvider.invocationContext(type, descending, stateless)); } } } - return parameters; + return contexts; } - @Parameterized.Parameter - public CheckPagination.Type type; - - @Parameterized.Parameter(value = 1) - public boolean descending; - - @Parameterized.Parameter(value = 2) - public boolean stateless; - - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); - @Rule - public DatabaseResource dbResource = new DatabaseResource(clientResource); - - private Database db; - - @Before + @BeforeEach public void setUp() throws Exception { - db = dbResource.get(); Utils.putDesignDocs(db); } @@ -87,8 +134,10 @@ public void setUp() throws Exception { * Page forward to the last page, back to the first page, forward to the last page and back to * the first page. */ - @Test - public void allTheWayInEachDirectionTwice() throws Exception { + @TestTemplate + public void allTheWayInEachDirectionTwice(CheckPagination.Type type, + boolean descending, + boolean stateless) throws Exception { CheckPagination.newTest(type) .descending(descending) .docCount(6) @@ -106,8 +155,10 @@ public void allTheWayInEachDirectionTwice() throws Exception { * Page forward to the last page, back to the first page, forward to the last page and back to * the first page. */ - @Test - public void partialLastPageAllTheWayInEachDirectionTwice() throws Exception { + @TestTemplate + public void partialLastPageAllTheWayInEachDirectionTwice(CheckPagination.Type type, + boolean descending, + boolean stateless) throws Exception { CheckPagination.newTest(type) .descending(descending) .docCount(5) @@ -124,8 +175,10 @@ public void partialLastPageAllTheWayInEachDirectionTwice() throws Exception { * * Page part way forward, and part way back a few times before paging to the last page. */ - @Test - public void partWayInEachDirection() throws Exception { + @TestTemplate + public void partWayInEachDirection(CheckPagination.Type type, + boolean descending, + boolean stateless) throws Exception { CheckPagination.newTest(type) .descending(descending) .docCount(30) @@ -142,8 +195,10 @@ public void partWayInEachDirection() throws Exception { * * Page part way forward, and part way back a few times before paging to the last page. */ - @Test - public void partialLastPagePartWayInEachDirection() throws Exception { + @TestTemplate + public void partialLastPagePartWayInEachDirection(CheckPagination.Type type, + boolean descending, + boolean stateless) throws Exception { CheckPagination.newTest(type) .descending(descending) .docCount(28) @@ -157,21 +212,28 @@ public void partialLastPagePartWayInEachDirection() throws Exception { * Check that we can page through a view where we use start and end keys. * Assert that we don't exceed the limits of those keys. */ - @Test - public void startAndEndKeyLimits() throws Exception { - startAndEndKeyLimits(true); + @TestTemplate + public void startAndEndKeyLimits(CheckPagination.Type type, + boolean descending, + boolean stateless) throws Exception { + startAndEndKeyLimits(type, descending, stateless, true); } /** * Check that we can page through a view where we use start and end keys. * Assert that we don't exceed the limits of those keys. */ - @Test - public void startAndEndKeyLimitsExclusiveEnd() throws Exception { - startAndEndKeyLimits(false); + @TestTemplate + public void startAndEndKeyLimitsExclusiveEnd(CheckPagination.Type type, + boolean descending, + boolean stateless) throws Exception { + startAndEndKeyLimits(type, descending, stateless, false); } - private void startAndEndKeyLimits(boolean inclusiveEnd) throws Exception { + private void startAndEndKeyLimits(CheckPagination.Type type, + boolean descending, + boolean stateless, + boolean inclusiveEnd) throws Exception { //use the CheckPagination to set-up for this test, but we need to do running differently //since this page is not just simple paging CheckPagination cp = CheckPagination.newTest(type) @@ -193,7 +255,7 @@ private void startAndEndKeyLimits(boolean inclusiveEnd) throws Exception { .newPaginatedRequest(Key.Type.STRING, String.class).reduce(false).descending (descending).inclusiveEnd(inclusiveEnd).rowsPerPage(4).startKey (startKey).endKey(endKey).build(); - runStartAndEndKeyLimits(request, startKey, expectedEndKey); + runStartAndEndKeyLimits(stateless, request, startKey, expectedEndKey); } else { //for multi we will use the creator_created view Key.ComplexKey startKey = (descending) ? Key.complex("uuid").add(1011) : Key.complex @@ -207,19 +269,21 @@ private void startAndEndKeyLimits(boolean inclusiveEnd) throws Exception { .newPaginatedRequest(Key.Type.COMPLEX, String.class).reduce(false).descending (descending).inclusiveEnd(inclusiveEnd).rowsPerPage(4).startKey (startKey).endKey(endKey).build(); - runStartAndEndKeyLimits(request, startKey, expectedEndKey); + runStartAndEndKeyLimits(stateless, request, startKey, expectedEndKey); } } - private void runStartAndEndKeyLimits(ViewRequest request, T expectedStartKey, + private void runStartAndEndKeyLimits(boolean stateless, + ViewRequest request, + T expectedStartKey, T expectedEndKey) throws Exception { ViewResponse page = request.getResponse(); //check the start key is as expected - assertEquals("The start key should be " + expectedStartKey, expectedStartKey, page - .getKeys().get(0)); + assertEquals(expectedStartKey, page.getKeys().get(0), "The start key should be " + + expectedStartKey); //get the last page while (page.hasNextPage()) { @@ -230,10 +294,8 @@ private void runStartAndEndKeyLimits(ViewRequest request, T expec } } //check the end key is as expected - assertEquals("The end key should be " + expectedEndKey, expectedEndKey, page.getKeys() - .get(page.getKeys() - .size() - - 1)); + assertEquals(expectedEndKey, page.getKeys().get(page.getKeys().size() - 1), "The end key " + + "should be " + expectedEndKey); //now page backwards and ensure the last key we get is the start key while (page.hasPreviousPage()) { @@ -244,7 +306,7 @@ private void runStartAndEndKeyLimits(ViewRequest request, T expec } } //check the start key is as expected - assertEquals("The start key should be " + expectedStartKey, expectedStartKey, page - .getKeys().get(0)); + assertEquals(expectedStartKey, page.getKeys().get(0), "The start key should be " + + expectedStartKey); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ViewsTest.java b/cloudant-client/src/test/java/com/cloudant/tests/ViewsTest.java index 1f4f89f44..d9150d09e 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ViewsTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/ViewsTest.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 lightcouch.org - * Copyright © 2015, 2016 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -16,13 +16,14 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.cloudant.client.api.CloudantClient; import com.cloudant.client.api.Database; @@ -38,21 +39,22 @@ import com.cloudant.http.HttpConnectionInterceptorContext; import com.cloudant.test.main.RequiresCloudant; import com.cloudant.test.main.RequiresDB; -import com.cloudant.tests.util.CloudantClientResource; +import com.cloudant.tests.base.TestWithDbPerTest; +import com.cloudant.tests.extensions.CloudantClientExtension; +import com.cloudant.tests.extensions.DatabaseExtension; +import com.cloudant.tests.extensions.MockWebServerExtension; +import com.cloudant.tests.extensions.MultiExtension; import com.cloudant.tests.util.ContextCollectingInterceptor; -import com.cloudant.tests.util.DatabaseResource; import com.cloudant.tests.util.Utils; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.function.Executable; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -69,30 +71,29 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -@Category(RequiresDB.class) -public class ViewsTest { - - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); - @ClassRule - public static MockWebServer mockWebServer = new MockWebServer(); - - @Rule - public DatabaseResource dbResource = new DatabaseResource(clientResource); - - - private static ContextCollectingInterceptor cci = new ContextCollectingInterceptor(); - public static CloudantClientResource interceptedClient = new CloudantClientResource - (CloudantClientHelper.getClientBuilder().interceptors(cci)); - public static DatabaseResource interceptedDB = new DatabaseResource(interceptedClient); - @ClassRule - public static RuleChain intercepted = RuleChain.outerRule(interceptedClient).around - (interceptedDB); - private Database db; - - @Before - public void setUp() throws Exception { - db = dbResource.get(); +@RequiresDB +public class ViewsTest extends TestWithDbPerTest { + + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + public static ContextCollectingInterceptor cci = new ContextCollectingInterceptor(); + public static CloudantClientExtension interceptedClient = new CloudantClientExtension + (CloudantClientHelper.getClientBuilder() + .interceptors(cci)); + public static DatabaseExtension.PerClass interceptedDB = new DatabaseExtension.PerClass + (interceptedClient); + + @RegisterExtension + public static MultiExtension extensions = new MultiExtension( + mockWebServerExt, + interceptedClient, + interceptedDB + ); + + protected MockWebServer mockWebServer; + + @BeforeEach + public void beforeEach() throws Exception { + mockWebServer = mockWebServerExt.get(); Utils.putDesignDocs(db); } @@ -126,10 +127,11 @@ public void byKeys() throws Exception { public void byNonExistentAndExistingKey() throws Exception { init(); List> foos = db.getViewRequestBuilder("example", "foo") - .newRequest(Key.Type.STRING, Object.class).includeDocs(true).keys("key-1", "non-existent") + .newRequest(Key.Type.STRING, Object.class).includeDocs(true).keys("key-1", + "non-existent") .build().getResponse().getRows(); assertThat(foos.size(), is(1)); - for (ViewResponse.Row row: foos) { + for (ViewResponse.Row row : foos) { if (row.getError() == null) { assertThat(row.getKey().toString(), is("key-1")); } else { @@ -623,19 +625,18 @@ public void scalarValues() throws Exception { @Test public void viewWithNoResult_emptyList() throws IOException { init(); - assertEquals("The results list should be of length 0", 0, db.getViewRequestBuilder - ("example", "by_tag").newRequest(Key.Type.STRING, Object.class).keys - ("javax").build().getResponse().getKeys().size()); + assertEquals(0, db.getViewRequestBuilder("example", "by_tag").newRequest(Key.Type.STRING, + Object.class).keys("javax").build().getResponse().getKeys().size(), "The results " + + "list should be of length 0"); } @Test public void viewWithNoResult_nullSingleResult() throws IOException { init(); - assertNull("The single result should be null", db.getViewRequestBuilder("example", - "by_tag").newRequest(Key.Type.STRING, - Object.class).keys - ("javax").build().getSingleValue()); + assertNull(db.getViewRequestBuilder("example", "by_tag").newRequest(Key.Type.STRING, + Object.class).keys("javax").build().getSingleValue(), "The single result should " + + "be null"); } @@ -657,7 +658,7 @@ public void allDocs() throws Exception { .getIdsAndRevs(); assertThat(idsAndRevs.size(), not(0)); for (Map.Entry doc : idsAndRevs.entrySet()) { - assertNotNull("The document _rev value should not be null", doc.getValue()); + assertNotNull(doc.getValue(), "The document _rev value should not be null"); } } @@ -694,7 +695,7 @@ public void allDocsWithOneNonExistingKey() throws Exception { Map idsAndRevs = response.getIdsAndRevs(); assertThat(idsAndRevs.size(), is(1)); for (Map.Entry doc : idsAndRevs.entrySet()) { - assertNotNull("The document _rev value should not be null", doc.getValue()); + assertNotNull(doc.getValue(), "The document _rev value should not be null"); } assertThat(errors.size(), is(1)); @@ -867,7 +868,7 @@ private void assertJsonArrayKeysAndValues(ArrayList expectedJson, * @throws IOException */ @Test - @Category(RequiresCloudant.class) + @RequiresCloudant public void multiRequest() throws IOException { init(); ViewMultipleRequest multi = db.getViewRequestBuilder("example", "foo") @@ -878,11 +879,11 @@ public void multiRequest() throws IOException { .build(); int i = 1; List> responses = multi.getViewResponses(); - assertEquals("There should be 3 respones for 3 requests", 3, responses.size()); + assertEquals(3, responses.size(), "There should be 3 responses for 3 requests"); for (ViewResponse response : responses) { - assertEquals("There should be 1 row in each response", 1, response.getRows().size()); - assertEquals("The returned key should be key-" + i, "key-" + i, response.getKeys() - .get(0)); + assertEquals(1, response.getRows().size(), "There should be 1 row in each response"); + assertEquals("key-" + i, response.getKeys().get(0), "The returned key should be key-" + + i); i++; } } @@ -949,24 +950,38 @@ public void multiRequestBuildParametersSecond() { * Validate that an IllegalStateException is thrown if an attempt is made to build a multi * request without calling add() before build() with two requests. */ - @Test(expected = IllegalStateException.class) + @Test public void multiRequestBuildOnlyAfterAdd() { - ViewMultipleRequest multi = db.getViewRequestBuilder("example", "foo") - .newMultipleRequest(Key.Type.STRING, Object.class) - .keys("key-1").add() - .keys("key-2").build(); + assertThrows(IllegalStateException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewMultipleRequest multi = db.getViewRequestBuilder + ("example", "foo") + .newMultipleRequest(Key.Type.STRING, Object.class) + .keys("key-1").add() + .keys("key-2").build(); + } + }); } /** * Validate that an IllegalStateException is thrown if an attempt is made to build a multi * request without calling add() before build() with a single request with parameters. */ - @Test(expected = IllegalStateException.class) + @Test public void multiRequestBuildOnlyAfterAddSingle() { - ViewMultipleRequest multi = db.getViewRequestBuilder("example", "foo") - .newMultipleRequest(Key.Type.STRING, Object.class) - .keys("key-1") - .build(); + assertThrows(IllegalStateException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewMultipleRequest multi = db.getViewRequestBuilder + ("example", "foo") + .newMultipleRequest(Key.Type.STRING, Object.class) + .keys("key-1") + .build(); + } + }); } /** @@ -974,11 +989,18 @@ public void multiRequestBuildOnlyAfterAddSingle() { * request without calling add() before build() with a single request with no view request * parameter calls. */ - @Test(expected = IllegalStateException.class) + @Test public void multiRequestBuildOnlyAfterAddNoParams() { - ViewMultipleRequest multi = db.getViewRequestBuilder("example", "foo") - .newMultipleRequest(Key.Type.STRING, Object.class) - .build(); + assertThrows(IllegalStateException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewMultipleRequest multi = db.getViewRequestBuilder + ("example", "foo") + .newMultipleRequest(Key.Type.STRING, Object.class) + .build(); + } + }); } /** @@ -988,7 +1010,7 @@ public void multiRequestBuildOnlyAfterAddNoParams() { * @throws IOException */ @Test - @Category(RequiresCloudant.class) + @RequiresCloudant public void multiRequestMixedReduced() throws IOException { init(); ViewMultipleRequest multi = db.getViewRequestBuilder("example", "by_tag") @@ -999,15 +1021,15 @@ public void multiRequestMixedReduced() throws IOException { .build(); List> responses = multi.getViewResponses(); - assertEquals("There should be 2 respones for 2 requests", 2, responses.size()); + assertEquals(2, responses.size(), "There should be 2 responses for 2 requests"); List javaTagKeys = responses.get(0).getKeys(); - assertEquals("There should be 1 java tag result", 1, javaTagKeys.size()); - assertEquals("The key should be java", "java", javaTagKeys.get(0)); + assertEquals(1, javaTagKeys.size(), "There should be 1 java tag result"); + assertEquals("java", javaTagKeys.get(0), "The key should be java"); List allTagsReduced = responses.get(1).getValues(); - assertEquals("There should be 1 reduced result", 1, allTagsReduced.size()); - assertEquals("The result should be 4", 4, ((Number) allTagsReduced.get(0)).intValue()); + assertEquals(1, allTagsReduced.size(), "There should be 1 reduced result"); + assertEquals(4, ((Number) allTagsReduced.get(0)).intValue(), "The result should be 4"); } /** @@ -1022,9 +1044,9 @@ public void assertNoPagesOnUnpaginated() throws Exception { ViewResponse response = db.getViewRequestBuilder("example", "foo") .newRequest(Key.Type.STRING, Object.class).limit(2).build().getResponse(); - assertEquals("There should be 2 keys returned", 2, response.getKeys().size()); - assertFalse("There should be no additional pages", response.hasNextPage()); - assertNull("The next page should be null", response.nextPage()); + assertEquals(2, response.getKeys().size(), "There should be 2 keys returned"); + assertFalse(response.hasNextPage(), "There should be no additional pages"); + assertNull(response.nextPage(), "The next page should be null"); } /** @@ -1041,8 +1063,8 @@ public void enhancedForPagination() throws Exception { Object.class).rowsPerPage(1).build(); int i = 1; for (ViewResponse page : paginatedQuery.getResponse()) { - assertEquals("There should be one key on each page", 1, page.getKeys().size()); - assertEquals("The key should be key-" + i, "key-" + i, page.getKeys().get(0)); + assertEquals(1, page.getKeys().size(), "There should be one key on each page"); + assertEquals("key-" + i, page.getKeys().get(0), "The key should be key-" + i); i++; } } @@ -1052,11 +1074,18 @@ public void enhancedForPagination() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void rowsPerPageValidationMax() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newPaginatedRequest(Key.Type.STRING, - Object.class).rowsPerPage(Integer.MAX_VALUE).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newPaginatedRequest(Key.Type.STRING, + Object.class).rowsPerPage(Integer.MAX_VALUE).build(); + } + }); } /** @@ -1064,11 +1093,18 @@ public void rowsPerPageValidationMax() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void rowsPerPageValidationZero() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newPaginatedRequest(Key.Type.STRING, - Object.class).rowsPerPage(0).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newPaginatedRequest(Key.Type.STRING, + Object.class).rowsPerPage(0).build(); + } + }); } /** @@ -1076,11 +1112,18 @@ public void rowsPerPageValidationZero() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void rowsPerPageValidationMin() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newPaginatedRequest(Key.Type.STRING, - Object.class).rowsPerPage(-25).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newPaginatedRequest(Key.Type.STRING, + Object.class).rowsPerPage(-25).build(); + } + }); } /** @@ -1089,11 +1132,18 @@ public void rowsPerPageValidationMin() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void validationIncludeDocsReduceView() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newRequest(Key.Type.STRING, - Object.class).includeDocs(true).reduce(true).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newRequest(Key.Type.STRING, + Object.class).includeDocs(true).reduce(true).build(); + } + }); } /** @@ -1117,11 +1167,18 @@ public void noExceptionWhenReduceTrueByDefault() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void validationGroupLevelWithSimpleKey() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newRequest(Key.Type.STRING, - Object.class).groupLevel(1).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newRequest(Key.Type.STRING, + Object.class).groupLevel(1).build(); + } + }); } /** @@ -1130,11 +1187,18 @@ public void validationGroupLevelWithSimpleKey() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void validationGroupLevelWithNonReduce() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newRequest(Key.Type.STRING, - Object.class).reduce(false).groupLevel(1).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newRequest(Key.Type.STRING, + Object.class).reduce(false).groupLevel(1).build(); + } + }); } /** @@ -1143,11 +1207,18 @@ public void validationGroupLevelWithNonReduce() throws Exception { * * @throws Exception */ - @Test(expected = IllegalArgumentException.class) + @Test public void validationGroupWithNonReduce() throws Exception { - ViewRequest paginatedQuery = db.getViewRequestBuilder("example", "foo") - .newRequest(Key.Type.STRING, - Object.class).reduce(false).group(true).build(); + assertThrows(IllegalArgumentException.class, + new Executable() { + @Override + public void execute() throws Throwable { + ViewRequest paginatedQuery = db.getViewRequestBuilder + ("example", "foo") + .newRequest(Key.Type.STRING, + Object.class).reduce(false).group(true).build(); + } + }); } /** @@ -1195,8 +1266,8 @@ public void testComplexKeyContainingIntTokenPagination() throws Exception { // We want the last context HttpConnectionInterceptorContext context = cci.contexts.get(cci.contexts.size() - 1); String query = context.connection.url.getQuery(); - assertTrue("The query startkey should match.", query.contains("startkey=%5B%22uuid%22," + - "1005%5D")); + assertTrue(query.contains("startkey=%5B%22uuid%22," + "1005%5D"), "The query startkey " + + "should match."); } /** @@ -1258,13 +1329,16 @@ public void staleParameterValues() throws Exception { assertStaleParameter(viewBuilder.build(), noStaleParameter); // Test the OK stale argument supplied case - assertStaleParameter(viewBuilder.stale(SettableViewParameters.STALE_OK).build(), staleParameterOK); + assertStaleParameter(viewBuilder.stale(SettableViewParameters.STALE_OK).build(), + staleParameterOK); // Test the update_after stale argument supplied case - assertStaleParameter(viewBuilder.stale(SettableViewParameters.STALE_UPDATE_AFTER).build(), staleParameterUpdate); + assertStaleParameter(viewBuilder.stale(SettableViewParameters.STALE_UPDATE_AFTER).build() + , staleParameterUpdate); // Test the NO stale argument supplied case - assertStaleParameter(viewBuilder.stale(SettableViewParameters.STALE_NO).build(), noStaleParameter); + assertStaleParameter(viewBuilder.stale(SettableViewParameters.STALE_NO).build(), + noStaleParameter); } /** @@ -1283,9 +1357,9 @@ private void assertStaleParameter(ViewRequest viewRequest, Patt mockWebServer.enqueue(mockResponse); viewRequest.getSingleValue(); RecordedRequest request = mockWebServer.takeRequest(1, TimeUnit.SECONDS); - assertNotNull("There should have been a view request", request); - assertTrue("There request URL should match the pattern " + p.toString(), p.matcher - (request.getPath()).matches()); + assertNotNull(request, "There should have been a view request"); + assertTrue(p.matcher(request.getPath()).matches(), "There request URL should match the " + + "pattern " + p.toString()); } /** @@ -1350,6 +1424,6 @@ public void getIdsAndRevsForDeletedIDsWithAllDocs() throws Exception { // Do an _all_docs request using the 4 _ids of the generated docs. Map allDocsIdsAndRevs = database.getAllDocsRequestBuilder().keys(idsAndRevs .keySet().toArray(new String[4])).build().getResponse().getIdsAndRevs(); - assertEquals("The ids and revs should be equal", idsAndRevs, allDocsIdsAndRevs); + assertEquals(idsAndRevs, allDocsIdsAndRevs, "The ids and revs should be equal"); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDb.java b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDb.java new file mode 100644 index 000000000..b701fa502 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDb.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2018 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.base; + +import com.cloudant.client.api.CloudantClient; +import com.cloudant.client.api.Database; +import com.cloudant.tests.extensions.CloudantClientExtension; + +public abstract class TestWithDb { + + protected static CloudantClientExtension clientResource = new CloudantClientExtension(); + + protected static CloudantClient account; + + protected static Database db; + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDbPerClass.java b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDbPerClass.java new file mode 100644 index 000000000..7d0a000d7 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDbPerClass.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2018 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.base; + +import com.cloudant.tests.extensions.DatabaseExtension; +import com.cloudant.tests.extensions.MultiExtension; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +// Base class for tests which require a DB which is used throughout the lifetime of the test class +public class TestWithDbPerClass extends TestWithDb { + + protected static DatabaseExtension.PerClass dbResource = new DatabaseExtension.PerClass + (clientResource); + + @RegisterExtension + protected static MultiExtension perClassExtensions = new MultiExtension(clientResource, + dbResource); + + @BeforeAll + public static void testWithDbBeforeAll() { + account = clientResource.get(); + db = dbResource.get(); + } + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDbPerTest.java b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDbPerTest.java new file mode 100644 index 000000000..3f7965dd4 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithDbPerTest.java @@ -0,0 +1,43 @@ +/* + * Copyright © 2018 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.base; + +import com.cloudant.tests.extensions.DatabaseExtension; +import com.cloudant.tests.extensions.MultiExtension; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; + +// Base class for tests which require a new DB for each test method +public class TestWithDbPerTest extends TestWithDb { + + protected static DatabaseExtension.PerTest dbResource = new DatabaseExtension.PerTest + (clientResource); + + @RegisterExtension + protected static MultiExtension perTestExtensions = new MultiExtension(clientResource, + dbResource); + + @BeforeEach + public void testWithDbBeforeEach() { + db = dbResource.get(); + } + + @BeforeAll + public static void testWithDbBeforeAll() { + account = clientResource.get(); + } + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithMockedServer.java b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithMockedServer.java new file mode 100644 index 000000000..c53b28ce3 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithMockedServer.java @@ -0,0 +1,58 @@ +/* + * Copyright © 2017, 2018 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.base; + +import com.cloudant.client.api.CloudantClient; +import com.cloudant.client.api.Database; +import com.cloudant.tests.extensions.CloudantClientMockServerExtension; +import com.cloudant.tests.extensions.DatabaseExtension; +import com.cloudant.tests.extensions.MockWebServerExtension; +import com.cloudant.tests.extensions.MultiExtension; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; + +import okhttp3.mockwebserver.MockWebServer; + +/** + * Class of tests that run against a MockWebServer. This class handles set up and tear down of the + * mock server and associated client and db objects before/after each test. + */ +public abstract class TestWithMockedServer { + + public static MockWebServerExtension mockWebServerExt = new MockWebServerExtension(); + public static CloudantClientMockServerExtension clientResource = new + CloudantClientMockServerExtension(mockWebServerExt); + public static DatabaseExtension.PerTest dbResource = new DatabaseExtension.PerTest + (clientResource); + @RegisterExtension + public static MultiExtension extensions = new MultiExtension( + mockWebServerExt, + clientResource, + dbResource + ); + + protected MockWebServer server; + protected CloudantClient client; + protected Database db; + + @BeforeEach + public void beforeEach() { + server = mockWebServerExt.get(); + client = clientResource.get(); + db = dbResource.get(); + } + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/ReplicateBaseTest.java b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithReplication.java similarity index 53% rename from cloudant-client/src/test/java/com/cloudant/tests/ReplicateBaseTest.java rename to cloudant-client/src/test/java/com/cloudant/tests/base/TestWithReplication.java index 227e697a9..ad1ba61f9 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/ReplicateBaseTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/base/TestWithReplication.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -12,42 +12,45 @@ * and limitations under the License. */ -package com.cloudant.tests; +package com.cloudant.tests.base; -import static org.junit.Assert.assertTrue; +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.views.Key; import com.cloudant.client.api.views.ViewResponse; -import com.cloudant.tests.util.CloudantClientResource; -import com.cloudant.tests.util.DatabaseResource; +import com.cloudant.tests.extensions.CloudantClientExtension; +import com.cloudant.tests.extensions.DatabaseExtension; +import com.cloudant.tests.extensions.MultiExtension; import com.cloudant.tests.util.Utils; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; -public class ReplicateBaseTest { +public class TestWithReplication { - @ClassRule - public static CloudantClientResource clientResource = new CloudantClientResource(); - protected CloudantClient account = clientResource.get(); + protected static CloudantClientExtension clientResource = new CloudantClientExtension(); + protected static CloudantClient account; - @Rule - public DatabaseResource db1Resource = new DatabaseResource(clientResource); - @Rule - public DatabaseResource db2Resource = new DatabaseResource(clientResource); + protected static DatabaseExtension.PerClass db1Resource = new DatabaseExtension.PerClass + (clientResource); + protected static DatabaseExtension.PerClass db2Resource = new DatabaseExtension.PerClass + (clientResource); - protected Database db1; + @RegisterExtension + public static MultiExtension extensions = new MultiExtension(clientResource, db1Resource, + db2Resource); - protected Database db2; + protected static Database db1; + protected static Database db2; protected static String db1URI; protected static String db2URI; - @Before - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { + account = clientResource.get(); db1 = db1Resource.get(); db1URI = db1Resource.getDbURIWithUserInfo(); @@ -56,6 +59,7 @@ public void setUp() throws Exception { db2 = db2Resource.get(); db2URI = db2Resource.getDbURIWithUserInfo(); Utils.putDesignDocs(db2); + } protected void assertConflictsNotZero(Database db) throws Exception { @@ -63,7 +67,7 @@ protected void assertConflictsNotZero(Database db) throws Exception { ("conflicts", "conflict").newRequest(Key.Type.COMPLEX, String.class).build() .getResponse(); int conflictCount = conflicts.getRows().size(); - assertTrue("There should be at least 1 conflict, there were " + conflictCount, - conflictCount > 0); + assertTrue(conflictCount > 0, "There should be at least 1 conflict, there were " + + conflictCount); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/extensions/AbstractClientExtension.java b/cloudant-client/src/test/java/com/cloudant/tests/extensions/AbstractClientExtension.java new file mode 100644 index 000000000..160c34548 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/extensions/AbstractClientExtension.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2018 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.extensions; + +import com.cloudant.client.api.CloudantClient; + +public abstract class AbstractClientExtension { + + public abstract CloudantClient get(); + + public abstract String getBaseURIWithUserInfo(); + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/CloudantClientResource.java b/cloudant-client/src/test/java/com/cloudant/tests/extensions/CloudantClientExtension.java similarity index 67% rename from cloudant-client/src/test/java/com/cloudant/tests/util/CloudantClientResource.java rename to cloudant-client/src/test/java/com/cloudant/tests/extensions/CloudantClientExtension.java index 375d16fb2..4083ac700 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/CloudantClientResource.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/extensions/CloudantClientExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -12,38 +12,44 @@ * and limitations under the License. */ -package com.cloudant.tests.util; +package com.cloudant.tests.extensions; import com.cloudant.client.api.ClientBuilder; import com.cloudant.client.api.CloudantClient; import com.cloudant.tests.CloudantClientHelper; -import org.junit.rules.ExternalResource; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class CloudantClientExtension extends AbstractClientExtension implements + BeforeAllCallback, AfterAllCallback { -public class CloudantClientResource extends ExternalResource { private ClientBuilder clientBuilder; + private CloudantClient client; - public CloudantClientResource() { + public CloudantClientExtension() { this.clientBuilder = CloudantClientHelper.getClientBuilder(); } - public CloudantClientResource(ClientBuilder clientBuilder) { + public CloudantClientExtension(ClientBuilder clientBuilder) { this.clientBuilder = clientBuilder; } @Override - public void before() { - client = clientBuilder.build(); + public CloudantClient get() { + return this.client; } @Override - public void after() { + public void afterAll(ExtensionContext context) throws Exception { client.shutdown(); } - public CloudantClient get() { - return this.client; + @Override + public void beforeAll(ExtensionContext context) throws Exception { + client = clientBuilder.build(); } /** @@ -52,6 +58,7 @@ public CloudantClient get() { * * @return String representation of the URI */ + @Override public String getBaseURIWithUserInfo() { return CloudantClientHelper.SERVER_URI_WITH_USER_INFO; } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/extensions/CloudantClientMockServerExtension.java b/cloudant-client/src/test/java/com/cloudant/tests/extensions/CloudantClientMockServerExtension.java new file mode 100644 index 000000000..a3270d7e5 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/extensions/CloudantClientMockServerExtension.java @@ -0,0 +1,62 @@ +/* + * Copyright © 2017, 2018 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.extensions; + +import com.cloudant.client.api.CloudantClient; +import com.cloudant.tests.CloudantClientHelper; +import com.cloudant.tests.util.MockWebServerResources; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import okhttp3.mockwebserver.MockWebServer; + +public class CloudantClientMockServerExtension extends AbstractClientExtension implements + BeforeEachCallback, AfterEachCallback { + + private final MockWebServerExtension mockWebServerExt; + private MockWebServer mockWebServer; + private CloudantClient client; + + public CloudantClientMockServerExtension(MockWebServerExtension mockWebServerExt) { + this.mockWebServerExt = mockWebServerExt; + } + + @Override + public void beforeEach(ExtensionContext ctx) { + this.mockWebServer = this.mockWebServerExt.get(); + this.client = CloudantClientHelper.newMockWebServerClientBuilder(this.mockWebServer) + .build(); + } + + @Override + public void afterEach(ExtensionContext ctx) { + // Queue a 200 for the _session DELETE that is called on shutdown. + mockWebServer.enqueue(MockWebServerResources.JSON_OK); + } + + @Override + public CloudantClient get() { + return client; + } + + @Override + public String getBaseURIWithUserInfo() { + return CloudantClientHelper.SERVER_URI_WITH_USER_INFO; + } + + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/DatabaseResource.java b/cloudant-client/src/test/java/com/cloudant/tests/extensions/DatabaseExtension.java similarity index 59% rename from cloudant-client/src/test/java/com/cloudant/tests/util/DatabaseResource.java rename to cloudant-client/src/test/java/com/cloudant/tests/extensions/DatabaseExtension.java index 553369090..da30a0490 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/DatabaseResource.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/extensions/DatabaseExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -12,25 +12,29 @@ * and limitations under the License. */ -package com.cloudant.tests.util; +package com.cloudant.tests.extensions; import com.cloudant.client.api.CloudantClient; import com.cloudant.client.api.Database; +import com.cloudant.tests.util.Utils; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class DatabaseResource extends ExternalResource { +public class DatabaseExtension { // Get the maximum length for a database name, defaults to 128 chars private static final int DB_NAME_SIZE = Integer.parseInt(System.getProperty("test.db.name" + ".length", "128")); - private final CloudantClientResource clientResource; + private final AbstractClientExtension clientResource; + private CloudantClient client; private String databaseName = Utils.generateUUID(); private Database database; @@ -52,25 +56,16 @@ public class DatabaseResource extends ExternalResource { * * @param clientResource */ - public DatabaseResource(CloudantClientResource clientResource) { + + protected DatabaseExtension(CloudantClientExtension clientResource) { this.clientResource = clientResource; } - public DatabaseResource(CloudantClientMockServerResource mockServerResource) { + protected DatabaseExtension(CloudantClientMockServerExtension mockServerResource) { this.clientResource = mockServerResource; this.mock = true; } - @Override - public Statement apply(Statement base, Description description) { - String testClassName = description.getClassName(); - String testMethodName = description.getMethodName(); - String uniqueSuffix = Utils.generateUUID(); - databaseName = sanitizeDbName(testClassName + "-" + (testMethodName == null ? "" : - testMethodName + "-") + uniqueSuffix); - return super.apply(base, description); - } - /** * The database must start with a letter and can only contain lowercase letters (a-z), digits * (0-9) and the following characters _, $, (, ), +, -, and /. @@ -79,6 +74,14 @@ public Statement apply(Statement base, Description description) { * @return sanitized name */ private static String sanitizeDbName(String name) { + //lowercase to remove any caps that will not be permitted + name = name.toLowerCase(Locale.ENGLISH); + //replace any non alphanum with underscores + name = name.replaceAll("[^a-z0-9]", "_"); + //squash multiple underscores + name = name.replaceAll("(_){2,}", "_"); + //remove leading underscore as it is reserved for internal couch use + name = name.replaceAll("^_", ""); //database name is limited to 128 characters on the Cloudant service //forego the package name in favour of test details and UUID int excess; @@ -90,15 +93,12 @@ private static String sanitizeDbName(String name) { name = m.group(1); } } - //lowercase to remove any caps that will not be permitted - name = name.toLowerCase(Locale.ENGLISH); - //replace any characters that are not permitted with underscores - name = name.replaceAll("[^a-z0-9_\\$\\(\\)\\+\\-/]", "_"); return name; } - @Override - public void before() { + public void before(ExtensionContext context) { + String uniqueSuffix = Utils.generateUUID(); + databaseName = sanitizeDbName(String.format("%s-%s", context.getUniqueId(), uniqueSuffix)); client = clientResource.get(); if (!mock) { database = client.database(databaseName, true); @@ -107,8 +107,7 @@ public void before() { } } - @Override - public void after() { + public void after(ExtensionContext context) { if (!mock) { client.deleteDB(databaseName); } @@ -130,6 +129,47 @@ public String getDatabaseName() { * @return the URI for the DB with creds */ public String getDbURIWithUserInfo() throws Exception { - return clientResource.getBaseURIWithUserInfo() + "/" + getDatabaseName(); + String info = clientResource.getBaseURIWithUserInfo() + "/" + getDatabaseName(); + return info; + } + + public static class PerClass extends DatabaseExtension implements BeforeAllCallback, + AfterAllCallback { + + public PerClass(CloudantClientExtension clientResource) { + super(clientResource); + } + + @Override + public void afterAll(ExtensionContext extensionContext) throws Exception { + super.after(extensionContext); + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + super.before(extensionContext); + } + } + + public static class PerTest extends DatabaseExtension implements BeforeEachCallback, + AfterEachCallback { + + public PerTest(CloudantClientExtension clientResource) { + super(clientResource); + } + + public PerTest(CloudantClientMockServerExtension mockServerResource) { + super(mockServerResource); + } + + @Override + public void afterEach(ExtensionContext extensionContext) throws Exception { + super.after(extensionContext); + } + + @Override + public void beforeEach(ExtensionContext extensionContext) throws Exception { + super.before(extensionContext); + } } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/extensions/MockWebServerExtension.java b/cloudant-client/src/test/java/com/cloudant/tests/extensions/MockWebServerExtension.java new file mode 100644 index 000000000..0ea96dfb6 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/extensions/MockWebServerExtension.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2018 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.extensions; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import okhttp3.mockwebserver.MockWebServer; + +public class MockWebServerExtension implements BeforeEachCallback, AfterEachCallback { + + private MockWebServer mockWebServer; + private boolean started = false; + + public MockWebServerExtension() { + } + + public MockWebServer get() { + return this.mockWebServer; + } + + // NB beforeEach/afterEach emulate before and after in the actual mock, because we can't call + // these directly as they are marked as protected + + @Override + public synchronized void beforeEach(ExtensionContext context) throws Exception { + this.mockWebServer = new MockWebServer(); + if (started) { + System.err.println("*** WARNING: MockWebServer already started"); + return; + } + this.mockWebServer.start(); + started = true; + } + + @Override + public synchronized void afterEach(ExtensionContext context) throws Exception { + this.mockWebServer.shutdown(); + started = false; + } + +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/extensions/MultiExtension.java b/cloudant-client/src/test/java/com/cloudant/tests/extensions/MultiExtension.java new file mode 100644 index 000000000..feb051cb6 --- /dev/null +++ b/cloudant-client/src/test/java/com/cloudant/tests/extensions/MultiExtension.java @@ -0,0 +1,88 @@ +/* + * Copyright © 2018 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.extensions; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class MultiExtension implements Extension, AfterAllCallback, AfterEachCallback, + AfterTestExecutionCallback, BeforeAllCallback, BeforeEachCallback, + BeforeTestExecutionCallback { + + private final Extension[] extensions; + + public MultiExtension(Extension... extensions) { + this.extensions = extensions; + } + + @Override + public void afterAll(ExtensionContext extensionContext) throws Exception { + for (Extension extension : extensions) { + if (extension instanceof AfterAllCallback) { + ((AfterAllCallback) extension).afterAll(extensionContext); + } + } + } + + @Override + public void afterEach(ExtensionContext extensionContext) throws Exception { + for (Extension extension : extensions) { + if (extension instanceof AfterEachCallback) { + ((AfterEachCallback) extension).afterEach(extensionContext); + } + } + } + + @Override + public void afterTestExecution(ExtensionContext extensionContext) throws Exception { + for (Extension extension : extensions) { + if (extension instanceof AfterTestExecutionCallback) { + ((AfterTestExecutionCallback) extension).afterTestExecution(extensionContext); + } + } + } + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + for (Extension extension : extensions) { + if (extension instanceof BeforeAllCallback) { + ((BeforeAllCallback) extension).beforeAll(extensionContext); + } + } + } + + @Override + public void beforeEach(ExtensionContext extensionContext) throws Exception { + for (Extension extension : extensions) { + if (extension instanceof BeforeEachCallback) { + ((BeforeEachCallback) extension).beforeEach(extensionContext); + } + } + } + + @Override + public void beforeTestExecution(ExtensionContext extensionContext) throws Exception { + for (Extension extension : extensions) { + if (extension instanceof BeforeTestExecutionCallback) { + ((BeforeTestExecutionCallback) extension).beforeTestExecution(extensionContext); + } + } + } +} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/CheckPagination.java b/cloudant-client/src/test/java/com/cloudant/tests/util/CheckPagination.java index 949d02127..ff4e94de1 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/CheckPagination.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/util/CheckPagination.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -15,10 +15,10 @@ package com.cloudant.tests.util; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.cloudant.client.api.Database; import com.cloudant.client.api.views.Key; @@ -116,9 +116,9 @@ public CheckPagination stateless(boolean stateless) { * @return the last page in the view. */ private void checkPagesForward(int currentPage, - int numberOfPages, - int docCount, - int docsPerPage) throws IOException { + int numberOfPages, + int docCount, + int docsPerPage) throws IOException { for (int i = 0; i < numberOfPages; ++i) { nextPage(); checkPage(page, docCount, docsPerPage, currentPage + i + 1, descending); @@ -135,9 +135,9 @@ private void checkPagesForward(int currentPage, * @return the first page in the view */ private void checkPagesBackward(int currentPage, - int numberOfPages, - int docCount, - int docsPerPage) throws IOException { + int numberOfPages, + int docCount, + int docsPerPage) throws IOException { for (int i = 0; i < numberOfPages; ++i) { previousPage(); checkPage(page, docCount, docsPerPage, currentPage - i - 1, descending); @@ -198,8 +198,8 @@ private void checkDocumentTitles(ViewResponse page, int docCount, int docsPerPag } List resultList = page.getDocsAs(Foo.class); for (int i = 0; i < resultList.size(); ++i) { - assertEquals("Document titles do not match", ViewsTest.docTitle(descending ? offset-- : - offset++), resultList.get(i).getTitle()); + assertEquals(ViewsTest.docTitle(descending ? offset-- : + offset++), resultList.get(i).getTitle(), "Document titles do not match"); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/CloudantClientMockServerResource.java b/cloudant-client/src/test/java/com/cloudant/tests/util/CloudantClientMockServerResource.java deleted file mode 100644 index 0141a3c44..000000000 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/CloudantClientMockServerResource.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2017 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 com.cloudant.tests.CloudantClientHelper; - -import okhttp3.mockwebserver.MockWebServer; - -public class CloudantClientMockServerResource extends CloudantClientResource { - - private final MockWebServer server; - - public CloudantClientMockServerResource(MockWebServer mockWebServer) { - super(CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer)); - this.server = mockWebServer; - } - - @Override - public void after() { - // Queue a 200 for the _session DELETE that is called on shutdown. - server.enqueue(MockWebServerResources.JSON_OK); - super.after(); - } -} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/HttpFactoryParameterizedTest.java b/cloudant-client/src/test/java/com/cloudant/tests/util/HttpFactoryParameterizedTest.java index 19c11921e..ae1cb50d1 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/HttpFactoryParameterizedTest.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/util/HttpFactoryParameterizedTest.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016 IBM Corp. All rights reserved. + * Copyright © 2016, 2018 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 @@ -14,27 +14,24 @@ package com.cloudant.tests.util; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.cloudant.http.internal.DefaultHttpUrlConnectionFactory; import com.cloudant.http.internal.ok.OkHelper; +import com.cloudant.tests.base.TestWithDbPerClass; -import org.junit.Before; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; import mockit.Mock; import mockit.MockUp; -@RunWith(Parameterized.class) -public abstract class HttpFactoryParameterizedTest { +public abstract class HttpFactoryParameterizedTest extends TestWithDbPerClass { /** * A parameter governing whether to allow okhttp or not. This lets us exercise both * HttpURLConnection types in these tests. */ - @Parameterized.Parameter - public boolean okUsable; + public boolean isOkUsable; /** * A mock OkHelper that always returns false to force use of the JVM HttpURLConnection @@ -47,14 +44,16 @@ public static boolean isOkUsable() { } } - @Before - public void changeHttpConnectionFactory() throws Exception { - if (!okUsable) { + @BeforeEach + public void changeHttpConnectionFactory(boolean isOkUsable) throws Exception { + this.isOkUsable = isOkUsable; + if (!isOkUsable) { // New up the mock that will stop okhttp's factory being used new OkHelperMock(); } // Verify that we are getting the behaviour we expect. - assertEquals("The OK usable value was not what was expected for the test parameter.", - okUsable, OkHelper.isOkUsable()); + assertEquals( + isOkUsable, OkHelper.isOkUsable(), "The OK usable value was not what was expected" + + " for the test parameter."); } } diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/IamSystemPropertyMock.java b/cloudant-client/src/test/java/com/cloudant/tests/util/IamSystemPropertyMock.java index 734457610..51585704b 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/IamSystemPropertyMock.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/util/IamSystemPropertyMock.java @@ -32,7 +32,7 @@ public class IamSystemPropertyMock extends MockUp { public void setMockIamTokenEndpointUrl(String mockIamTokenEndpointUrl) { this.mockIamTokenEndpointUrl.set(mockIamTokenEndpointUrl); } - + @Mock public String getProperty(Invocation inv, String key) { if (key.equals("com.cloudant.client.iamserver")) { diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/MockedServerTest.java b/cloudant-client/src/test/java/com/cloudant/tests/util/MockedServerTest.java deleted file mode 100644 index 020097b4a..000000000 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/MockedServerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2017 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 com.cloudant.client.api.CloudantClient; -import com.cloudant.client.api.Database; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.RuleChain; - -import okhttp3.mockwebserver.MockWebServer; - -/** - * Class of tests that run against a MockWebServer. This class handles set up and tear down of the - * mock server and associated client and db objects before/after each test. - */ -public abstract class MockedServerTest { - - protected MockWebServer server = new MockWebServer(); - protected CloudantClient client; - protected Database db; - - protected CloudantClientMockServerResource clientResource = new - CloudantClientMockServerResource(server); - protected DatabaseResource dbResource = new DatabaseResource(clientResource); - @Rule - public RuleChain chain = RuleChain.outerRule(server).around(clientResource).around(dbResource); - - @Before - public void setup() throws Exception { - client = clientResource.get(); - db = dbResource.get(); - } -} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/TestLog.java b/cloudant-client/src/test/java/com/cloudant/tests/util/TestLog.java deleted file mode 100644 index 0759afcb2..000000000 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/TestLog.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.logging.Logger; - -public class TestLog implements TestRule { - - //default to a logger name for this class, but replace when the rule is applied - public Logger logger = Logger.getLogger(TestLog.class.getName()); - - @Override - public Statement apply(final Statement base, Description description) { - //set a logger for the name of the test class - logger = Logger.getLogger(description.getClassName()); - return base; - } - -} diff --git a/cloudant-client/src/test/java/com/cloudant/tests/util/Utils.java b/cloudant-client/src/test/java/com/cloudant/tests/util/Utils.java index a274ab000..687aab4da 100644 --- a/cloudant-client/src/test/java/com/cloudant/tests/util/Utils.java +++ b/cloudant-client/src/test/java/com/cloudant/tests/util/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 IBM Corp. All rights reserved. + * Copyright © 2015, 2018 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 @@ -16,10 +16,10 @@ 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 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; @@ -186,14 +186,16 @@ public static T findDocumentWithRetries(Database db, String docId, Class */ 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); + 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 db database to put the design docs * @param directory location of design docs */ public static void putDesignDocs(Database db, File directory) throws FileNotFoundException { @@ -212,7 +214,7 @@ public static void putDesignDocs(Database db) throws FileNotFoundException { } public static void assertOKResponse(Response r) throws Exception { - assertTrue("The response code should be 2XX was " + r.getStatusCode(), r.getStatusCode() - / 100 == 2); + assertTrue(r.getStatusCode() + / 100 == 2, "The response code should be 2XX was " + r.getStatusCode()); } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d3b83982b..8252d7e25 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7e36e6166..bf3de2183 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Oct 06 15:51:02 MSK 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip diff --git a/gradlew b/gradlew index 27309d923..cccdd3d51 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f6d5974e7..e95643d6a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -49,7 +49,6 @@ goto fail @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line