Skip to content

Commit 350af71

Browse files
committed
Support CSFLE unified tests
- Added scala UnifiedTest support. - Added scala unified tests for ClientEncryption and Crud JAVA-5674
1 parent 2fe7827 commit 350af71

File tree

15 files changed

+279
-34
lines changed

15 files changed

+279
-34
lines changed

driver-core/src/test/functional/com/mongodb/ClusterFixture.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import java.util.HashMap;
8989
import java.util.List;
9090
import java.util.Map;
91+
import java.util.Optional;
9192

9293
import static com.mongodb.assertions.Assertions.assertNotNull;
9394
import static com.mongodb.connection.ClusterConnectionMode.LOAD_BALANCED;
@@ -278,6 +279,11 @@ public static String getEnv(final String name, final String defaultValue) {
278279
return value == null ? defaultValue : value;
279280
}
280281

282+
public static Optional<String> cryptSharedLibPathSysPropValue() {
283+
String value = getEnv("CRYPT_SHARED_LIB_PATH", "");
284+
return value.isEmpty() ? Optional.empty() : Optional.of(value);
285+
}
286+
281287
@Nullable
282288
public static String getEnv(final String name) {
283289
return System.getenv(name);

driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ public void create(final WriteConcern writeConcern, final BsonDocument createOpt
179179
case "encryptedFields":
180180
createCollectionOptions.encryptedFields(createOptions.getDocument("encryptedFields"));
181181
break;
182+
case "validator":
183+
ValidationOptions validationOptions = new ValidationOptions();
184+
validationOptions.validator(createOptions.getDocument("validator"));
185+
createCollectionOptions.validationOptions(validationOptions);
186+
break;
182187
default:
183188
throw new UnsupportedOperationException("Unsupported create collection option: " + option);
184189
}
@@ -198,6 +203,10 @@ public void create(final String collectionName, final CreateCollectionOptions op
198203
if (indexOptionDefaults.getStorageEngine() != null) {
199204
operation.indexOptionDefaults(new BsonDocument("storageEngine", toBsonDocument(indexOptionDefaults.getStorageEngine())));
200205
}
206+
Bson encryptedFields = options.getEncryptedFields();
207+
if (encryptedFields != null) {
208+
operation.encryptedFields(encryptedFields.toBsonDocument());
209+
}
201210
ValidationOptions validationOptions = options.getValidationOptions();
202211
if (validationOptions.getValidator() != null) {
203212
operation.validator(toBsonDocument(validationOptions.getValidator()));

driver-core/src/test/unit/com/mongodb/internal/connection/TestClusterListener.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.concurrent.locks.ReentrantLock;
3333
import java.util.function.Predicate;
3434

35-
import static com.mongodb.assertions.Assertions.isTrue;
3635
import static com.mongodb.assertions.Assertions.notNull;
3736
import static com.mongodb.internal.Locks.withLock;
3837

@@ -48,15 +47,17 @@ public final class TestClusterListener implements ClusterListener {
4847

4948
@Override
5049
public void clusterOpening(final ClusterOpeningEvent event) {
51-
isTrue("clusterOpeningEvent is null", clusterOpeningEvent == null);
52-
clusterOpeningEvent = event;
50+
if(clusterOpeningEvent == null) {
51+
clusterOpeningEvent = event;
52+
}
5353
}
5454

5555
@Override
5656
public void clusterClosed(final ClusterClosedEvent event) {
57-
isTrue("clusterClosingEvent is null", clusterClosingEvent == null);
58-
closedLatch.countDown();
59-
clusterClosingEvent = event;
57+
if(clusterClosingEvent == null) {
58+
closedLatch.countDown();
59+
clusterClosingEvent = event;
60+
}
6061
}
6162

6263
@Override
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.mongodb.scala.syncadapter
18+
19+
import com.mongodb.ClusterFixture.TIMEOUT_DURATION
20+
import com.mongodb.client.model.{ CreateCollectionOptions, CreateEncryptedCollectionParams }
21+
import com.mongodb.client.model.vault.{
22+
DataKeyOptions,
23+
EncryptOptions,
24+
RewrapManyDataKeyOptions,
25+
RewrapManyDataKeyResult
26+
}
27+
import com.mongodb.client.result.DeleteResult
28+
import com.mongodb.client.vault.{ ClientEncryption => JClientEncryption }
29+
import com.mongodb.client.{ MongoDatabase => JMongoDatabase }
30+
import org.bson.{ BsonBinary, BsonDocument, BsonValue }
31+
import org.bson.conversions.Bson
32+
import org.mongodb.scala.vault.ClientEncryption
33+
import reactor.core.publisher.Mono
34+
35+
import java.util.Objects.requireNonNull
36+
37+
case class SyncClientEncryption(wrapped: ClientEncryption) extends JClientEncryption {
38+
39+
override def createDataKey(kmsProvider: String): BsonBinary =
40+
requireNonNull(Mono.from(wrapped.createDataKey(kmsProvider, new DataKeyOptions)).block(TIMEOUT_DURATION))
41+
42+
override def createDataKey(kmsProvider: String, dataKeyOptions: DataKeyOptions): BsonBinary =
43+
requireNonNull(Mono.from(wrapped.createDataKey(kmsProvider, dataKeyOptions)).block(TIMEOUT_DURATION))
44+
45+
override def encrypt(value: BsonValue, options: EncryptOptions): BsonBinary =
46+
requireNonNull(Mono.from(wrapped.encrypt(value, options)).block(TIMEOUT_DURATION))
47+
48+
override def encryptExpression(expression: Bson, options: EncryptOptions): BsonDocument =
49+
requireNonNull(Mono.from(wrapped
50+
.encryptExpression(expression.toBsonDocument, options)).block(TIMEOUT_DURATION).toBsonDocument)
51+
52+
override def decrypt(value: BsonBinary): BsonValue =
53+
requireNonNull(Mono.from(wrapped.decrypt(value)).block(TIMEOUT_DURATION))
54+
55+
override def deleteKey(id: BsonBinary): DeleteResult =
56+
requireNonNull(Mono.from(wrapped.deleteKey(id)).block(TIMEOUT_DURATION))
57+
58+
override def getKey(id: BsonBinary): BsonDocument = Mono.from(wrapped.getKey(id)).block(TIMEOUT_DURATION)
59+
60+
override def getKeys = new SyncFindIterable[BsonDocument](wrapped.keys)
61+
62+
override def addKeyAltName(id: BsonBinary, keyAltName: String): BsonDocument =
63+
Mono.from(wrapped.addKeyAltName(id, keyAltName)).block(TIMEOUT_DURATION)
64+
65+
override def removeKeyAltName(id: BsonBinary, keyAltName: String): BsonDocument =
66+
Mono.from(wrapped.removeKeyAltName(id, keyAltName)).block(TIMEOUT_DURATION)
67+
68+
override def getKeyByAltName(keyAltName: String): BsonDocument =
69+
Mono.from(wrapped.getKeyByAltName(keyAltName)).block(TIMEOUT_DURATION)
70+
71+
override def rewrapManyDataKey(filter: Bson): RewrapManyDataKeyResult =
72+
requireNonNull(Mono.from(wrapped.rewrapManyDataKey(filter)).block(TIMEOUT_DURATION))
73+
74+
override def rewrapManyDataKey(filter: Bson, options: RewrapManyDataKeyOptions): RewrapManyDataKeyResult =
75+
requireNonNull(Mono.from(wrapped.rewrapManyDataKey(filter, options)).block(TIMEOUT_DURATION))
76+
77+
override def createEncryptedCollection(
78+
database: JMongoDatabase,
79+
collectionName: String,
80+
createCollectionOptions: CreateCollectionOptions,
81+
createEncryptedCollectionParams: CreateEncryptedCollectionParams
82+
): BsonDocument = {
83+
database match {
84+
case syncMongoDatabase: SyncMongoDatabase =>
85+
requireNonNull(Mono.from(wrapped.createEncryptedCollection(
86+
syncMongoDatabase.wrapped,
87+
collectionName,
88+
createCollectionOptions,
89+
createEncryptedCollectionParams
90+
)).block(TIMEOUT_DURATION))
91+
case _ => throw new AssertionError(s"Unexpected database type: ${database.getClass}")
92+
}
93+
}
94+
95+
override def close(): Unit = {
96+
wrapped.close()
97+
}
98+
}

driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoDatabase.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
170170
}
171171

172172
override def createView(viewName: String, viewOn: String, pipeline: java.util.List[_ <: Bson]): Unit = {
173-
throw new UnsupportedOperationException
173+
wrapped.createView(viewName, viewOn, pipeline.asScala.toList).toFuture().get()
174174
}
175175

176176
override def createView(
@@ -179,7 +179,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
179179
pipeline: java.util.List[_ <: Bson],
180180
createViewOptions: CreateViewOptions
181181
): Unit = {
182-
throw new UnsupportedOperationException
182+
wrapped.createView(viewName, viewOn, pipeline.asScala.toList, createViewOptions).toFuture().get()
183183
}
184184

185185
override def createView(
@@ -188,7 +188,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
188188
viewOn: String,
189189
pipeline: java.util.List[_ <: Bson]
190190
): Unit = {
191-
throw new UnsupportedOperationException
191+
wrapped.createView(unwrap(clientSession), viewName, viewOn, pipeline.asScala.toList).toFuture().get()
192192
}
193193

194194
override def createView(
@@ -198,7 +198,13 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase {
198198
pipeline: java.util.List[_ <: Bson],
199199
createViewOptions: CreateViewOptions
200200
): Unit = {
201-
throw new UnsupportedOperationException
201+
wrapped.createView(
202+
unwrap(clientSession),
203+
viewName,
204+
viewOn,
205+
pipeline.asScala.toList,
206+
createViewOptions
207+
).toFuture().get()
202208
}
203209

204210
override def watch = new SyncChangeStreamIterable[Document](wrapped.watch[Document]())
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.mongodb.scala.unified
18+
19+
object ClientEncryptionTest extends UnifiedTest {
20+
val directory = "client-side-encryption/tests/unified"
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.mongodb.scala.unified
18+
19+
object UnifiedCrudTest extends UnifiedTest {
20+
val directory = "crud"
21+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.mongodb.scala.unified
18+
19+
import com.mongodb.client.gridfs.{ GridFSBucket => JGridFSBucket }
20+
import com.mongodb.client.unified.UnifiedTest.Language
21+
import com.mongodb.client.unified.{ UnifiedTest, UnifiedTest => JUnifiedTest }
22+
import com.mongodb.client.vault.{ ClientEncryption => JClientEncryption }
23+
import com.mongodb.client.{ MongoClient => JMongoClient, MongoDatabase => JMongoDatabase }
24+
import com.mongodb.reactivestreams.client.internal.vault.ClientEncryptionImpl
25+
import com.mongodb.{ ClientEncryptionSettings => JClientEncryptionSettings, MongoClientSettings }
26+
import org.junit.jupiter.api.TestInstance
27+
import org.junit.jupiter.api.TestInstance.Lifecycle
28+
import org.junit.jupiter.params.provider.Arguments
29+
import org.mongodb.scala.MongoClient
30+
import org.mongodb.scala.MongoClient.DEFAULT_CODEC_REGISTRY
31+
import org.mongodb.scala.syncadapter.{ SyncClientEncryption, SyncMongoClient }
32+
import org.mongodb.scala.vault.ClientEncryption
33+
34+
import java.util
35+
36+
@TestInstance(Lifecycle.PER_CLASS)
37+
abstract class UnifiedTest extends JUnifiedTest {
38+
39+
val directory: String
40+
41+
def data(): util.Collection[Arguments] = JUnifiedTest.getTestData(directory, true, Language.SCALA)
42+
43+
override def createMongoClient(settings: MongoClientSettings): JMongoClient =
44+
SyncMongoClient(MongoClient(MongoClientSettings.builder(settings).codecRegistry(DEFAULT_CODEC_REGISTRY).build()))
45+
46+
override def createGridFSBucket(database: JMongoDatabase): JGridFSBucket =
47+
throw new NotImplementedError("Not implemented")
48+
49+
override def createClientEncryption(
50+
keyVaultClient: JMongoClient,
51+
clientEncryptionSettings: JClientEncryptionSettings
52+
): JClientEncryption = {
53+
keyVaultClient match {
54+
case client: SyncMongoClient =>
55+
SyncClientEncryption(ClientEncryption(new ClientEncryptionImpl(
56+
client.wrapped.wrapped,
57+
clientEncryptionSettings
58+
)))
59+
case _ => throw new IllegalArgumentException(s"Invalid keyVaultClient type: ${keyVaultClient.getClass}")
60+
}
61+
}
62+
63+
override protected def isReactive: Boolean = true
64+
65+
override protected def getLanguage: Language = Language.SCALA
66+
}

driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ object MongoClient {
111111
* @param wrapped the underlying java MongoClient
112112
* @since 1.0
113113
*/
114-
case class MongoClient(private val wrapped: JMongoClient) extends MongoCluster(wrapped) with Closeable {
114+
case class MongoClient(protected[scala] val wrapped: JMongoClient) extends MongoCluster(wrapped) with Closeable {
115115

116116
/**
117117
* Close the client, which will close all underlying cached resources, including, for example,

driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotCreateMongocryptdClientTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
import java.util.concurrent.TimeoutException;
4545
import java.util.stream.Stream;
4646

47-
import static com.mongodb.client.AbstractClientSideEncryptionTest.cryptSharedLibPathSysPropValue;
47+
import static com.mongodb.ClusterFixture.cryptSharedLibPathSysPropValue;
4848
import static com.mongodb.client.Fixture.getMongoClientSettings;
4949
import static com.mongodb.client.unified.UnifiedClientEncryptionHelper.localKmsProviderKey;
5050
import static java.lang.Math.toIntExact;

0 commit comments

Comments
 (0)