Convert couchbase tests to java (#12193)

This commit is contained in:
Lauri Tulmin 2024-09-12 18:29:02 +03:00 committed by GitHub
parent 2a7a5530f3
commit 7ecc678866
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 1405 additions and 1115 deletions

View File

@ -26,8 +26,9 @@ dependencies {
testImplementation(project(":instrumentation:couchbase:couchbase-common:testing"))
latestDepTestLibrary("org.springframework.data:spring-data-couchbase:3.+")
latestDepTestLibrary("com.couchbase.client:java-client:2.+")
// later versions are tested with couchbase-2.6 instrumentation
latestDepTestLibrary("org.springframework.data:spring-data-couchbase:2.+")
latestDepTestLibrary("com.couchbase.client:java-client:2.5.+")
}
tasks.withType<Test>().configureEach {

View File

@ -32,6 +32,11 @@ public class CouchbaseInstrumentationModule extends InstrumentationModule
return asList(new CouchbaseBucketInstrumentation(), new CouchbaseClusterInstrumentation());
}
@Override
public String getModuleGroup() {
return "couchbase";
}
@Override
public List<String> injectedClassNames() {
return singletonList("rx.__OpenTelemetryTracingUtil");

View File

@ -1,7 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
class CouchbaseAsyncClientTest extends AbstractCouchbaseAsyncClientTest {
}

View File

@ -1,7 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
class CouchbaseClientTest extends AbstractCouchbaseClientTest {
}

View File

@ -1,9 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
class CouchbaseSpringRepositoryTest extends AbstractCouchbaseSpringRepositoryTest {
}

View File

@ -1,9 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
class CouchbaseSpringTemplateTest extends AbstractCouchbaseSpringTemplateTest {
}

View File

@ -0,0 +1,19 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseAsyncClientTest;
class CouchbaseAsyncClientTest extends AbstractCouchbaseAsyncClientTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseClientTest;
class CouchbaseClientTest extends AbstractCouchbaseClientTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0;
import com.couchbase.client.core.metrics.DefaultLatencyMetricsCollectorConfig;
import com.couchbase.client.core.metrics.DefaultMetricsCollectorConfig;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import java.util.concurrent.TimeUnit;
public class CouchbaseUtil {
public static DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
// Couchbase seems to be really slow to start sometimes
long timeout = TimeUnit.SECONDS.toMillis(20);
return DefaultCouchbaseEnvironment.builder()
.bootstrapCarrierDirectPort(carrierDirectPort)
.bootstrapHttpDirectPort(httpDirectPort)
// settings to try to reduce variability in the tests:
.runtimeMetricsCollectorConfig(DefaultMetricsCollectorConfig.create(0, TimeUnit.DAYS))
.networkLatencyMetricsCollectorConfig(
DefaultLatencyMetricsCollectorConfig.create(0, TimeUnit.DAYS))
.computationPoolSize(1)
.connectTimeout(timeout)
.disconnectTimeout(timeout)
.kvTimeout(timeout)
.managementTimeout(timeout)
.queryTimeout(timeout)
.viewTimeout(timeout)
.keepAliveTimeout(timeout)
.searchTimeout(timeout)
.analyticsTimeout(timeout)
.socketConnectTimeout((int) timeout);
}
private CouchbaseUtil() {}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0.springdata;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.springdata.AbstractCouchbaseSpringRepositoryTest;
import io.opentelemetry.instrumentation.couchbase.springdata.TestDocument;
import io.opentelemetry.instrumentation.couchbase.springdata.TestRepository;
import io.opentelemetry.javaagent.instrumentation.couchbase.v2_0.CouchbaseUtil;
class CouchbaseSpringRepositoryTest extends AbstractCouchbaseSpringRepositoryTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
@Override
protected TestDocument findById(TestRepository repository, String id) {
return repository.findOne(id);
}
@Override
protected void deleteById(TestRepository repository, String id) {
repository.delete(id);
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_0.springdata;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.springdata.AbstractCouchbaseSpringTemplateTest;
import io.opentelemetry.javaagent.instrumentation.couchbase.v2_0.CouchbaseUtil;
class CouchbaseSpringTemplateTest extends AbstractCouchbaseSpringTemplateTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return CouchbaseUtil.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
}

View File

@ -10,10 +10,13 @@ import static java.util.Arrays.asList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import java.util.List;
@AutoService(InstrumentationModule.class)
public class CouchbaseInstrumentationModule extends InstrumentationModule {
public class CouchbaseInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public CouchbaseInstrumentationModule() {
super("couchbase", "couchbase-2.6");
}
@ -22,4 +25,9 @@ public class CouchbaseInstrumentationModule extends InstrumentationModule {
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new CouchbaseCoreInstrumentation(), new CouchbaseNetworkInstrumentation());
}
@Override
public String getModuleGroup() {
return "couchbase";
}
}

View File

@ -1,21 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
class CouchbaseAsyncClient26Test extends AbstractCouchbaseAsyncClientTest {
@Override
void assertCouchbaseCall(TraceAssert trace,
int index,
Object name,
SpanData parentSpan = null,
String bucketName = null,
Object statement = null,
Object operation = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, parentSpan, bucketName, statement, operation)
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
class CouchbaseClient26Test extends AbstractCouchbaseClientTest {
@Override
void assertCouchbaseCall(TraceAssert trace,
int index,
Object name,
SpanData parentSpan = null,
String bucketName = null,
Object statement = null,
Object operation = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, parentSpan, bucketName, statement, operation)
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes
import io.opentelemetry.semconv.NetworkAttributes
import static io.opentelemetry.api.trace.SpanKind.CLIENT
class CouchbaseSpanUtil {
// Reusable span assertion method. Cannot directly override AbstractCouchbaseTest.assertCouchbaseSpan because
// Of the class hierarchy of these tests
static void assertCouchbaseCall(TraceAssert trace,
int index,
Object spanName,
SpanData parentSpan = null,
String bucketName = null,
Object statement = null,
Object operation = null) {
trace.span(index) {
name spanName
kind CLIENT
if (parentSpan == null) {
hasNoParent()
} else {
childOf((SpanData) parentSpan)
}
attributes {
"$DbIncubatingAttributes.DB_SYSTEM" "couchbase"
"$DbIncubatingAttributes.DB_NAME" bucketName
"$DbIncubatingAttributes.DB_STATEMENT" statement
"$DbIncubatingAttributes.DB_OPERATION"(operation ?: spanName)
// Because of caching, not all requests hit the server so these attributes may be absent
"$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == null }
"$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == null }
"$NetworkAttributes.NETWORK_PEER_PORT" { it instanceof Number || it == null }
// Because of caching, not all requests hit the server so this tag may be absent
"couchbase.local.address" { it == null || it instanceof String }
// Not all couchbase operations have operation id. Notably, 'ViewQuery's do not
// We assign a spanName of 'Bucket.query' and this is shared with n1ql queries
// that do have operation ids
"couchbase.operation_id" { it == null || it instanceof String }
}
}
}
}

View File

@ -1,23 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
class CouchbaseSpringRepository26Test extends AbstractCouchbaseSpringRepositoryTest {
@Override
void assertCouchbaseCall(TraceAssert trace,
int index,
Object name,
SpanData parentSpan = null,
String bucketName = null,
Object statement = null,
Object operation = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, parentSpan, bucketName, statement, operation)
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
class CouchbaseSpringTemplate26Test extends AbstractCouchbaseSpringTemplateTest {
@Override
void assertCouchbaseCall(TraceAssert trace,
int index,
Object name,
SpanData parentSpan = null,
String bucketName = null,
Object statement = null,
Object operation = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, parentSpan, bucketName, statement, operation)
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static org.assertj.core.api.Assertions.assertThat;
import com.couchbase.client.core.metrics.DefaultLatencyMetricsCollectorConfig;
import com.couchbase.client.core.metrics.DefaultMetricsCollectorConfig;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.semconv.NetworkAttributes;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class Couchbase26Util {
public static DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
// Couchbase seems to be really slow to start sometimes
long timeout = TimeUnit.SECONDS.toMillis(20);
return DefaultCouchbaseEnvironment.builder()
.bootstrapCarrierDirectPort(carrierDirectPort)
.bootstrapHttpDirectPort(httpDirectPort)
// settings to try to reduce variability in the tests:
.runtimeMetricsCollectorConfig(DefaultMetricsCollectorConfig.create(0, TimeUnit.DAYS))
.networkLatencyMetricsCollectorConfig(
DefaultLatencyMetricsCollectorConfig.create(0, TimeUnit.DAYS))
.computationPoolSize(1)
.connectTimeout(timeout)
.disconnectTimeout(timeout)
.kvTimeout(timeout)
.managementTimeout(timeout)
.queryTimeout(timeout)
.viewTimeout(timeout)
.keepAliveTimeout(timeout)
.searchTimeout(timeout)
.analyticsTimeout(timeout)
.socketConnectTimeout((int) timeout);
}
public static List<AttributeAssertion> couchbaseAttributes() {
return Arrays.asList(
equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4"),
equalTo(NetworkAttributes.NETWORK_PEER_ADDRESS, "127.0.0.1"),
satisfies(NetworkAttributes.NETWORK_PEER_PORT, val -> assertThat(val).isNotNull()),
satisfies(
AttributeKey.stringKey("couchbase.local.address"), val -> assertThat(val).isNotNull()),
satisfies(
AttributeKey.stringKey("couchbase.operation_id"), val -> assertThat(val).isNotNull()));
}
private Couchbase26Util() {}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseAsyncClientTest;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import java.util.List;
class CouchbaseAsyncClient26Test extends AbstractCouchbaseAsyncClientTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return Couchbase26Util.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
@Override
protected List<AttributeAssertion> couchbaseAttributes() {
return Couchbase26Util.couchbaseAttributes();
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseClientTest;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import java.util.List;
class CouchbaseClient26Test extends AbstractCouchbaseClientTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return Couchbase26Util.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
@Override
protected List<AttributeAssertion> couchbaseAttributes() {
return Couchbase26Util.couchbaseAttributes();
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6.springdata;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.springdata.AbstractCouchbaseSpringRepositoryTest;
import io.opentelemetry.instrumentation.couchbase.springdata.TestDocument;
import io.opentelemetry.instrumentation.couchbase.springdata.TestRepository;
import io.opentelemetry.javaagent.instrumentation.couchbase.v2_6.Couchbase26Util;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import java.util.List;
class CouchbaseSpringRepository26Test extends AbstractCouchbaseSpringRepositoryTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return Couchbase26Util.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
@Override
protected List<AttributeAssertion> couchbaseAttributes() {
return Couchbase26Util.couchbaseAttributes();
}
@Override
protected TestDocument findById(TestRepository repository, String id) {
return repository.findById(id).get();
}
@Override
protected void deleteById(TestRepository repository, String id) {
repository.deleteById(id);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v2_6.springdata;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import io.opentelemetry.instrumentation.couchbase.springdata.AbstractCouchbaseSpringTemplateTest;
import io.opentelemetry.javaagent.instrumentation.couchbase.v2_6.Couchbase26Util;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import java.util.List;
class CouchbaseSpringTemplate26Test extends AbstractCouchbaseSpringTemplateTest {
@Override
protected DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort) {
return Couchbase26Util.envBuilder(bucketSettings, carrierDirectPort, httpDirectPort);
}
@Override
protected List<AttributeAssertion> couchbaseAttributes() {
return Couchbase26Util.couchbaseAttributes();
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import com.couchbase.client.core.error.DocumentNotFoundException
import com.couchbase.client.java.Cluster
import com.couchbase.client.java.Collection
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.testcontainers.containers.output.Slf4jLogConsumer
import org.testcontainers.couchbase.BucketDefinition
import org.testcontainers.couchbase.CouchbaseContainer
import org.testcontainers.couchbase.CouchbaseService
import spock.lang.Shared
import java.time.Duration
// Couchbase instrumentation is owned upstream so we don't assert on the contents of the spans, only
// that the instrumentation is properly registered by the agent, meaning some spans were generated.
class CouchbaseClient316Test extends AgentInstrumentationSpecification {
private static final Logger logger = LoggerFactory.getLogger("couchbase-container")
@Shared
CouchbaseContainer couchbase
@Shared
Cluster cluster
@Shared
Collection collection
def setupSpec() {
couchbase = new CouchbaseContainer()
.withExposedPorts(8091)
.withEnabledServices(CouchbaseService.KV)
.withBucket(new BucketDefinition("test"))
.withLogConsumer(new Slf4jLogConsumer(logger))
.withStartupTimeout(Duration.ofSeconds(120))
couchbase.start()
cluster = Cluster.connect(couchbase.connectionString, couchbase.username, couchbase.password)
def bucket = cluster.bucket("test")
collection = bucket.defaultCollection()
bucket.waitUntilReady(Duration.ofSeconds(30))
}
def cleanupSpec() {
couchbase.stop()
}
def "emits spans"() {
when:
try {
collection.get("id")
} catch (DocumentNotFoundException e) {
// Expected
}
then:
assertTracesWithoutScopeVersionVerification(1) {
trace(0, 2) {
span(0) {
name(~/.*get/)
}
span(1) {
name(~/.*dispatch_to_server/)
}
}
}
cleanup:
cluster.disconnect()
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.couchbase.v3_1_6;
import com.couchbase.client.core.env.TimeoutConfig;
import com.couchbase.client.core.error.DocumentNotFoundException;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.ClusterOptions;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.env.ClusterEnvironment;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import java.time.Duration;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.couchbase.BucketDefinition;
import org.testcontainers.couchbase.CouchbaseContainer;
import org.testcontainers.couchbase.CouchbaseService;
// Couchbase instrumentation is owned upstream, so we don't assert on the contents of the spans,
// only that the instrumentation is properly registered by the agent, meaning some spans were
// generated.
class CouchbaseClient316Test {
@RegisterExtension
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
private static final Logger logger = LoggerFactory.getLogger("couchbase-container");
private static CouchbaseContainer couchbase;
private static Cluster cluster;
private static Collection collection;
@BeforeAll
static void setup() {
couchbase =
new CouchbaseContainer("couchbase/server:7.6.0")
.withExposedPorts(8091)
.withEnabledServices(CouchbaseService.KV)
.withBucket(new BucketDefinition("test"))
.withLogConsumer(new Slf4jLogConsumer(logger))
.withStartupTimeout(Duration.ofMinutes(2));
couchbase.start();
ClusterEnvironment environment =
ClusterEnvironment.builder()
.timeoutConfig(TimeoutConfig.kvTimeout(Duration.ofSeconds(30)))
.build();
cluster =
Cluster.connect(
couchbase.getConnectionString(),
ClusterOptions.clusterOptions(couchbase.getUsername(), couchbase.getPassword())
.environment(environment));
Bucket bucket = cluster.bucket("test");
collection = bucket.defaultCollection();
// Wait 1 minute due to slow startup contributing to flakiness
bucket.waitUntilReady(Duration.ofMinutes(1));
}
@AfterAll
static void cleanup() {
cluster.disconnect();
couchbase.stop();
}
@Test
void testEmitsSpans() {
try {
collection.get("id");
} catch (DocumentNotFoundException e) {
// Expected
}
testing.waitAndAssertTracesWithoutScopeVersionVerification(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("get"), span -> span.hasName("dispatch_to_server")));
}
}

View File

@ -1,187 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import com.couchbase.client.java.AsyncCluster
import com.couchbase.client.java.CouchbaseAsyncCluster
import com.couchbase.client.java.document.JsonDocument
import com.couchbase.client.java.document.json.JsonObject
import com.couchbase.client.java.env.CouchbaseEnvironment
import com.couchbase.client.java.query.N1qlQuery
import io.opentelemetry.api.trace.SpanKind
import spock.lang.Unroll
import spock.util.concurrent.BlockingVariable
import util.AbstractCouchbaseTest
import java.util.concurrent.TimeUnit
@Unroll
abstract class AbstractCouchbaseAsyncClientTest extends AbstractCouchbaseTest {
static final int TIMEOUT = 10
def "test hasBucket #type"() {
setup:
def hasBucket = new BlockingVariable<Boolean>(TIMEOUT)
when:
cluster.openBucket(bucketSettings.name(), bucketSettings.password()).subscribe({ bkt ->
manager.hasBucket(bucketSettings.name()).subscribe({ result -> hasBucket.set(result) })
})
then:
assert hasBucket.get()
assertTraces(1) {
trace(0, 2) {
assertCouchbaseCall(it, 0, "Cluster.openBucket")
assertCouchbaseCall(it, 1, "ClusterManager.hasBucket", span(0))
}
}
cleanup:
cluster?.disconnect()?.timeout(TIMEOUT, TimeUnit.SECONDS)?.toBlocking()?.single()
environment.shutdown()
where:
bucketSettings << [bucketCouchbase, bucketMemcache]
environment = envBuilder(bucketSettings).build()
cluster = CouchbaseAsyncCluster.create(environment, Arrays.asList("127.0.0.1"))
manager = cluster.clusterManager(AbstractCouchbaseTest.USERNAME, AbstractCouchbaseTest.PASSWORD).toBlocking().single()
type = bucketSettings.type().name()
}
def "test upsert #type"() {
setup:
JsonObject content = JsonObject.create().put("hello", "world")
def inserted = new BlockingVariable<JsonDocument>(TIMEOUT)
when:
runWithSpan("someTrace") {
// Connect to the bucket and open it
cluster.openBucket(bucketSettings.name(), bucketSettings.password()).subscribe({ bkt ->
bkt.upsert(JsonDocument.create("helloworld", content)).subscribe({ result -> inserted.set(result) })
})
}
then:
inserted.get().content().getString("hello") == "world"
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Cluster.openBucket", span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", span(1), bucketSettings.name())
}
}
cleanup:
cluster?.disconnect()?.timeout(TIMEOUT, TimeUnit.SECONDS)?.toBlocking()?.single()
environment.shutdown()
where:
bucketSettings << [bucketCouchbase, bucketMemcache]
environment = envBuilder(bucketSettings).build()
cluster = CouchbaseAsyncCluster.create(environment, Arrays.asList("127.0.0.1"))
type = bucketSettings.type().name()
}
def "test upsert and get #type"() {
setup:
JsonObject content = JsonObject.create().put("hello", "world")
def inserted = new BlockingVariable<JsonDocument>(TIMEOUT)
def found = new BlockingVariable<JsonDocument>(TIMEOUT)
when:
runWithSpan("someTrace") {
cluster.openBucket(bucketSettings.name(), bucketSettings.password()).subscribe({ bkt ->
bkt.upsert(JsonDocument.create("helloworld", content))
.subscribe({ result ->
inserted.set(result)
bkt.get("helloworld")
.subscribe({ searchResult -> found.set(searchResult)
})
})
})
}
// Create a JSON document and store it with the ID "helloworld"
then:
found.get() == inserted.get()
found.get().content().getString("hello") == "world"
assertTraces(1) {
trace(0, 4) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Cluster.openBucket", span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", span(1), bucketSettings.name())
assertCouchbaseCall(it, 3, "Bucket.get", span(2), bucketSettings.name())
}
}
cleanup:
cluster?.disconnect()?.timeout(TIMEOUT, TimeUnit.SECONDS)?.toBlocking()?.single()
environment.shutdown()
where:
bucketSettings << [bucketCouchbase, bucketMemcache]
environment = envBuilder(bucketSettings).build()
cluster = CouchbaseAsyncCluster.create(environment, Arrays.asList("127.0.0.1"))
type = bucketSettings.type().name()
}
def "test query"() {
setup:
// Only couchbase buckets support queries.
CouchbaseEnvironment environment = envBuilder(bucketCouchbase).build()
AsyncCluster cluster = CouchbaseAsyncCluster.create(environment, Arrays.asList("127.0.0.1"))
def queryResult = new BlockingVariable<JsonObject>(TIMEOUT)
when:
// Mock expects this specific query.
// See com.couchbase.mock.http.query.QueryServer.handleString.
runWithSpan("someTrace") {
cluster.openBucket(bucketCouchbase.name(), bucketCouchbase.password()).subscribe({
bkt ->
bkt.query(N1qlQuery.simple("SELECT mockrow"))
.flatMap({ query -> query.rows() })
.single()
.subscribe({ row -> queryResult.set(row.value()) })
})
}
then:
queryResult.get().get("row") == "value"
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Cluster.openBucket", span(0))
def dbName = bucketCouchbase.name()
assertCouchbaseCall(it, 2, "SELECT $dbName", span(1), dbName, 'SELECT mockrow', 'SELECT')
}
}
cleanup:
cluster?.disconnect()?.timeout(TIMEOUT, TimeUnit.SECONDS)?.toBlocking()?.single()
environment.shutdown()
}
}

View File

@ -1,123 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import com.couchbase.client.java.Bucket
import com.couchbase.client.java.Cluster
import com.couchbase.client.java.CouchbaseCluster
import com.couchbase.client.java.document.JsonDocument
import com.couchbase.client.java.document.json.JsonObject
import com.couchbase.client.java.env.CouchbaseEnvironment
import com.couchbase.client.java.query.N1qlQuery
import io.opentelemetry.api.trace.SpanKind
import spock.lang.Unroll
import util.AbstractCouchbaseTest
@Unroll
abstract class AbstractCouchbaseClientTest extends AbstractCouchbaseTest {
def "test hasBucket #type"() {
when:
def hasBucket = manager.hasBucket(bucketSettings.name())
then:
assert hasBucket
assertTraces(1) {
trace(0, 1) {
assertCouchbaseCall(it, 0, "ClusterManager.hasBucket")
}
}
cleanup:
cluster?.disconnect()
environment.shutdown()
where:
bucketSettings << [bucketCouchbase, bucketMemcache]
environment = envBuilder(bucketSettings).build()
cluster = CouchbaseCluster.create(environment, Arrays.asList("127.0.0.1"))
manager = cluster.clusterManager(AbstractCouchbaseTest.USERNAME, AbstractCouchbaseTest.PASSWORD)
type = bucketSettings.type().name()
}
def "test upsert and get #type"() {
when:
// Connect to the bucket and open it
Bucket bkt = cluster.openBucket(bucketSettings.name(), bucketSettings.password())
// Create a JSON document and store it with the ID "helloworld"
JsonObject content = JsonObject.create().put("hello", "world")
def inserted
def found
runWithSpan("someTrace") {
inserted = bkt.upsert(JsonDocument.create("helloworld", content))
found = bkt.get("helloworld")
}
then:
found == inserted
found.content().getString("hello") == "world"
assertTraces(2) {
trace(0, 1) {
assertCouchbaseCall(it, 0, "Cluster.openBucket")
}
trace(1, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Bucket.upsert", span(0), bucketSettings.name())
assertCouchbaseCall(it, 2, "Bucket.get", span(0), bucketSettings.name())
}
}
cleanup:
cluster?.disconnect()
environment.shutdown()
where:
bucketSettings << [bucketCouchbase, bucketMemcache]
environment = envBuilder(bucketSettings).build()
cluster = CouchbaseCluster.create(environment, Arrays.asList("127.0.0.1"))
type = bucketSettings.type().name()
}
def "test query"() {
setup:
// Only couchbase buckets support queries.
CouchbaseEnvironment environment = envBuilder(bucketCouchbase).build()
Cluster cluster = CouchbaseCluster.create(environment, Arrays.asList("127.0.0.1"))
Bucket bkt = cluster.openBucket(bucketCouchbase.name(), bucketCouchbase.password())
when:
// Mock expects this specific query.
// See com.couchbase.mock.http.query.QueryServer.handleString.
def result = bkt.query(N1qlQuery.simple("SELECT mockrow"))
then:
result.parseSuccess()
result.finalSuccess()
result.first().value().get("row") == "value"
and:
assertTraces(2) {
trace(0, 1) {
assertCouchbaseCall(it, 0, "Cluster.openBucket")
}
trace(1, 1) {
def dbName = bucketCouchbase.name()
assertCouchbaseCall(it, 0, "SELECT $dbName", null, dbName, 'SELECT mockrow', 'SELECT')
}
}
cleanup:
cluster?.disconnect()
environment.shutdown()
}
}

View File

@ -1,202 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import com.couchbase.client.java.Cluster
import com.couchbase.client.java.CouchbaseCluster
import com.couchbase.client.java.env.CouchbaseEnvironment
import com.couchbase.client.java.view.DefaultView
import com.couchbase.client.java.view.DesignDocument
import io.opentelemetry.api.trace.SpanKind
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.data.repository.CrudRepository
import spock.lang.Shared
import spock.lang.Unroll
import util.AbstractCouchbaseTest
@Unroll
abstract class AbstractCouchbaseSpringRepositoryTest extends AbstractCouchbaseTest {
static final Closure<Doc> FIND
static {
// This method is different in Spring Data 2+
try {
CrudRepository.getMethod("findOne", Serializable)
FIND = { DocRepository repo, String id ->
repo.findOne(id)
}
} catch (NoSuchMethodException e) {
FIND = { DocRepository repo, String id ->
repo.findById(id).get()
}
}
}
@Shared
ConfigurableApplicationContext applicationContext
@Shared
DocRepository repo
def setupSpec() {
CouchbaseEnvironment environment = envBuilder(bucketCouchbase).build()
Cluster couchbaseCluster = CouchbaseCluster.create(environment, Arrays.asList("127.0.0.1"))
// Create view for SpringRepository's findAll()
couchbaseCluster.openBucket(bucketCouchbase.name(), bucketCouchbase.password()).bucketManager()
.insertDesignDocument(
DesignDocument.create("doc", Collections.singletonList(DefaultView.create("all",
'''
function (doc, meta) {
if (doc._class == "springdata.Doc") {
emit(meta.id, null);
}
}
'''.stripIndent()
)))
)
CouchbaseConfig.setEnvironment(environment)
CouchbaseConfig.setBucketSettings(bucketCouchbase)
// Close all buckets and disconnect
couchbaseCluster.disconnect()
applicationContext = new AnnotationConfigApplicationContext(CouchbaseConfig)
repo = applicationContext.getBean(DocRepository)
}
def cleanupSpec() {
applicationContext.close()
}
def "test empty repo"() {
when:
def result = repo.findAll()
then:
!result.iterator().hasNext()
and:
assertTraces(1) {
trace(0, 1) {
def dbName = bucketCouchbase.name()
assertCouchbaseCall(it, 0, dbName, null, dbName, ~/^ViewQuery\(doc\/all\).*/, { it == null })
}
}
}
def "test save"() {
setup:
def doc = new Doc()
when:
def result = repo.save(doc)
then:
result == doc
assertTraces(1) {
trace(0, 1) {
assertCouchbaseCall(it, 0, "Bucket.upsert", null, bucketCouchbase.name())
}
}
cleanup:
clearExportedData()
repo.deleteAll()
ignoreTracesAndClear(2)
}
def "test save and retrieve"() {
setup:
def doc = new Doc()
def result
when:
runWithSpan("someTrace") {
repo.save(doc)
result = FIND(repo, "1")
}
then: // RETRIEVE
result == doc
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Bucket.upsert", span(0), bucketCouchbase.name())
assertCouchbaseCall(it, 2, "Bucket.get", span(0), bucketCouchbase.name())
}
}
cleanup:
clearExportedData()
repo.deleteAll()
ignoreTracesAndClear(2)
}
def "test save and update"() {
setup:
def doc = new Doc()
when:
runWithSpan("someTrace") {
repo.save(doc)
doc.data = "other data"
repo.save(doc)
}
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Bucket.upsert", span(0), bucketCouchbase.name())
assertCouchbaseCall(it, 2, "Bucket.upsert", span(0), bucketCouchbase.name())
}
}
cleanup:
clearExportedData()
repo.deleteAll()
ignoreTracesAndClear(2)
}
def "save and delete"() {
setup:
def doc = new Doc()
def result
when: // DELETE
runWithSpan("someTrace") {
repo.save(doc)
repo.delete("1")
result = repo.findAll().iterator().hasNext()
}
then:
assert !result
assertTraces(1) {
trace(0, 4) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
def dbName = bucketCouchbase.name()
assertCouchbaseCall(it, 1, "Bucket.upsert", span(0), dbName)
assertCouchbaseCall(it, 2, "Bucket.remove", span(0), dbName)
assertCouchbaseCall(it, 3, dbName, span(0), dbName, ~/^ViewQuery\(doc\/all\).*/, { it == null })
}
}
}
}

