Further attempt at making tests reliable.
MODIFY
dd-java-agent/instrumentation/couchbase-2.0/src/test/groovy/CouchbaseClientTest.groovy
dd-java-agent/instrumentation/couchbase-2.0/src/test/groovy/CouchbaseClientTest.groovy
import com.couchbase.client.java.Bucket
import com.couchbase.client.java.document.JsonDocument
import com.couchbase.client.java.document.json.JsonObject
+import com.couchbase.client.java.query.N1qlParams
import com.couchbase.client.java.query.N1qlQuery
-import com.couchbase.client.java.query.N1qlQueryRow
+import com.couchbase.client.java.query.consistency.ScanConsistency
import util.AbstractCouchbaseTest
import static datadog.trace.agent.test.asserts.ListWriterAssert.assertTraces
@@ -120,15 +121,15 @@ class CouchbaseClientTest extends AbstractCouchbaseTest {
N1qlQuery.parameterized(
"SELECT * FROM `${bkt.name()}` WHERE hello = \$hello",
JsonObject.create()
- .put("hello", "world")
+ .put("hello", "world"),
+ N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS)
)
)
then:
result.parseSuccess()
result.finalSuccess()
- N1qlQueryRow row = result.first()
- row.value().get(bkt.name()).get("hello") == "world"
+ result.first().value().get(bkt.name()).get("hello") == "world"
and:
assertTraces(TEST_WRITER, 1) {

MODIFY
dd-java-agent/instrumentation/couchbase-2.0/src/test/groovy/springdata/CouchbaseSpringTemplateTest.groovy
dd-java-agent/instrumentation/couchbase-2.0/src/test/groovy/springdata/CouchbaseSpringTemplateTest.groovy
package springdata
+
import com.couchbase.client.java.Bucket
import org.springframework.data.couchbase.core.CouchbaseTemplate
import spock.lang.Shared
@@ -23,6 +24,7 @@ class CouchbaseSpringTemplateTest extends AbstractCouchbaseTest {
new CouchbaseTemplate(info, bucketEphemeral)]
}
+
def "test write/read #name"() {
setup:
def doc = new Doc()

MODIFY
dd-java-agent/instrumentation/couchbase-2.0/src/test/groovy/util/AbstractCouchbaseTest.groovy
dd-java-agent/instrumentation/couchbase-2.0/src/test/groovy/util/AbstractCouchbaseTest.groovy
@@ -21,6 +21,9 @@ import com.couchbase.client.java.cluster.UserSettings
import com.couchbase.client.java.env.CouchbaseEnvironment
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment
import com.couchbase.client.java.query.Index
+import com.couchbase.client.java.query.N1qlParams
+import com.couchbase.client.java.query.N1qlQuery
+import com.couchbase.client.java.query.consistency.ScanConsistency
import com.couchbase.client.java.view.DefaultView
import com.couchbase.client.java.view.DesignDocument
import datadog.trace.agent.test.AgentTestRunner
@@ -38,9 +41,10 @@ import java.util.concurrent.TimeUnit
// Do not run tests locally on Java7 since testcontainers are not compatible with Java7
// It is fine to run on CI because CI provides couchbase externally, not through testcontainers
@Requires({ "true" == System.getenv("CI") || jvm.java8Compatible })
+
// Couchbase client sometimes throws com.couchbase.client.java.error.TemporaryFailureException.
// Lets automatically retry to avoid the test from failing completely.
-@RetryOnFailure(delaySeconds = 1)
+@RetryOnFailure(times = 3, delaySeconds = 1)
abstract class AbstractCouchbaseTest extends AgentTestRunner {
private static final USERNAME = "Administrator"
@@ -97,7 +101,7 @@ abstract class AbstractCouchbaseTest extends AgentTestRunner {
When building locally, however, we need to take matters into our own hands
and we use 'testcontainers' for this.
*/
- if ("true" != System.getenv("CI")) {
+ if (false && "true" != System.getenv("CI")) {
couchbaseContainer = new CouchbaseContainer(
clusterUsername: USERNAME,
clusterPassword: PASSWORD)
@@ -113,14 +117,14 @@ abstract class AbstractCouchbaseTest extends AgentTestRunner {
}
manager = cluster.clusterManager(USERNAME, PASSWORD)
- resetBucket(cluster, bucketCouchbase)
- resetBucket(cluster, bucketMemcache)
- resetBucket(cluster, bucketEphemeral)
+ resetBucket(bucketCouchbase)
+ resetBucket(bucketMemcache)
+ resetBucket(bucketEphemeral)
}
def cleanupSpec() {
// Close all buckets and disconnect
- cluster.disconnect()
+ cluster?.disconnect()
couchbaseContainer?.stop()
}
@@ -149,57 +153,77 @@ abstract class AbstractCouchbaseTest extends AgentTestRunner {
protected void initCluster() {
- assert callCouchbaseRestAPI("/pools/default", "memoryQuota=1000&indexMemoryQuota=300") == 200
- // This one fails if already initialized, so don't assert.
- callCouchbaseRestAPI("/node/controller/setupServices", "services=kv%2Cindex%2Cn1ql%2Cfts")
- assert callCouchbaseRestAPI("/settings/web", "username=$USERNAME&password=$PASSWORD&port=8091") == 200
- assert callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized") == 200
+ callCouchbaseRestAPI("/pools/default", new FormBody.Builder().add("memoryQuota", "1000").add("indexMemoryQuota", "300").build())
+ callCouchbaseRestAPI("/node/controller/setupServices", new FormBody.Builder().add("services", "kv,index,n1ql,fts").build(), ["cannot change node services after cluster is provisioned"])
+ callCouchbaseRestAPI("/settings/web", new FormBody.Builder().add("username", USERNAME).add("password", PASSWORD).add("port", "8091").build())
+ callCouchbaseRestAPI("/settings/indexes", RequestBody.create(FormBody.CONTENT_TYPE, "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"))
}
/**
* Adapted from CouchbaseContainer.callCouchbaseRestAPI()
*/
- protected int callCouchbaseRestAPI(String url, String payload) throws IOException {
+ protected void callCouchbaseRestAPI(String url, RequestBody body, expectations = null) throws IOException {
String authToken = Base64.encode((USERNAME + ":" + PASSWORD).getBytes("UTF-8"))
def request = new Request.Builder()
.url("http://localhost:8091$url")
.header("Authorization", "Basic " + authToken)
- .post(RequestBody.create(FormBody.CONTENT_TYPE, payload))
+ .post(body)
.build()
def response = HTTP_CLIENT.newCall(request).execute()
- return response.code()
+ try {
+ if (expectations != null) {
+ if (expectations instanceof Integer) {
+ assert response.code() == expectations
+ } else if (expectations instanceof String) {
+ assert response.body().string() == expectations
+ }
+ } else {
+ assert response.code() == 200
+ }
+ } finally {
+ response.close()
+ }
}
/**
* Copied from CouchbaseContainer.callCouchbaseRestAPI()
*/
- protected void resetBucket(CouchbaseCluster cluster, BucketSettings bucketSetting) {
- ClusterManager clusterManager = cluster.clusterManager(USERNAME, PASSWORD)
-
+ protected void resetBucket(BucketSettings bucketSetting) {
+ try {
// Remove existing Bucket
- if (clusterManager.hasBucket(bucketSetting.name())) {
- clusterManager.removeBucket(bucketSetting.name())
+ if (manager.hasBucket(bucketSetting.name())) {
+ manager.removeBucket(bucketSetting.name())
+ assert !manager.hasBucket(bucketSetting.name())
}
- assert !clusterManager.hasBucket(bucketSetting.name())
- // Insert Bucket... This generates a LOT of traces
- BucketSettings bucketSettings = clusterManager.insertBucket(bucketSetting)
+ // Calling the rest api because the library is slow and creates a lot of traces.
+ callCouchbaseRestAPI("/pools/default/buckets", new FormBody.Builder()
+ .add("name", bucketSetting.name())
+ .add("bucketType", bucketSetting.type().name().toLowerCase())
+ .add("ramQuotaMB", String.valueOf(bucketSetting.quota()))
+ .add("authType", "sasl")
+ .add("saslPassword", bucketSetting.password())
+ .build(), 202)
// Insert Bucket admin user
UserSettings userSettings = UserSettings.build().password(bucketSetting.password())
.roles([new UserRole("bucket_full_access", bucketSetting.name())])
- clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings)
-
- Bucket bucket = cluster.openBucket(bucketSettings.name(), bucketSettings.password())
+ manager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings)
- if (BucketType.COUCHBASE.equals(bucketSettings.type())) {
- bucket.query(Index.dropPrimaryIndex(bucketSetting.name()))
- def result = bucket.query(Index.createPrimaryIndex().on(bucketSetting.name()))
- if (!result.finalSuccess()) {
- // Lets try again.
- result = bucket.query(Index.createPrimaryIndex().on(bucketSetting.name()))
+ } catch (Exception e) {
+ throw new RuntimeException(e)
}
- assert result.finalSuccess()
+
+
+ Bucket bucket = cluster.openBucket(bucketSetting.name(), bucketSetting.password())
+
+ if (BucketType.COUCHBASE.equals(bucketSetting.type())) {
+ try {
+ bucket.query(N1qlQuery.simple(Index.dropPrimaryIndex(bucketSetting.name()),
+ N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS)))
+
+ assert bucket.query(N1qlQuery.simple(Index.createPrimaryIndex().on(bucketSetting.name()),
+ N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS))).finalSuccess()
// Create view for SpringRepository's findAll()
bucket.bucketManager().insertDesignDocument(
@@ -213,11 +237,13 @@ abstract class AbstractCouchbaseTest extends AgentTestRunner {
'''
)))
)
+ TEST_WRITER.waitForTraces(13)
+ } catch (Exception e) {
+ throw new RuntimeException(e)
+ }
+ } else {
+ TEST_WRITER.waitForTraces(4)
}
-
- // We don't have a good way to tell that all traces are reported
- // since we don't know how many there will be.
- Thread.sleep(150)
TEST_WRITER.clear() // remove traces generated by insertBucket
}
}

MODIFY
dd-java-agent/testing/testing.gradle
dd-java-agent/testing/testing.gradle
@@ -19,7 +19,7 @@ dependencies {
compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901'
- compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.10.0'
+ compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.11.0'
compile project(':dd-trace-ot')
compile project(':dd-java-agent:agent-tooling')

Further attempt at making tests reliable.