Cleanup mongodb tests (#2732)

* Cleanup mongodb tests

* Drift

* Cleanup

* Timeout

* Cleanup
This commit is contained in:
Anuraag Agrawal 2021-04-07 14:02:56 +09:00 committed by GitHub
parent d8f6018ba6
commit 826d8ac781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 515 additions and 948 deletions

View File

@ -17,135 +17,63 @@ import org.bson.BsonString
import org.bson.Document import org.bson.Document
import spock.lang.Shared import spock.lang.Shared
class MongoClientTest extends MongoBaseTest { class MongoClientTest extends AbstractMongoClientTest {
@Shared @Shared
MongoClient client MongoClient client
def setup() throws Exception { def setupSpec() throws Exception {
client = new MongoClient(new ServerAddress("localhost", port), client = new MongoClient(new ServerAddress("localhost", port),
MongoClientOptions.builder() MongoClientOptions.builder()
.description("some-description") .description("some-description")
.build()) .build())
} }
def cleanup() throws Exception { def cleanupSpec() throws Exception {
client?.close() client?.close()
client = null client = null
} }
def "test create collection"() { @Override
setup: void createCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
when:
db.createCollection(collectionName) db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
// Tests the fix for https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/457 @Override
// TracingCommandListener might get added multiple times if clientOptions are built using existing clientOptions or when calling a build method twice. void createCollectionNoDescription(String dbName, String collectionName) {
// This test asserts that duplicate traces are not created in those cases. MongoDatabase db = new MongoClient("localhost", port).getDatabase(dbName)
def "test create collection with already built ClientOptions"() { db.createCollection(collectionName)
setup: }
@Override
void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
def clientOptions = client.mongoClientOptions def clientOptions = client.mongoClientOptions
def newClientOptions = MongoClientOptions.builder(clientOptions).build() def newClientOptions = MongoClientOptions.builder(clientOptions).build()
MongoDatabase db = new MongoClient(new ServerAddress("localhost", port), newClientOptions).getDatabase(dbName) MongoDatabase db = new MongoClient(new ServerAddress("localhost", port), newClientOptions).getDatabase(dbName)
when:
db.createCollection(collectionName) db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test create collection no description"() { @Override
setup: int getCollection(String dbName, String collectionName) {
MongoDatabase db = new MongoClient("localhost", port).getDatabase(dbName)
when:
db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
}
def "test get collection"() {
setup:
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
return db.getCollection(collectionName).count()
when:
int count = db.getCollection(collectionName).count()
then:
count == 0
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test insert"() { @Override
setup: int insert(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.insertOne(new Document("password", "SECRET")) collection.insertOne(new Document("password", "SECRET"))
return collection.count()
then:
collection.count() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "insert", collectionName, dbName, "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test update"() { @Override
setup: int update(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
@ -154,31 +82,15 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = collection.updateOne( def result = collection.updateOne(
new BsonDocument("password", new BsonString("OLDPW")), new BsonDocument("password", new BsonString("OLDPW")),
new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW")))) new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))))
collection.count()
then: return result.modifiedCount
result.modifiedCount == 1
collection.count() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "update", collectionName, dbName, "{\"update\":\"$collectionName\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test delete"() { @Override
setup: int delete(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
@ -187,29 +99,13 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))) def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")))
collection.count()
then: return result.deletedCount
result.deletedCount == 1
collection.count() == 0
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "delete", collectionName, dbName, "{\"delete\":\"$collectionName\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test collection name for getMore command"() { @Override
setup: void getMore(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def coll = db.getCollection(collectionName) def coll = db.getCollection(collectionName)
@ -217,46 +113,19 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.find().filter(new Document("_id", new Document('$gte', 0))) collection.find().filter(new Document("_id", new Document('$gte', 0)))
.batchSize(2).into(new ArrayList()) .batchSize(2).into(new ArrayList())
then:
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "find", collectionName, dbName, '{"find":"testCollection","filter":{"_id":{"$gte":"?"}},"batchSize":"?"}')
}
trace(1, 1) {
mongoSpan(it, 0, "getMore", collectionName, dbName, '{"getMore":"?","collection":"?","batchSize":"?"}')
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test error"() { @Override
setup: void error(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.updateOne(new BsonDocument(), new BsonDocument()) collection.updateOne(new BsonDocument(), new BsonDocument())
then:
thrown(IllegalArgumentException)
// Unfortunately not caught by our instrumentation.
assertTraces(0) {}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test client failure"() { def "test client failure"() {
@ -275,6 +144,6 @@ class MongoClientTest extends MongoBaseTest {
where: where:
dbName = "test_db" dbName = "test_db"
collectionName = "testCollection" collectionName = createCollectionName()
} }
} }

View File

@ -18,12 +18,12 @@ import org.bson.BsonString
import org.bson.Document import org.bson.Document
import spock.lang.Shared import spock.lang.Shared
class MongoClientTest extends MongoBaseTest { class MongoClientTest extends AbstractMongoClientTest {
@Shared @Shared
MongoClient client MongoClient client
def setup() throws Exception { def setupSpec() throws Exception {
client = MongoClients.create(MongoClientSettings.builder() client = MongoClients.create(MongoClientSettings.builder()
.applyToClusterSettings({ builder -> .applyToClusterSettings({ builder ->
builder.hosts(Arrays.asList( builder.hosts(Arrays.asList(
@ -33,35 +33,25 @@ class MongoClientTest extends MongoBaseTest {
.build()) .build())
} }
def cleanup() throws Exception { def cleanupSpec() throws Exception {
client?.close() client?.close()
client = null client = null
} }
def "test create collection"() { @Override
setup: void createCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
when:
db.createCollection(collectionName) db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
// Tests the fix for https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/457 @Override
// TracingCommandListener might get added multiple times if ClientSettings are built using existing ClientSettings or when calling a build method twice. void createCollectionNoDescription(String dbName, String collectionName) {
// This test asserts that duplicate traces are not created in those cases. MongoDatabase db = MongoClients.create("mongodb://localhost:${port}").getDatabase(dbName)
def "test create collection with already built ClientSettings"() { db.createCollection(collectionName)
setup: }
@Override
void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
def clientSettings = MongoClientSettings.builder() def clientSettings = MongoClientSettings.builder()
.applyToClusterSettings({ builder -> .applyToClusterSettings({ builder ->
builder.hosts(Arrays.asList( builder.hosts(Arrays.asList(
@ -71,91 +61,29 @@ class MongoClientTest extends MongoBaseTest {
.build() .build()
def newClientSettings = MongoClientSettings.builder(clientSettings).build() def newClientSettings = MongoClientSettings.builder(clientSettings).build()
MongoDatabase db = MongoClients.create(newClientSettings).getDatabase(dbName) MongoDatabase db = MongoClients.create(newClientSettings).getDatabase(dbName)
when:
db.createCollection(collectionName) db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test create collection no description"() { @Override
setup: int getCollection(String dbName, String collectionName) {
MongoDatabase db = MongoClients.create("mongodb://localhost:" + port).getDatabase(dbName)
when:
db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
}
def "test get collection"() {
setup:
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
return db.getCollection(collectionName).count()
when:
int count = db.getCollection(collectionName).count()
then:
count == 0
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test insert"() { @Override
setup: int insert(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.insertOne(new Document("password", "SECRET")) collection.insertOne(new Document("password", "SECRET"))
return collection.count()
then:
collection.count() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "insert", collectionName, dbName, "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test update"() { @Override
setup: int update(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
@ -164,31 +92,15 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = collection.updateOne( def result = collection.updateOne(
new BsonDocument("password", new BsonString("OLDPW")), new BsonDocument("password", new BsonString("OLDPW")),
new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW")))) new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))))
collection.count()
then: return result.modifiedCount
result.modifiedCount == 1
collection.count() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "update", collectionName, dbName, "{\"update\":\"$collectionName\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test delete"() { @Override
setup: int delete(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
@ -197,29 +109,13 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))) def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")))
collection.count()
then: return result.deletedCount
result.deletedCount == 1
collection.count() == 0
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "delete", collectionName, dbName, "{\"delete\":\"$collectionName\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test collection name for getMore command"() { @Override
setup: void getMore(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def coll = db.getCollection(collectionName) def coll = db.getCollection(collectionName)
@ -227,46 +123,19 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.find().filter(new Document("_id", new Document('$gte', 0))) collection.find().filter(new Document("_id", new Document('$gte', 0)))
.batchSize(2).into(new ArrayList()) .batchSize(2).into(new ArrayList())
then:
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "find", collectionName, dbName, '{"find":"testCollection","filter":{"_id":{"$gte":"?"}},"batchSize":"?"}')
}
trace(1, 1) {
mongoSpan(it, 0, "getMore", collectionName, dbName, '{"getMore":"?","collection":"?","batchSize":"?"}')
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test error"() { @Override
setup: void error(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.updateOne(new BsonDocument(), new BsonDocument()) collection.updateOne(new BsonDocument(), new BsonDocument())
then:
thrown(IllegalArgumentException)
// Unfortunately not caught by our instrumentation.
assertTraces(0) {}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test client failure"() { def "test client failure"() {
@ -284,6 +153,6 @@ class MongoClientTest extends MongoBaseTest {
where: where:
dbName = "test_db" dbName = "test_db"
collectionName = "testCollection" collectionName = createCollectionName()
} }
} }

View File

@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
import com.mongodb.client.result.DeleteResult import com.mongodb.client.result.DeleteResult
@ -12,105 +11,57 @@ import com.mongodb.reactivestreams.client.MongoClient
import com.mongodb.reactivestreams.client.MongoClients import com.mongodb.reactivestreams.client.MongoClients
import com.mongodb.reactivestreams.client.MongoCollection import com.mongodb.reactivestreams.client.MongoCollection
import com.mongodb.reactivestreams.client.MongoDatabase import com.mongodb.reactivestreams.client.MongoDatabase
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import org.bson.BsonDocument import org.bson.BsonDocument
import org.bson.BsonString import org.bson.BsonString
import org.bson.Document import org.bson.Document
import org.junit.AssumptionViolatedException
import org.reactivestreams.Subscriber import org.reactivestreams.Subscriber
import org.reactivestreams.Subscription import org.reactivestreams.Subscription
import spock.lang.Shared import spock.lang.Shared
class Mongo4ReactiveClientTest extends MongoBaseTest { class Mongo4ReactiveClientTest extends AbstractMongoClientTest {
@Shared @Shared
MongoClient client MongoClient client
def setup() throws Exception { def setupSpec() throws Exception {
client = MongoClients.create("mongodb://localhost:$port") client = MongoClients.create("mongodb://localhost:$port")
} }
def cleanup() throws Exception { def cleanupSpec() throws Exception {
client?.close() client?.close()
client = null client = null
} }
def "test create collection"() { @Override
setup: void createCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
when:
db.createCollection(collectionName).subscribe(toSubscriber {}) db.createCollection(collectionName).subscribe(toSubscriber {})
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test create collection no description"() { @Override
setup: void createCollectionNoDescription(String dbName, String collectionName) {
MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(dbName) MongoDatabase db = MongoClients.create("mongodb://localhost:${port}").getDatabase(dbName)
when:
db.createCollection(collectionName).subscribe(toSubscriber {}) db.createCollection(collectionName).subscribe(toSubscriber {})
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, {
assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
})
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test get collection"() { @Override
setup: void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) throw new AssumptionViolatedException("not tested on 4.0")
}
when: @Override
def count = new CompletableFuture() int getCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName)
def count = new CompletableFuture<Integer>()
db.getCollection(collectionName).estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) }) db.getCollection(collectionName).estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) })
return count.join()
then:
count.get() == 0
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test insert"() { @Override
setup: int insert(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def latch1 = new CountDownLatch(1) def latch1 = new CountDownLatch(1)
@ -120,39 +71,15 @@ class Mongo4ReactiveClientTest extends MongoBaseTest {
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(2) ignoreTracesAndClear(2)
def count = new CompletableFuture<Integer>()
when:
def count = new CompletableFuture()
collection.insertOne(new Document("password", "SECRET")).subscribe(toSubscriber { collection.insertOne(new Document("password", "SECRET")).subscribe(toSubscriber {
collection.estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) }) collection.estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) })
}) })
return count.join()
then:
count.get() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "insert", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}" ||
it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test update"() { @Override
setup: int update(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def latch1 = new CountDownLatch(1) def latch1 = new CountDownLatch(1)
@ -165,8 +92,6 @@ class Mongo4ReactiveClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = new CompletableFuture<UpdateResult>() def result = new CompletableFuture<UpdateResult>()
def count = new CompletableFuture() def count = new CompletableFuture()
collection.updateOne( collection.updateOne(
@ -175,34 +100,11 @@ class Mongo4ReactiveClientTest extends MongoBaseTest {
result.complete(it) result.complete(it)
collection.estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) }) collection.estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) })
}) })
return result.join().modifiedCount
then:
result.get().modifiedCount == 1
count.get() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "update", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"update\":\"$collectionName\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}" ||
it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test delete"() { @Override
setup: int delete(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def latch1 = new CountDownLatch(1) def latch1 = new CountDownLatch(1)
@ -215,38 +117,37 @@ class Mongo4ReactiveClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = new CompletableFuture<DeleteResult>() def result = new CompletableFuture<DeleteResult>()
def count = new CompletableFuture() def count = new CompletableFuture()
collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))).subscribe(toSubscriber { collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))).subscribe(toSubscriber {
result.complete(it) result.complete(it)
collection.estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) }) collection.estimatedDocumentCount().subscribe(toSubscriber { count.complete(it) })
}) })
return result.join().deletedCount
}
then: @Override
result.get().deletedCount == 1 void getMore(String dbName, String collectionName) {
count.get() == 0 throw new AssumptionViolatedException("not tested on reactive")
assertTraces(2) { }
trace(0, 1) {
mongoSpan(it, 0, "delete", collectionName, dbName) { @Override
assert it.replaceAll(" ", "") == "{\"delete\":\"$collectionName\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}" || void error(String dbName, String collectionName) {
it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" MongoCollection<Document> collection = runUnderTrace("setup") {
true MongoDatabase db = client.getDatabase(dbName)
} def latch = new CountDownLatch(1)
} db.createCollection(collectionName).subscribe(toSubscriber {
trace(1, 1) { latch.countDown()
mongoSpan(it, 0, "count", collectionName, dbName) { })
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || latch.await()
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" return db.getCollection(collectionName)
true
}
}
} }
ignoreTracesAndClear(1)
where: def result = new CompletableFuture<Throwable>()
dbName = "test_db" collection.updateOne(new BsonDocument(), new BsonDocument()).subscribe(toSubscriber {
collectionName = "testCollection" result.complete(it)
})
throw result.join()
} }
def Subscriber<?> toSubscriber(Closure closure) { def Subscriber<?> toSubscriber(Closure closure) {
@ -273,30 +174,4 @@ class Mongo4ReactiveClientTest extends MongoBaseTest {
} }
} }
} }
def mongoSpan(TraceAssert trace, int index,
String operation, String collection,
String dbName, Closure<Boolean> statementEval,
Object parentSpan = null, Throwable exception = null) {
trace.span(index) {
name { operation + " " + dbName + "." + collection }
kind CLIENT
if (parentSpan == null) {
hasNoParent()
} else {
childOf((SpanData) parentSpan)
}
attributes {
"$SemanticAttributes.NET_PEER_NAME.key" "localhost"
"$SemanticAttributes.NET_PEER_IP.key" "127.0.0.1"
"$SemanticAttributes.NET_PEER_PORT.key" port
"$SemanticAttributes.DB_CONNECTION_STRING.key" "mongodb://localhost:" + port
"$SemanticAttributes.DB_STATEMENT.key" statementEval
"$SemanticAttributes.DB_SYSTEM.key" "mongodb"
"$SemanticAttributes.DB_NAME.key" dbName
"$SemanticAttributes.DB_OPERATION.key" operation
"$SemanticAttributes.DB_MONGODB_COLLECTION.key" collection
}
}
}
} }