View File

@ -1,136 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import com.couchbase.client.java.Bucket
import com.couchbase.client.java.Cluster
import com.couchbase.client.java.CouchbaseCluster
import com.couchbase.client.java.cluster.ClusterManager
import com.couchbase.client.java.env.CouchbaseEnvironment
import io.opentelemetry.api.trace.SpanKind
import org.springframework.data.couchbase.core.CouchbaseTemplate
import spock.lang.Retry
import spock.lang.Shared
import spock.lang.Unroll
import util.AbstractCouchbaseTest
@Retry(count = 10, delay = 500)
@Unroll
class AbstractCouchbaseSpringTemplateTest extends AbstractCouchbaseTest {
@Shared
List<CouchbaseTemplate> templates
@Shared
Cluster couchbaseCluster
@Shared
Cluster memcacheCluster
@Shared
protected CouchbaseEnvironment couchbaseEnvironment
@Shared
protected CouchbaseEnvironment memcacheEnvironment
def setupSpec() {
couchbaseEnvironment = envBuilder(bucketCouchbase).build()
memcacheEnvironment = envBuilder(bucketMemcache).build()
couchbaseCluster = CouchbaseCluster.create(couchbaseEnvironment, Arrays.asList("127.0.0.1"))
memcacheCluster = CouchbaseCluster.create(memcacheEnvironment, Arrays.asList("127.0.0.1"))
ClusterManager couchbaseManager = couchbaseCluster.clusterManager(AbstractCouchbaseTest.USERNAME, AbstractCouchbaseTest.PASSWORD)
ClusterManager memcacheManager = memcacheCluster.clusterManager(AbstractCouchbaseTest.USERNAME, AbstractCouchbaseTest.PASSWORD)
Bucket bucketCouchbase = couchbaseCluster.openBucket(bucketCouchbase.name(), bucketCouchbase.password())
Bucket bucketMemcache = memcacheCluster.openBucket(bucketMemcache.name(), bucketMemcache.password())
runWithSpan("getting info") {
templates = [new CouchbaseTemplate(couchbaseManager.info(), bucketCouchbase),
new CouchbaseTemplate(memcacheManager.info(), bucketMemcache)]
}
}
def cleanupSpec() {
couchbaseCluster?.disconnect()
memcacheCluster?.disconnect()
couchbaseEnvironment.shutdown()
memcacheEnvironment.shutdown()
}
def "test write #testName"() {
setup:
def doc = new Doc()
def result
when:
runWithSpan("someTrace") {
template.save(doc)
result = template.findById("1", Doc)
}
then:
result != null
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Bucket.upsert", span(0), testName)
assertCouchbaseCall(it, 2, "Bucket.get", span(0), testName)
}
}
where:
template << templates
testName = template.couchbaseBucket.name()
}
def "test remove #testName"() {
setup:
def doc = new Doc()
when:
runWithSpan("someTrace") {
template.save(doc)
template.remove(doc)
}
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
kind SpanKind.INTERNAL
hasNoParent()
}
assertCouchbaseCall(it, 1, "Bucket.upsert", span(0), testName)
assertCouchbaseCall(it, 2, "Bucket.remove", span(0), testName)
}
}
clearExportedData()
when:
def result = template.findById("1", Doc)
then:
result == null
assertTraces(1) {
trace(0, 1) {
assertCouchbaseCall(it, 0, "Bucket.get", null, testName)
}
}
where:
template << templates
testName = template.couchbaseBucket.name()
}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import com.couchbase.client.java.cluster.BucketSettings
import com.couchbase.client.java.env.CouchbaseEnvironment
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories
import static java.util.Objects.requireNonNull
@Configuration
@EnableCouchbaseRepositories(basePackages = "springdata")
@ComponentScan(basePackages = "springdata")
class CouchbaseConfig extends AbstractCouchbaseConfiguration {
// These need to be set before this class can be used by Spring
static CouchbaseEnvironment environment
static BucketSettings bucketSettings
@Override
protected CouchbaseEnvironment getEnvironment() {
return requireNonNull(environment)
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("127.0.0.1")
}
@Override
protected String getBucketName() {
return bucketSettings.name()
}
@Override
protected String getBucketPassword() {
return bucketSettings.password()
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import groovy.transform.EqualsAndHashCode
import org.springframework.data.annotation.Id
import org.springframework.data.couchbase.core.mapping.Document
@Document
@EqualsAndHashCode
class Doc {
@Id
private String id = "1"
private String data = "some data"
String getId() {
return id
}
void setId(String id) {
this.id = id
}
String getData() {
return data
}
void setData(String data) {
this.data = data
}
}

View File

@ -1,10 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package springdata
import org.springframework.data.couchbase.repository.CouchbaseRepository
interface DocRepository extends CouchbaseRepository<Doc, String> {}

View File

@ -1,130 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package util
import com.couchbase.client.core.metrics.DefaultLatencyMetricsCollectorConfig
import com.couchbase.client.core.metrics.DefaultMetricsCollectorConfig
import com.couchbase.client.java.bucket.BucketType
import com.couchbase.client.java.cluster.BucketSettings
import com.couchbase.client.java.cluster.DefaultBucketSettings
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment
import com.couchbase.mock.Bucket
import com.couchbase.mock.BucketConfiguration
import com.couchbase.mock.CouchbaseMock
import com.couchbase.mock.http.query.QueryServer
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.utils.PortUtils
import io.opentelemetry.sdk.trace.data.SpanData
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes
import spock.lang.Shared
import java.util.concurrent.TimeUnit
import static io.opentelemetry.api.trace.SpanKind.CLIENT
abstract class AbstractCouchbaseTest extends AgentInstrumentationSpecification {
static final USERNAME = "Administrator"
static final PASSWORD = "password"
@Shared
private int port = PortUtils.findOpenPort()
@Shared
private String testBucketName = this.getClass().simpleName
@Shared
protected bucketCouchbase = DefaultBucketSettings.builder()
.enableFlush(true)
.name("$testBucketName-cb")
.password("test-pass")
.type(BucketType.COUCHBASE)
.quota(100)
.build()
@Shared
protected bucketMemcache = DefaultBucketSettings.builder()
.enableFlush(true)
.name("$testBucketName-mem")
.password("test-pass")
.type(BucketType.MEMCACHED)
.quota(100)
.build()
@Shared
CouchbaseMock mock
def setupSpec() {
mock = new CouchbaseMock("127.0.0.1", port, 1, 1)
mock.httpServer.register("/query", new QueryServer())
mock.start()
println "CouchbaseMock listening on localhost:$port"
mock.createBucket(convert(bucketCouchbase))
mock.createBucket(convert(bucketMemcache))
}
private static BucketConfiguration convert(BucketSettings bucketSettings) {
def configuration = new BucketConfiguration()
configuration.name = bucketSettings.name()
configuration.password = bucketSettings.password()
configuration.type = Bucket.BucketType.valueOf(bucketSettings.type().name())
configuration.numNodes = 1
configuration.numReplicas = 0
return configuration
}
def cleanupSpec() {
mock?.stop()
}
protected DefaultCouchbaseEnvironment.Builder envBuilder(BucketSettings bucketSettings) {
// Couchbase seems to be really slow to start sometimes
def timeout = TimeUnit.SECONDS.toMillis(20)
return DefaultCouchbaseEnvironment.builder()
.bootstrapCarrierDirectPort(mock.getCarrierPort(bucketSettings.name()))
.bootstrapHttpDirectPort(port)
// settings to try to reduce variability in the tests:
.runtimeMetricsCollectorConfig(DefaultMetricsCollectorConfig.create(0, TimeUnit.DAYS))
.networkLatencyMetricsCollectorConfig(DefaultLatencyMetricsCollectorConfig.create(0, TimeUnit.DAYS))
.computationPoolSize(1)
.connectTimeout(timeout)
.disconnectTimeout(timeout)
.kvTimeout(timeout)
.managementTimeout(timeout)
.queryTimeout(timeout)
.viewTimeout(timeout)
.keepAliveTimeout(timeout)
.searchTimeout(timeout)
.analyticsTimeout(timeout)
.socketConnectTimeout(timeout.intValue())
}
void assertCouchbaseCall(TraceAssert trace,
int index,
Object spanName,
SpanData parentSpan = null,
String bucketName = null,
Object statement = null,
Object operation = null) {
trace.span(index) {
name spanName
kind CLIENT
if (parentSpan == null) {
hasNoParent()
} else {
childOf((SpanData) parentSpan)
}
attributes {
"$DbIncubatingAttributes.DB_SYSTEM" "couchbase"
"$DbIncubatingAttributes.DB_NAME" bucketName
"$DbIncubatingAttributes.DB_STATEMENT" statement
"$DbIncubatingAttributes.DB_OPERATION"(operation ?: spanName)
}
}
}
}

View File

@ -0,0 +1,236 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import com.couchbase.client.java.CouchbaseAsyncCluster;
import com.couchbase.client.java.cluster.AsyncClusterManager;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.query.AsyncN1qlQueryResult;
import com.couchbase.client.java.query.N1qlQuery;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
public abstract class AbstractCouchbaseAsyncClientTest extends AbstractCouchbaseTest {
private static final int TIMEOUT_SECONDS = 10;
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create();
private static Stream<Arguments> bucketSettings() {
return Stream.of(
Arguments.of(named(bucketCouchbase.type().name(), bucketCouchbase)),
Arguments.of(named(bucketMemcache.type().name(), bucketMemcache)));
}
private CouchbaseAsyncCluster prepareCluster(BucketSettings bucketSettings) {
CouchbaseEnvironment environment = envBuilder(bucketSettings).build();
CouchbaseAsyncCluster cluster =
CouchbaseAsyncCluster.create(environment, Collections.singletonList("127.0.0.1"));
cleanup.deferCleanup(
() -> cluster.disconnect().timeout(10, TimeUnit.SECONDS).toBlocking().single());
cleanup.deferCleanup(environment::shutdown);
return cluster;
}
@ParameterizedTest
@MethodSource("bucketSettings")
void hasBucket(BucketSettings bucketSettings)
throws ExecutionException, InterruptedException, TimeoutException {
CouchbaseAsyncCluster cluster = prepareCluster(bucketSettings);
AsyncClusterManager manager = cluster.clusterManager(USERNAME, PASSWORD).toBlocking().single();
testing.waitForTraces(1);
testing.clearData();
CompletableFuture<Boolean> hasBucket = new CompletableFuture<>();
cluster
.openBucket(bucketSettings.name(), bucketSettings.password())
.subscribe(
bucket -> manager.hasBucket(bucketSettings.name()).subscribe(hasBucket::complete));
assertThat(hasBucket.get(TIMEOUT_SECONDS, TimeUnit.SECONDS)).isTrue();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("Cluster.openBucket")
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
DbIncubatingAttributes.DB_SYSTEM,
DbIncubatingAttributes.DbSystemValues.COUCHBASE),
equalTo(DbIncubatingAttributes.DB_OPERATION, "Cluster.openBucket")),
span ->
assertCouchbaseSpan(span, "ClusterManager.hasBucket")
.hasParent(trace.getSpan(0))));
}
@ParameterizedTest
@MethodSource("bucketSettings")
void upsert(BucketSettings bucketSettings)
throws ExecutionException, InterruptedException, TimeoutException {
CouchbaseAsyncCluster cluster = prepareCluster(bucketSettings);
JsonObject content = JsonObject.create().put("hello", "world");
CompletableFuture<JsonDocument> inserted = new CompletableFuture<>();
testing.runWithSpan(
"someTrace",
() -> {
cluster
.openBucket(bucketSettings.name(), bucketSettings.password())
.subscribe(
bucket ->
bucket
.upsert(JsonDocument.create("helloworld", content))
.subscribe(inserted::complete));
});
assertThat(inserted.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).content().getString("hello"))
.isEqualTo("world");
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
span.hasName("Cluster.openBucket")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
DbIncubatingAttributes.DB_SYSTEM,
DbIncubatingAttributes.DbSystemValues.COUCHBASE),
equalTo(DbIncubatingAttributes.DB_OPERATION, "Cluster.openBucket")),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketSettings.name())
.hasParent(trace.getSpan(1))));
}
@ParameterizedTest
@MethodSource("bucketSettings")
void upsertAndGet(BucketSettings bucketSettings)
throws ExecutionException, InterruptedException, TimeoutException {
CouchbaseAsyncCluster cluster = prepareCluster(bucketSettings);
JsonObject content = JsonObject.create().put("hello", "world");
CompletableFuture<JsonDocument> inserted = new CompletableFuture<>();
CompletableFuture<JsonDocument> found = new CompletableFuture<>();
testing.runWithSpan(
"someTrace",
() -> {
cluster
.openBucket(bucketSettings.name(), bucketSettings.password())
.subscribe(
bucket ->
bucket
.upsert(JsonDocument.create("helloworld", content))
.subscribe(
result -> {
inserted.complete(result);
bucket.get("helloworld").subscribe(found::complete);
}));
});
JsonDocument insertedResult = inserted.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
JsonDocument foundResult = found.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertThat(foundResult).isEqualTo(insertedResult);
assertThat(foundResult.content().getString("hello")).isEqualTo("world");
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
span.hasName("Cluster.openBucket")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
DbIncubatingAttributes.DB_SYSTEM,
DbIncubatingAttributes.DbSystemValues.COUCHBASE),
equalTo(DbIncubatingAttributes.DB_OPERATION, "Cluster.openBucket")),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketSettings.name())
.hasParent(trace.getSpan(1)),
span ->
assertCouchbaseSpan(span, "Bucket.get", bucketSettings.name())
.hasParent(trace.getSpan(2))));
}
@Test
void query() throws ExecutionException, InterruptedException, TimeoutException {
// Only couchbase buckets support queries.
CouchbaseAsyncCluster cluster = prepareCluster(bucketCouchbase);
CompletableFuture<JsonObject> queryResult = new CompletableFuture<>();
// Mock expects this specific query.
// See com.couchbase.mock.http.query.QueryServer.handleString.
testing.runWithSpan(
"someTrace",
() -> {
cluster
.openBucket(bucketCouchbase.name(), bucketCouchbase.password())
.subscribe(
bucket ->
bucket
.query(N1qlQuery.simple("SELECT mockrow"))
.flatMap(AsyncN1qlQueryResult::rows)
.single()
.subscribe(row -> queryResult.complete(row.value())));
});
assertThat(queryResult.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).get("row")).isEqualTo("value");
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
span.hasName("Cluster.openBucket")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(
DbIncubatingAttributes.DB_SYSTEM,
DbIncubatingAttributes.DbSystemValues.COUCHBASE),
equalTo(DbIncubatingAttributes.DB_OPERATION, "Cluster.openBucket")),
span ->
assertCouchbaseSpan(
span,
"SELECT " + bucketCouchbase.name(),
"SELECT",
bucketCouchbase.name(),
"SELECT mockrow")
.hasParent(trace.getSpan(1))));
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.cluster.ClusterManager;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.query.N1qlQuery;
import com.couchbase.client.java.query.N1qlQueryResult;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
public abstract class AbstractCouchbaseClientTest extends AbstractCouchbaseTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create();
private static Stream<Arguments> bucketSettings() {
return Stream.of(
Arguments.of(named(bucketCouchbase.type().name(), bucketCouchbase)),
Arguments.of(named(bucketMemcache.type().name(), bucketMemcache)));
}
private CouchbaseCluster prepareCluster(BucketSettings bucketSettings) {
CouchbaseEnvironment environment = envBuilder(bucketSettings).build();
CouchbaseCluster cluster =
CouchbaseCluster.create(environment, Collections.singletonList("127.0.0.1"));
cleanup.deferCleanup(cluster::disconnect);
cleanup.deferCleanup(environment::shutdown);
return cluster;
}
@ParameterizedTest
@MethodSource("bucketSettings")
void hasBucket(BucketSettings bucketSettings) {
CouchbaseCluster cluster = prepareCluster(bucketSettings);
ClusterManager manager = cluster.clusterManager(USERNAME, PASSWORD);
testing.waitForTraces(1);
testing.clearData();
boolean hasBucket = manager.hasBucket(bucketSettings.name());
assertThat(hasBucket).isTrue();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> assertCouchbaseSpan(span, "ClusterManager.hasBucket").hasNoParent()));
}
@ParameterizedTest
@MethodSource("bucketSettings")
void upsertAndGet(BucketSettings bucketSettings) {
CouchbaseCluster cluster = prepareCluster(bucketSettings);
// Connect to the bucket and open it
Bucket bucket = cluster.openBucket(bucketSettings.name(), bucketSettings.password());
// Create a JSON document and store it with the ID "helloworld"
JsonObject content = JsonObject.create().put("hello", "world");
AtomicReference<JsonDocument> inserted = new AtomicReference<>();
AtomicReference<JsonDocument> found = new AtomicReference<>();
testing.runWithSpan(
"someTrace",
() -> {
inserted.set(bucket.upsert(JsonDocument.create("helloworld", content)));
found.set(bucket.get("helloworld"));
});
assertThat(found.get()).isEqualTo(inserted.get());
assertThat(found.get().content().getString("hello")).isEqualTo("world");
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("Cluster.openBucket")
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
DbIncubatingAttributes.DB_SYSTEM,
DbIncubatingAttributes.DbSystemValues.COUCHBASE),
equalTo(DbIncubatingAttributes.DB_OPERATION, "Cluster.openBucket"))),
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketSettings.name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(span, "Bucket.get", bucketSettings.name())
.hasParent(trace.getSpan(0))));
}
@Test
void query() {
// Only couchbase buckets support queries.
CouchbaseCluster cluster = prepareCluster(bucketCouchbase);
Bucket bucket = cluster.openBucket(bucketCouchbase.name(), bucketCouchbase.password());
// Mock expects this specific query.
// See com.couchbase.mock.http.query.QueryServer.handleString.
N1qlQueryResult result = bucket.query(N1qlQuery.simple("SELECT mockrow"));
assertThat(result.parseSuccess()).isTrue();
assertThat(result.finalSuccess()).isTrue();
assertThat(result.rows().next().value().get("row")).isEqualTo("value");
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("Cluster.openBucket")
.hasKind(SpanKind.CLIENT)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
DbIncubatingAttributes.DB_SYSTEM,
DbIncubatingAttributes.DbSystemValues.COUCHBASE),
equalTo(DbIncubatingAttributes.DB_OPERATION, "Cluster.openBucket"))),
trace ->
trace.hasSpansSatisfyingExactly(
span ->
assertCouchbaseSpan(
span,
"SELECT " + bucketCouchbase.name(),
"SELECT",
bucketCouchbase.name(),
"SELECT mockrow")
.hasNoParent()));
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import com.couchbase.client.java.bucket.BucketType;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.cluster.DefaultBucketSettings;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;
import com.couchbase.mock.Bucket;
import com.couchbase.mock.BucketConfiguration;
import com.couchbase.mock.CouchbaseMock;
import com.couchbase.mock.http.query.QueryServer;
import com.couchbase.mock.httpio.HttpServer;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.test.utils.PortUtils;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class AbstractCouchbaseTest {
private static final Logger logger = LoggerFactory.getLogger(AbstractCouchbaseTest.class);
protected static final String USERNAME = "Administrator";
protected static final String PASSWORD = "password";
private static final int port = PortUtils.findOpenPort();
protected static final BucketSettings bucketCouchbase =
DefaultBucketSettings.builder()
.enableFlush(true)
.name("$testBucketName-cb")
.password("test-pass")
.type(BucketType.COUCHBASE)
.quota(100)
.build();
protected static final BucketSettings bucketMemcache =
DefaultBucketSettings.builder()
.enableFlush(true)
.name("$testBucketName-mem")
.password("test-pass")
.type(BucketType.MEMCACHED)
.quota(100)
.build();
private CouchbaseMock mock;
@BeforeAll
void setUp() throws Exception {
mock = new CouchbaseMock("127.0.0.1", port, 1, 1);
Field httpServerFiled = CouchbaseMock.class.getDeclaredField("httpServer");
httpServerFiled.setAccessible(true);
HttpServer httpServer = (HttpServer) httpServerFiled.get(mock);
httpServer.register("/query", new QueryServer());
mock.start();
logger.info("CouchbaseMock listening on localhost:{}", port);
mock.createBucket(convert(bucketCouchbase));
mock.createBucket(convert(bucketMemcache));
}
private static BucketConfiguration convert(BucketSettings bucketSettings) {
BucketConfiguration configuration = new BucketConfiguration();
configuration.name = bucketSettings.name();
configuration.password = bucketSettings.password();
configuration.type = Bucket.BucketType.valueOf(bucketSettings.type().name());
configuration.numNodes = 1;
configuration.numReplicas = 0;
return configuration;
}
@AfterAll
void cleanUp() {
mock.stop();
}
protected DefaultCouchbaseEnvironment.Builder envBuilder(
EnvBuilder envBuilder, BucketSettings bucketSettings) {
return envBuilder.apply(bucketSettings, mock.getCarrierPort(bucketSettings.name()), port);
}
protected abstract DefaultCouchbaseEnvironment.Builder envBuilder(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort);
protected DefaultCouchbaseEnvironment.Builder envBuilder(BucketSettings bucketSettings) {
return envBuilder(this::envBuilder, bucketSettings);
}
@FunctionalInterface
public interface EnvBuilder {
DefaultCouchbaseEnvironment.Builder apply(
BucketSettings bucketSettings, int carrierDirectPort, int httpDirectPort);
}
protected SpanDataAssert assertCouchbaseSpan(SpanDataAssert span, String operation) {
return assertCouchbaseSpan(span, operation, null);
}
protected SpanDataAssert assertCouchbaseSpan(
SpanDataAssert span, String operation, String bucketName) {
return assertCouchbaseSpan(span, operation, operation, bucketName, null);
}
protected SpanDataAssert assertCouchbaseSpan(
SpanDataAssert span, String spanName, String operation, String bucketName, String statement) {
span.hasName(spanName).hasKind(SpanKind.CLIENT);
List<AttributeAssertion> assertions = new ArrayList<>();
assertions.add(
equalTo(DbIncubatingAttributes.DB_SYSTEM, DbIncubatingAttributes.DbSystemValues.COUCHBASE));
if (operation != null) {
assertions.add(equalTo(DbIncubatingAttributes.DB_OPERATION, operation));
}
if (bucketName != null) {
assertions.add(equalTo(DbIncubatingAttributes.DB_NAME, bucketName));
}
if (statement != null) {
assertions.add(satisfies(DbIncubatingAttributes.DB_STATEMENT, s -> s.startsWith(statement)));
}
assertions.addAll(couchbaseAttributes());
span.hasAttributesSatisfyingExactly(assertions);
return span;
}
protected List<AttributeAssertion> couchbaseAttributes() {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,198 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase.springdata;
import static org.assertj.core.api.Assertions.assertThat;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.view.DefaultView;
import com.couchbase.client.java.view.DesignDocument;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import java.util.Collections;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public abstract class AbstractCouchbaseSpringRepositoryTest extends AbstractCouchbaseTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
private ConfigurableApplicationContext applicationContext;
private TestRepository repository;
@BeforeAll
void setUp() {
CouchbaseEnvironment environment = envBuilder(bucketCouchbase).build();
Cluster couchbaseCluster =
CouchbaseCluster.create(environment, Collections.singletonList("127.0.0.1"));
// Create view for SpringRepository's findAll()
couchbaseCluster
.openBucket(bucketCouchbase.name(), bucketCouchbase.password())
.bucketManager()
.insertDesignDocument(
DesignDocument.create(
"testDocument",
Collections.singletonList(
DefaultView.create(
"all",
"function (doc, meta) {"
+ " if (doc._class == \"io.opentelemetry.instrumentation.couchbase.springdata.TestDocument\") {"
+ " emit(meta.id, null);"
+ " }"
+ "}"))));
CouchbaseConfig.environment = environment;
CouchbaseConfig.bucketSettings = bucketCouchbase;
// Close all buckets and disconnect
couchbaseCluster.disconnect();
applicationContext = new AnnotationConfigApplicationContext(CouchbaseConfig.class);
repository = applicationContext.getBean(TestRepository.class);
}
@AfterEach
void cleanUpTest() {
testing.clearData();
repository.deleteAll();
testing.waitForTraces(1);
}
@AfterAll
void cleanUp() {
applicationContext.close();
}
protected abstract TestDocument findById(TestRepository repository, String id);
protected abstract void deleteById(TestRepository repository, String id);
@Test
void emptyRepo() {
Iterable<TestDocument> result = repository.findAll();
assertThat(result.iterator().hasNext()).isFalse();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
assertCouchbaseSpan(
span,
bucketCouchbase.name(),
null,
bucketCouchbase.name(),
"ViewQuery(testDocument/all)")
.hasNoParent()));
}
@Test
void save() {
TestDocument document = new TestDocument();
TestDocument result = repository.save(document);
assertThat(result).isEqualTo(document);
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketCouchbase.name())
.hasNoParent()));
}
@Test
void saveAndRetrieve() {
TestDocument document = new TestDocument();
TestDocument result =
testing.runWithSpan(
"someTrace",
() -> {
repository.save(document);
return findById(repository, "1");
});
assertThat(result).isEqualTo(document);
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketCouchbase.name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(span, "Bucket.get", bucketCouchbase.name())
.hasParent(trace.getSpan(0))));
}
@Test
void saveAndUpdate() {
TestDocument document = new TestDocument();
testing.runWithSpan(
"someTrace",
() -> {
repository.save(document);
document.setData("other data");
repository.save(document);
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketCouchbase.name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketCouchbase.name())
.hasParent(trace.getSpan(0))));
}
@Test
void saveAndDelete() {
TestDocument document = new TestDocument();
boolean found =
testing.runWithSpan(
"someTrace",
() -> {
repository.save(document);
deleteById(repository, "1");
return repository.findAll().iterator().hasNext();
});
assertThat(found).isFalse();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", bucketCouchbase.name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(span, "Bucket.remove", bucketCouchbase.name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(
span,
bucketCouchbase.name(),
null,
bucketCouchbase.name(),
"ViewQuery(testDocument/all)")
.hasParent(trace.getSpan(0))));
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase.springdata;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Named.named;
import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.cluster.ClusterManager;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.couchbase.AbstractCouchbaseTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
public abstract class AbstractCouchbaseSpringTemplateTest extends AbstractCouchbaseTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
private static final List<AutoCloseable> cleanup = new ArrayList<>();
private static CouchbaseTemplate couchbaseTemplate;
private static CouchbaseTemplate memcacheTemplate;
@BeforeAll
void setUp() {
CouchbaseEnvironment couchbaseEnvironment = envBuilder(bucketCouchbase).build();
CouchbaseEnvironment memcacheEnvironment = envBuilder(bucketMemcache).build();
Cluster couchbaseCluster =
CouchbaseCluster.create(couchbaseEnvironment, Collections.singletonList("127.0.0.1"));
Cluster memcacheCluster =
CouchbaseCluster.create(memcacheEnvironment, Collections.singletonList("127.0.0.1"));
ClusterManager couchbaseManager = couchbaseCluster.clusterManager(USERNAME, PASSWORD);
ClusterManager memcacheManager = memcacheCluster.clusterManager(USERNAME, PASSWORD);
Bucket couchbaseBucket =
couchbaseCluster.openBucket(bucketCouchbase.name(), bucketCouchbase.password());
Bucket memcacheBucket =
memcacheCluster.openBucket(bucketMemcache.name(), bucketMemcache.password());
cleanup.add(couchbaseBucket::close);
cleanup.add(memcacheBucket::close);
cleanup.add(couchbaseCluster::disconnect);
cleanup.add(memcacheCluster::disconnect);
cleanup.add(couchbaseEnvironment::shutdown);
cleanup.add(memcacheEnvironment::shutdown);
testing.runWithSpan(
"getting info",
() -> {
couchbaseTemplate = new CouchbaseTemplate(couchbaseManager.info(), couchbaseBucket);
memcacheTemplate = new CouchbaseTemplate(memcacheManager.info(), memcacheBucket);
});
}
@AfterAll
void cleanUp() throws Exception {
for (AutoCloseable closeable : cleanup) {
closeable.close();
}
}
private static Stream<Arguments> templates() {
return Stream.of(
Arguments.of(named(bucketCouchbase.type().name(), couchbaseTemplate)),
Arguments.of(named(bucketMemcache.type().name(), memcacheTemplate)));
}
@ParameterizedTest
@MethodSource("templates")
void write(CouchbaseTemplate template) {
TestDocument document = new TestDocument();
TestDocument result =
testing.runWithSpan(
"someTrace",
() -> {
template.save(document);
return template.findById("1", TestDocument.class);
});
assertThat(result).isNotNull();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", template.getCouchbaseBucket().name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(span, "Bucket.get", template.getCouchbaseBucket().name())
.hasParent(trace.getSpan(0))));
}
@ParameterizedTest
@MethodSource("templates")
void remove(CouchbaseTemplate template) {
TestDocument document = new TestDocument();
testing.runWithSpan(
"someTrace",
() -> {
template.save(document);
template.remove(document);
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
assertCouchbaseSpan(span, "Bucket.upsert", template.getCouchbaseBucket().name())
.hasParent(trace.getSpan(0)),
span ->
assertCouchbaseSpan(span, "Bucket.remove", template.getCouchbaseBucket().name())
.hasParent(trace.getSpan(0))));
testing.clearData();
TestDocument result = template.findById("1", TestDocument.class);
assertThat(result).isNull();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
assertCouchbaseSpan(span, "Bucket.get", template.getCouchbaseBucket().name())
.hasNoParent()));
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase.springdata;
import static java.util.Objects.requireNonNull;
import com.couchbase.client.java.cluster.BucketSettings;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
@Configuration
@EnableCouchbaseRepositories(basePackages = "io.opentelemetry.instrumentation.couchbase.springdata")
@ComponentScan(basePackages = "io.opentelemetry.instrumentation.couchbase.springdata")
class CouchbaseConfig extends AbstractCouchbaseConfiguration {
// These need to be set before this class can be used by Spring
static CouchbaseEnvironment environment;
static BucketSettings bucketSettings;
@Override
protected CouchbaseEnvironment getEnvironment() {
return requireNonNull(environment);
}
@Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("127.0.0.1");
}
@Override
protected String getBucketName() {
return bucketSettings.name();
}
@Override
protected String getBucketPassword() {
return bucketSettings.password();
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase.springdata;
import java.util.Objects;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.core.mapping.Document;
@Document
public class TestDocument {
@Id private String id = "1";
private String data = "some data";
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof TestDocument)) {
return false;
}
TestDocument doc = (TestDocument) object;
return Objects.equals(id, doc.id) && Objects.equals(data, doc.data);
}
@Override
public int hashCode() {
return Objects.hash(id, data);
}
}

View File

@ -0,0 +1,10 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.couchbase.springdata;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
public interface TestRepository extends CouchbaseRepository<TestDocument, String> {}