View File

@ -3,10 +3,8 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
import com.mongodb.MongoTimeoutException
import com.mongodb.client.MongoClient import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients import com.mongodb.client.MongoClients
import com.mongodb.client.MongoCollection import com.mongodb.client.MongoCollection
@ -14,110 +12,60 @@ import com.mongodb.client.MongoDatabase
import org.bson.BsonDocument import org.bson.BsonDocument
import org.bson.BsonString import org.bson.BsonString
import org.bson.Document import org.bson.Document
import org.junit.AssumptionViolatedException
import spock.lang.Shared import spock.lang.Shared
class MongoClientTest extends MongoBaseTest { class MongoClientTest extends AbstractMongoClientTest {
@Shared @Shared
MongoClient client MongoClient client
def setup() throws Exception { def setupSpec() throws Exception {
client = MongoClients.create("mongodb://localhost:$port") client = MongoClients.create("mongodb://localhost:$port")
} }
def cleanup() throws Exception { def cleanupSpec() throws Exception {
client?.close() client?.close()
client = null client = null
} }
def "test create collection"() { @Override
setup: void createCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
when:
db.createCollection(collectionName) db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test create collection no description"() { @Override
setup: void createCollectionNoDescription(String dbName, String collectionName) {
MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(dbName) MongoDatabase db = MongoClients.create("mongodb://localhost:${port}").getDatabase(dbName)
when:
db.createCollection(collectionName) db.createCollection(collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, "{\"create\":\"$collectionName\",\"capped\":\"?\"}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test get collection"() { @Override
setup: void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
throw new AssumptionViolatedException("not tested on 4.0")
}
@Override
int getCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
return db.getCollection(collectionName).estimatedDocumentCount()
when:
int count = db.getCollection(collectionName).estimatedDocumentCount()
then:
count == 0
assertTraces(1) {
trace(0,1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test insert"() { @Override
setup: int insert(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.insertOne(new Document("password", "SECRET")) collection.insertOne(new Document("password", "SECRET"))
return collection.estimatedDocumentCount()
then:
collection.estimatedDocumentCount() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "insert", collectionName, dbName, "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test update"() { @Override
setup: int update(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
@ -126,31 +74,15 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = collection.updateOne( def result = collection.updateOne(
new BsonDocument("password", new BsonString("OLDPW")), new BsonDocument("password", new BsonString("OLDPW")),
new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW")))) new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))))
collection.estimatedDocumentCount()
then: return result.modifiedCount
result.modifiedCount == 1
collection.estimatedDocumentCount() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "update", collectionName, dbName, "{\"update\":\"$collectionName\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test delete"() { @Override
setup: int delete(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
@ -159,65 +91,32 @@ class MongoClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))) def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")))
collection.estimatedDocumentCount()
then: return result.deletedCount
result.deletedCount == 1
collection.estimatedDocumentCount() == 0
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "delete", collectionName, dbName, "{\"delete\":\"$collectionName\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}")
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName, "{\"count\":\"$collectionName\",\"query\":{}}")
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test error"() { @Override
setup: void getMore(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName)
def coll = db.getCollection(collectionName)
coll.insertMany([new Document("_id", 0), new Document("_id", 1), new Document("_id", 2)])
return coll
}
ignoreTracesAndClear(1)
collection.find().filter(new Document("_id", new Document('$gte', 0)))
.batchSize(2).into(new ArrayList())
}
@Override
void error(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName) db.createCollection(collectionName)
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
collection.updateOne(new BsonDocument(), new BsonDocument()) collection.updateOne(new BsonDocument(), new BsonDocument())
then:
thrown(IllegalArgumentException)
// Unfortunately not caught by our instrumentation.
assertTraces(0) {}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test client failure"() {
setup:
def client = MongoClients.create("mongodb://localhost:$UNUSABLE_PORT/?serverselectiontimeoutms=10")
when:
MongoDatabase db = client.getDatabase(dbName)
db.createCollection(collectionName)
then:
thrown(MongoTimeoutException)
// Unfortunately not caught by our instrumentation.
assertTraces(0) {}
where:
dbName = "test_db"
collectionName = "testCollection"
}
} }

View File

@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
import com.mongodb.ConnectionString import com.mongodb.ConnectionString
@ -16,22 +15,20 @@ import com.mongodb.async.client.MongoDatabase
import com.mongodb.client.result.DeleteResult import com.mongodb.client.result.DeleteResult
import com.mongodb.client.result.UpdateResult import com.mongodb.client.result.UpdateResult
import com.mongodb.connection.ClusterSettings import com.mongodb.connection.ClusterSettings
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import org.bson.BsonDocument import org.bson.BsonDocument
import org.bson.BsonString import org.bson.BsonString
import org.bson.Document import org.bson.Document
import org.junit.AssumptionViolatedException
import spock.lang.Shared import spock.lang.Shared
class MongoAsyncClientTest extends MongoBaseTest { class MongoAsyncClientTest extends AbstractMongoClientTest {
@Shared @Shared
MongoClient client MongoClient client
def setup() throws Exception { def setupSpec() throws Exception {
client = MongoClients.create( client = MongoClients.create(
MongoClientSettings.builder() MongoClientSettings.builder()
.clusterSettings( .clusterSettings(
@ -42,112 +39,41 @@ class MongoAsyncClientTest extends MongoBaseTest {
.build()) .build())
} }
def cleanup() throws Exception { def cleanupSpec() throws Exception {
client?.close() client?.close()
client = null client = null
} }
def "test create collection"() { @Override
setup: void createCollection(String dbName, String collectionName) {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
when:
db.createCollection(collectionName, toCallback {}) db.createCollection(collectionName, toCallback {})
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
// Tests the fix for https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/457 @Override
// TracingCommandListener might get added multiple times if ClientSettings are built using existing ClientSettings or when calling a build method twice. void createCollectionNoDescription(String dbName, String collectionName) {
// This test asserts that duplicate traces are not created in those cases. MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(dbName)
def "test create collection with already built ClientSettings"() { db.createCollection(collectionName, toCallback {})
setup: }
@Override
void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
def clientSettings = client.settings def clientSettings = client.settings
def newClientSettings = MongoClientSettings.builder(clientSettings).build() def newClientSettings = MongoClientSettings.builder(clientSettings).build()
MongoDatabase db = MongoClients.create(newClientSettings).getDatabase(dbName) MongoDatabase db = MongoClients.create(newClientSettings).getDatabase(dbName)
when:
db.createCollection(collectionName, toCallback {}) db.createCollection(collectionName, toCallback {})
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test create collection no description"() { @Override
setup: int getCollection(String dbName, String collectionName) {
MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(dbName)
when:
db.createCollection(collectionName, toCallback {})
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
}
def "test get collection"() {
setup:
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def count = new CompletableFuture<Integer>()
when:
def count = new CompletableFuture()
db.getCollection(collectionName).count toCallback { count.complete(it) } db.getCollection(collectionName).count toCallback { count.complete(it) }
return count.join()
then:
count.get() == 0
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test insert"() { @Override
setup: int insert(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def latch1 = new CountDownLatch(1) def latch1 = new CountDownLatch(1)
@ -156,39 +82,15 @@ class MongoAsyncClientTest extends MongoBaseTest {
return db.getCollection(collectionName) return db.getCollection(collectionName)
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
def count = new CompletableFuture<Integer>()
when:
def count = new CompletableFuture()
collection.insertOne(new Document("password", "SECRET"), toCallback { collection.insertOne(new Document("password", "SECRET"), toCallback {
collection.count toCallback { count.complete(it) } collection.count toCallback { count.complete(it) }
}) })
return count.get()
then:
count.get() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "insert", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}" ||
it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test update"() { @Override
setup: int update(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def latch1 = new CountDownLatch(1) def latch1 = new CountDownLatch(1)
@ -201,8 +103,6 @@ class MongoAsyncClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = new CompletableFuture<UpdateResult>() def result = new CompletableFuture<UpdateResult>()
def count = new CompletableFuture() def count = new CompletableFuture()
collection.updateOne( collection.updateOne(
@ -211,34 +111,11 @@ class MongoAsyncClientTest extends MongoBaseTest {
result.complete(it) result.complete(it)
collection.count toCallback { count.complete(it) } collection.count toCallback { count.complete(it) }
}) })
return result.get().modifiedCount
then:
result.get().modifiedCount == 1
count.get() == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "update", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"update\":\"$collectionName\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}" ||
it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = "testCollection"
} }
def "test delete"() { @Override
setup: int delete(String dbName, String collectionName) {
MongoCollection<Document> collection = runUnderTrace("setup") { MongoCollection<Document> collection = runUnderTrace("setup") {
MongoDatabase db = client.getDatabase(dbName) MongoDatabase db = client.getDatabase(dbName)
def latch1 = new CountDownLatch(1) def latch1 = new CountDownLatch(1)
@ -251,38 +128,37 @@ class MongoAsyncClientTest extends MongoBaseTest {
return coll return coll
} }
ignoreTracesAndClear(1) ignoreTracesAndClear(1)
when:
def result = new CompletableFuture<DeleteResult>() def result = new CompletableFuture<DeleteResult>()
def count = new CompletableFuture() def count = new CompletableFuture()
collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")), toCallback { collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")), toCallback {
result.complete(it) result.complete(it)
collection.count toCallback { count.complete(it) } collection.count toCallback { count.complete(it) }
}) })
return result.get().deletedCount
}
then: @Override
result.get().deletedCount == 1 void getMore(String dbName, String collectionName) {
count.get() == 0 throw new AssumptionViolatedException("not tested on async")
assertTraces(2) { }
trace(0, 1) {
mongoSpan(it, 0, "delete", collectionName, dbName) { @Override
assert it.replaceAll(" ", "") == "{\"delete\":\"$collectionName\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}" || void error(String dbName, String collectionName) {
it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" MongoCollection<Document> collection = runUnderTrace("setup") {
true MongoDatabase db = client.getDatabase(dbName)
} def latch = new CountDownLatch(1)
} db.createCollection(collectionName, toCallback {
trace(1, 1) { latch.countDown()
mongoSpan(it, 0, "count", collectionName, dbName) { })
assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || latch.await()
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" return db.getCollection(collectionName)
true
}
}
} }
ignoreTracesAndClear(1)
where: def result = new CompletableFuture<Throwable>()
dbName = "test_db" collection.updateOne(new BsonDocument(), new BsonDocument(), toCallback {
collectionName = "testCollection" result.complete(it)
})
throw result.join()
} }
SingleResultCallback toCallback(Closure closure) { SingleResultCallback toCallback(Closure closure) {
@ -297,30 +173,4 @@ class MongoAsyncClientTest extends MongoBaseTest {
} }
} }
} }
def mongoSpan(TraceAssert trace, int index,
String operation, String collection,
String dbName, Closure<Boolean> statementEval,
Object parentSpan = null, Throwable exception = null) {
trace.span(index) {
name { operation + " " + dbName + "." + collection }
kind CLIENT
if (parentSpan == null) {
hasNoParent()
} else {
childOf((SpanData) parentSpan)
}
attributes {
"$SemanticAttributes.NET_PEER_NAME.key" "localhost"
"$SemanticAttributes.NET_PEER_IP.key" "127.0.0.1"
"$SemanticAttributes.NET_PEER_PORT.key" port
"$SemanticAttributes.DB_CONNECTION_STRING.key" "mongodb://localhost:" + port
"$SemanticAttributes.DB_STATEMENT.key" statementEval
"$SemanticAttributes.DB_SYSTEM.key" "mongodb"
"$SemanticAttributes.DB_NAME.key" dbName
"$SemanticAttributes.DB_OPERATION.key" operation
"$SemanticAttributes.DB_MONGODB_COLLECTION.key" collection
}
}
}
} }

View File

@ -2,7 +2,7 @@ apply from: "$rootDir/gradle/java.gradle"
dependencies { dependencies {
api project(':testing-common') api project(':testing-common')
api group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' api group: 'org.testcontainers', name: 'mongodb', version: versions.testcontainers
implementation deps.groovy implementation deps.groovy
implementation deps.opentelemetryApi implementation deps.opentelemetryApi

View File

@ -0,0 +1,307 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import java.util.concurrent.atomic.AtomicInteger
import org.slf4j.LoggerFactory
import org.testcontainers.containers.GenericContainer
import org.testcontainers.containers.output.Slf4jLogConsumer
import spock.lang.Shared
abstract class AbstractMongoClientTest extends AgentInstrumentationSpecification {
@Shared
GenericContainer mongodb
@Shared
int port
def setupSpec() {
mongodb = new GenericContainer("mongo:3.2")
.withExposedPorts(27017)
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("mongodb")))
mongodb.start()
port = mongodb.getMappedPort(27017)
}
def cleanupSpec() throws Exception {
mongodb.stop()
}
// Different client versions have different APIs to do these operations. If adding a test for a new
// version, refer to existing ones on how to implement these operations.
abstract void createCollection(String dbName, String collectionName)
abstract void createCollectionNoDescription(String dbName, String collectionName)
// Tests the fix for https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/457
// TracingCommandListener might get added multiple times if clientOptions are built using existing clientOptions or when calling a build method twice.
// This test asserts that duplicate traces are not created in those cases.
abstract void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName)
abstract int getCollection(String dbName, String collectionName)
abstract int insert(String dbName, String collectionName)
abstract int update(String dbName, String collectionName)
abstract int delete(String dbName, String collectionName)
abstract void getMore(String dbName, String collectionName)
abstract void error(String dbName, String collectionName)
def "test port open"() {
when:
new Socket("localhost", port)
then:
noExceptionThrown()
}
def "test create collection"() {
when:
createCollection(dbName, collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName) {
assert it == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test create collection no description"() {
when:
createCollectionNoDescription(dbName, collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName, {
assert it == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
})
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test get collection"() {
when:
def count = getCollection(dbName, collectionName)
then:
count == 0
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test insert"() {
when:
def count = insert(dbName, collectionName)
then:
count == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "insert", collectionName, dbName) {
assert it == "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}" ||
it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test update"() {
when:
int modifiedCount = update(dbName, collectionName)
then:
modifiedCount == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "update", collectionName, dbName) {
assert it == "{\"update\":\"$collectionName\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}" ||
it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test delete"() {
when:
int deletedCount = delete(dbName, collectionName)
then:
deletedCount == 1
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "delete", collectionName, dbName) {
assert it == "{\"delete\":\"$collectionName\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}" ||
it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}"
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "count", collectionName, dbName) {
assert it == "{\"count\":\"$collectionName\",\"query\":{}}" ||
it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test collection name for getMore command"() {
when:
getMore(dbName, collectionName)
then:
assertTraces(2) {
trace(0, 1) {
mongoSpan(it, 0, "find", collectionName, dbName) {
assert it == '{"find":"' + collectionName + '","filter":{"_id":{"$gte":"?"}},"batchSize":"?"}'
true
}
}
trace(1, 1) {
mongoSpan(it, 0, "getMore", collectionName, dbName) {
assert it == '{"getMore":"?","collection":"?","batchSize":"?"}'
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test error"() {
when:
error(dbName, collectionName)
then:
thrown(IllegalArgumentException)
// Unfortunately not caught by our instrumentation.
assertTraces(0) {}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
def "test create collection with already built ClientOptions"() {
when:
createCollectionWithAlreadyBuiltClientOptions(dbName, collectionName)
then:
assertTraces(1) {
trace(0, 1) {
mongoSpan(it, 0, "create", collectionName, dbName) {
assert it == "{\"create\":\"$collectionName\",\"capped\":\"?\"}"
true
}
}
}
where:
dbName = "test_db"
collectionName = createCollectionName()
}
private static final AtomicInteger collectionIndex = new AtomicInteger()
def createCollectionName() {
return "testCollection-${collectionIndex.getAndIncrement()}"
}
def mongoSpan(TraceAssert trace, int index,
String operation, String collection,
String dbName, Closure<Boolean> statementEval,
Object parentSpan = null, Throwable exception = null) {
trace.span(index) {
name { operation + " " + dbName + "." + collection }
kind CLIENT
if (parentSpan == null) {
hasNoParent()
} else {
childOf((SpanData) parentSpan)
}
attributes {
"$SemanticAttributes.NET_PEER_NAME.key" "localhost"
"$SemanticAttributes.NET_PEER_IP.key" "127.0.0.1"
"$SemanticAttributes.NET_PEER_PORT.key" port
"$SemanticAttributes.DB_STATEMENT.key" {
statementEval.call(it.replaceAll(" ", ""))
}
"$SemanticAttributes.DB_SYSTEM.key" "mongodb"
"$SemanticAttributes.DB_CONNECTION_STRING.key" "mongodb://localhost:" + port
"$SemanticAttributes.DB_NAME.key" dbName
"$SemanticAttributes.DB_OPERATION.key" operation
"$SemanticAttributes.DB_MONGODB_COLLECTION.key" collection
}
}
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import de.flapdoodle.embed.mongo.MongodExecutable
import de.flapdoodle.embed.mongo.MongodProcess
import de.flapdoodle.embed.mongo.MongodStarter
import de.flapdoodle.embed.mongo.config.IMongodConfig
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder
import de.flapdoodle.embed.mongo.config.Net
import de.flapdoodle.embed.mongo.distribution.Version
import de.flapdoodle.embed.process.runtime.Network
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.trace.attributes.SemanticAttributes
import spock.lang.Shared
/**
* Testing needs to be in a centralized project.
* If tests in multiple different projects are using embedded mongo,
* they downloader is at risk of a race condition.
*/
class MongoBaseTest extends AgentInstrumentationSpecification {
// https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo#executable-collision
private static final MongodStarter STARTER = MongodStarter.getDefaultInstance()
@Shared
int port = PortUtils.findOpenPort()
@Shared
MongodExecutable mongodExe
@Shared
MongodProcess mongod
def setup() throws Exception {
IMongodConfig mongodConfig =
new MongodConfigBuilder()
.version(Version.Main.PRODUCTION)
.net(new Net("localhost", port, Network.localhostIsIPv6()))
.build()
// using a system-wide file lock to prevent other modules that may be running in parallel
// from clobbering each other while downloading and extracting mongodb
def lockFile = new File(System.getProperty("java.io.tmpdir"), "prepare-embedded-mongo.lock")
def channel = new RandomAccessFile(lockFile, "rw").getChannel()
def lock = channel.lock()
try {
mongodExe = STARTER.prepare(mongodConfig)
} finally {
lock.release()
channel.close()
}
mongod = mongodExe.start()
}
def cleanup() throws Exception {
mongod?.stop()
mongod = null
mongodExe?.stop()
mongodExe = null
}
def "test port open"() {
when:
new Socket("localhost", port)
then:
noExceptionThrown()
}
def mongoSpan(TraceAssert trace, int index,
String operation, String collection,
String dbName, String statement,
Object parentSpan = null, Throwable exception = null) {
trace.span(index) {
name { operation + " " + dbName + "." + collection }
kind CLIENT
if (parentSpan == null) {
hasNoParent()
} else {
childOf((SpanData) parentSpan)
}
attributes {
"$SemanticAttributes.NET_PEER_NAME.key" "localhost"
"$SemanticAttributes.NET_PEER_IP.key" "127.0.0.1"
"$SemanticAttributes.NET_PEER_PORT.key" port
"$SemanticAttributes.DB_STATEMENT.key" {
it.replace(" ", "") == statement
}
"$SemanticAttributes.DB_SYSTEM.key" "mongodb"
"$SemanticAttributes.DB_CONNECTION_STRING.key" "mongodb://localhost:" + port
"$SemanticAttributes.DB_NAME.key" dbName
"$SemanticAttributes.DB_OPERATION.key" operation
"$SemanticAttributes.DB_MONGODB_COLLECTION.key" collection
}
}
}
}