Skip to content

Commit b1ffe89

Browse files
committed
PHPLIB-888: Use a dedicated clients for legacy spec tests
While some specs already required separate clients (e.g. transactions for txnNumber assertions), applying it generally will allow internal operations (e.g. assertCollectionExists) to use separate clients and also allow command monitoring to focus on actual test operations. This also refactors ClientSideEncryptionSpecTest::insertKeyVaultData and several CSFLE prose tests, since a client and its key vault client must share the disableClientPersistence driver option.
1 parent 1d7d5d5 commit b1ffe89

8 files changed

+73
-71
lines changed

tests/SpecTests/AtlasDataLakeSpecTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function testAtlasDataLake(stdClass $test, ?array $runOn, array $data, ?s
8383
}
8484

8585
if (isset($test->expectations)) {
86-
$commandExpectations = CommandExpectations::fromCrud((array) $test->expectations);
86+
$commandExpectations = CommandExpectations::fromCrud($context->getClient(), (array) $test->expectations);
8787
$commandExpectations->startMonitoring();
8888
}
8989

tests/SpecTests/ClientSideEncryptionSpecTest.php

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public function testClientSideEncryption(stdClass $test, ?array $runOn, array $d
115115

116116
$this->setContext($context);
117117

118-
$this->insertKeyVaultData($keyVaultData);
118+
self::insertKeyVaultData($context->getClient(), $keyVaultData);
119119
$this->dropTestAndOutcomeCollections();
120120
$this->createTestCollection($jsonSchema);
121121
$this->insertDataFixtures($data);
@@ -127,7 +127,7 @@ public function testClientSideEncryption(stdClass $test, ?array $runOn, array $d
127127
$context->enableEncryption();
128128

129129
if (isset($test->expectations)) {
130-
$commandExpectations = CommandExpectations::fromClientSideEncryption($test->expectations);
130+
$commandExpectations = CommandExpectations::fromClientSideEncryption($context->getClient(), $test->expectations);
131131
$commandExpectations->startMonitoring();
132132
}
133133

@@ -192,14 +192,12 @@ public function provideTests()
192192
*/
193193
public function testDataKeyAndDoubleEncryption(string $providerName, $masterKey): void
194194
{
195-
$this->setContext(Context::fromClientSideEncryption((object) [], 'db', 'coll'));
196-
$client = $this->getContext()->getClient();
197-
198-
// This empty call ensures that the key vault is dropped with a majority
199-
// write concern
200-
$this->insertKeyVaultData([]);
195+
$client = static::createTestClient();
201196
$client->selectCollection('db', 'coll')->drop();
202197

198+
// Ensure that the key vault is dropped with a majority write concern
199+
self::insertKeyVaultData($client, []);
200+
203201
$encryptionOpts = [
204202
'keyVaultNamespace' => 'keyvault.datakeys',
205203
'kmsProviders' => [
@@ -332,19 +330,12 @@ public static function dataKeyProvider()
332330
*/
333331
public function testExternalKeyVault($withExternalKeyVault): void
334332
{
335-
$this->setContext(Context::fromClientSideEncryption((object) [], 'db', 'coll'));
336-
$client = $this->getContext()->getClient();
333+
$client = static::createTestClient();
337334
$client->selectCollection('db', 'coll')->drop();
338335

339-
$keyVaultCollection = $client->selectCollection(
340-
'keyvault',
341-
'datakeys',
342-
['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] + $this->getContext()->defaultWriteOptions
343-
);
344-
$keyVaultCollection->drop();
345-
$keyId = $keyVaultCollection
346-
->insertOne($this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-key.json')))
347-
->getInsertedId();
336+
self::insertKeyVaultData($client, [
337+
$this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-key.json')),
338+
]);
348339

349340
$encryptionOpts = [
350341
'keyVaultNamespace' => 'keyvault.datakeys',
@@ -386,7 +377,10 @@ public function testExternalKeyVault($withExternalKeyVault): void
386377
$this->expectException(AuthenticationException::class);
387378
}
388379

389-
$clientEncryption->encrypt('test', ['algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, 'keyId' => $keyId]);
380+
$clientEncryption->encrypt('test', [
381+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
382+
'keyId' => new Binary(base64_decode('LOCALAAAAAAAAAAAAAAAAA=='), Binary::TYPE_UUID),
383+
]);
390384
}
391385

392386
public static function provideBSONSizeLimitsAndBatchSplittingTests()
@@ -483,13 +477,12 @@ static function (self $test, Collection $collection, array $document): void {
483477
*/
484478
public function testBSONSizeLimitsAndBatchSplitting(Closure $test): void
485479
{
486-
$this->setContext(Context::fromClientSideEncryption((object) [], 'db', 'coll'));
487-
$client = $this->getContext()->getClient();
480+
$client = static::createTestClient();
488481

489482
$client->selectCollection('db', 'coll')->drop();
490483
$client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-schema.json'))]]);
491484

492-
$this->insertKeyVaultData([
485+
self::insertKeyVaultData($client, [
493486
$this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-key.json')),
494487
]);
495488

@@ -551,20 +544,16 @@ public function testViewsAreProhibited(): void
551544
*/
552545
public function testCorpus($schemaMap = true): void
553546
{
554-
$this->setContext(Context::fromClientSideEncryption((object) [], 'db', 'coll'));
555-
$client = $this->getContext()->getClient();
556-
547+
$client = static::createTestClient();
557548
$client->selectDatabase('db')->dropCollection('coll');
558549

559550
$schema = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-schema.json'));
560551

561552
if (! $schemaMap) {
562-
$client
563-
->selectDatabase('db')
564-
->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]);
553+
$client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]);
565554
}
566555

567-
$this->insertKeyVaultData([
556+
self::insertKeyVaultData($client, [
568557
$this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-local.json')),
569558
$this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-aws.json')),
570559
$this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-azure.json')),
@@ -1432,10 +1421,9 @@ private static function getEnv(string $name): string
14321421
return $value;
14331422
}
14341423

1435-
private function insertKeyVaultData(?array $keyVaultData = null): void
1424+
private static function insertKeyVaultData(Client $client, ?array $keyVaultData = null): void
14361425
{
1437-
$context = $this->getContext();
1438-
$collection = $context->selectCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] + $context->defaultWriteOptions);
1426+
$collection = $client->selectCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]);
14391427
$collection->drop();
14401428

14411429
if (empty($keyVaultData)) {

tests/SpecTests/CommandExpectations.php

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use ArrayIterator;
66
use LogicException;
7+
use MongoDB\Client;
78
use MongoDB\Driver\Monitoring\CommandFailedEvent;
89
use MongoDB\Driver\Monitoring\CommandStartedEvent;
910
use MongoDB\Driver\Monitoring\CommandSubscriber;
@@ -13,8 +14,6 @@
1314
use function count;
1415
use function in_array;
1516
use function key;
16-
use function MongoDB\Driver\Monitoring\addSubscriber;
17-
use function MongoDB\Driver\Monitoring\removeSubscriber;
1817

1918
/**
2019
* Spec test CommandStartedEvent expectations.
@@ -45,8 +44,13 @@ class CommandExpectations implements CommandSubscriber
4544
/** @var string[] */
4645
private $ignoredCommandNames = [];
4746

48-
private function __construct(array $events)
47+
/** @var Client */
48+
private $observedClient;
49+
50+
private function __construct(Client $observedClient, array $events)
4951
{
52+
$this->observedClient = $observedClient;
53+
5054
foreach ($events as $event) {
5155
switch (key((array) $event)) {
5256
case 'command_failed_event':
@@ -70,9 +74,9 @@ private function __construct(array $events)
7074
}
7175
}
7276

73-
public static function fromClientSideEncryption(array $expectedEvents)
77+
public static function fromClientSideEncryption(Client $client, array $expectedEvents)
7478
{
75-
$o = new self($expectedEvents);
79+
$o = new self($client, $expectedEvents);
7680

7781
$o->ignoreCommandFailed = true;
7882
$o->ignoreCommandSucceeded = true;
@@ -81,29 +85,29 @@ public static function fromClientSideEncryption(array $expectedEvents)
8185
return $o;
8286
}
8387

84-
public static function fromCrud(array $expectedEvents)
88+
public static function fromCrud(Client $client, array $expectedEvents)
8589
{
86-
$o = new self($expectedEvents);
90+
$o = new self($client, $expectedEvents);
8791

8892
$o->ignoreCommandFailed = true;
8993
$o->ignoreCommandSucceeded = true;
9094

9195
return $o;
9296
}
9397

94-
public static function fromReadWriteConcern(array $expectedEvents)
98+
public static function fromReadWriteConcern(Client $client, array $expectedEvents)
9599
{
96-
$o = new self($expectedEvents);
100+
$o = new self($client, $expectedEvents);
97101

98102
$o->ignoreCommandFailed = true;
99103
$o->ignoreCommandSucceeded = true;
100104

101105
return $o;
102106
}
103107

104-
public static function fromRetryableReads(array $expectedEvents)
108+
public static function fromRetryableReads(Client $client, array $expectedEvents)
105109
{
106-
$o = new self($expectedEvents);
110+
$o = new self($client, $expectedEvents);
107111

108112
$o->ignoreCommandFailed = true;
109113
$o->ignoreCommandSucceeded = true;
@@ -116,9 +120,9 @@ public static function fromRetryableReads(array $expectedEvents)
116120
return $o;
117121
}
118122

119-
public static function fromTransactions(array $expectedEvents)
123+
public static function fromTransactions(Client $client, array $expectedEvents)
120124
{
121-
$o = new self($expectedEvents);
125+
$o = new self($client, $expectedEvents);
122126

123127
$o->ignoreCommandFailed = true;
124128
$o->ignoreCommandSucceeded = true;
@@ -181,15 +185,15 @@ public function commandSucceeded(CommandSucceededEvent $event): void
181185
*/
182186
public function startMonitoring(): void
183187
{
184-
addSubscriber($this);
188+
$this->observedClient->getManager()->addSubscriber($this);
185189
}
186190

187191
/**
188192
* Stop command monitoring.
189193
*/
190194
public function stopMonitoring(): void
191195
{
192-
removeSubscriber($this);
196+
$this->observedClient->getManager()->removeSubscriber($this);
193197
}
194198

195199
/**

tests/SpecTests/Context.php

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use function array_keys;
1616
use function getenv;
1717
use function implode;
18-
use function uniqid;
1918

2019
/**
2120
* Execution context for spec tests.
@@ -58,6 +57,9 @@ final class Context
5857
/** @var object */
5958
public $session1Lsid;
6059

60+
/** @var Client */
61+
private $internalClient;
62+
6163
/** @var Client|null */
6264
private $encryptedClient;
6365

@@ -69,6 +71,7 @@ private function __construct(string $databaseName, ?string $collectionName)
6971
$this->databaseName = $databaseName;
7072
$this->collectionName = $collectionName;
7173
$this->outcomeCollectionName = $collectionName;
74+
$this->internalClient = FunctionalTestCase::createTestClient();
7275
}
7376

7477
public function disableEncryption(): void
@@ -100,11 +103,6 @@ public static function fromClientSideEncryption(stdClass $test, $databaseName, $
100103

101104
$clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : [];
102105

103-
/* mongocryptd caches collection information, which causes test failures
104-
* if we reuse the client. Thus, we add a random value to ensure we're
105-
* creating a new client for each test. */
106-
$driverOptions = ['random' => uniqid()];
107-
108106
$autoEncryptionOptions = [];
109107

110108
if (isset($clientOptions['autoEncryptOpts'])) {
@@ -138,10 +136,10 @@ public static function fromClientSideEncryption(stdClass $test, $databaseName, $
138136
$o->outcomeCollectionName = $test->outcome->collection->name;
139137
}
140138

141-
$o->client = FunctionalTestCase::createTestClient(null, $clientOptions, $driverOptions);
139+
$o->client = self::createTestClient(null, $clientOptions);
142140

143141
if ($autoEncryptionOptions !== []) {
144-
$o->encryptedClient = FunctionalTestCase::createTestClient(null, $clientOptions, $driverOptions + ['autoEncryption' => $autoEncryptionOptions]);
142+
$o->encryptedClient = self::createTestClient(null, $clientOptions, ['autoEncryption' => $autoEncryptionOptions]);
145143
}
146144

147145
return $o;
@@ -175,7 +173,7 @@ public static function fromCrud(stdClass $test, $databaseName, $collectionName)
175173
'readPreference' => new ReadPreference('primary'),
176174
];
177175

178-
$o->client = FunctionalTestCase::createTestClient(null, $clientOptions);
176+
$o->client = self::createTestClient(null, $clientOptions);
179177

180178
return $o;
181179
}
@@ -190,7 +188,7 @@ public static function fromReadWriteConcern(stdClass $test, $databaseName, $coll
190188

191189
$clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : [];
192190

193-
$o->client = FunctionalTestCase::createTestClient(null, $clientOptions);
191+
$o->client = self::createTestClient(null, $clientOptions);
194192

195193
return $o;
196194
}
@@ -203,7 +201,7 @@ public static function fromRetryableReads(stdClass $test, $databaseName, $collec
203201

204202
$clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : [];
205203

206-
$o->client = FunctionalTestCase::createTestClient(null, $clientOptions);
204+
$o->client = self::createTestClient(null, $clientOptions);
207205

208206
return $o;
209207
}
@@ -218,7 +216,7 @@ public static function fromRetryableWrites(stdClass $test, $databaseName, $colle
218216
$o->outcomeCollectionName = $test->outcome->collection->name;
219217
}
220218

221-
$o->client = FunctionalTestCase::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions);
219+
$o->client = self::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions);
222220

223221
return $o;
224222
}
@@ -238,10 +236,7 @@ public static function fromTransactions(stdClass $test, $databaseName, $collecti
238236

239237
$clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : [];
240238

241-
// Transaction spec tests expect a new client for each test so that txnNumber values are deterministic.
242-
$driverOptions = ['disableClientPersistence' => true];
243-
244-
$o->client = FunctionalTestCase::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions, $driverOptions);
239+
$o->client = self::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions);
245240

246241
$session0Options = isset($test->sessionOptions->session0) ? (array) $test->sessionOptions->session0 : [];
247242
$session1Options = isset($test->sessionOptions->session1) ? (array) $test->sessionOptions->session1 : [];
@@ -338,6 +333,11 @@ public function getGridFSBucket(array $bucketOptions = [])
338333
return $this->selectGridFSBucket($this->databaseName, $this->bucketName, $bucketOptions);
339334
}
340335

336+
public function getInternalClient(): Client
337+
{
338+
return $this->internalClient;
339+
}
340+
341341
/**
342342
* Prepare options readConcern, readPreference, and writeConcern options by
343343
* creating value objects.
@@ -470,6 +470,16 @@ public function selectGridFSBucket($databaseName, $bucketName, array $bucketOpti
470470
return $this->selectDatabase($databaseName)->selectGridFSBucket($this->prepareGridFSBucketOptions($bucketOptions, $bucketName));
471471
}
472472

473+
private static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client
474+
{
475+
/* Default to using a dedicated client. This was already necessary for
476+
* CSFLE and Transaction spec tests, but is generally useful for any
477+
* test that observes command monitoring events. */
478+
$driverOptions += ['disableClientPersistence' => true];
479+
480+
return FunctionalTestCase::createTestClient($uri, $options, $driverOptions);
481+
}
482+
473483
private function prepareGridFSBucketOptions(array $options, $bucketPrefix)
474484
{
475485
if ($bucketPrefix !== null) {

tests/SpecTests/Operation.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -608,15 +608,15 @@ private function executeForTestRunner(FunctionalTestCase $test, Context $context
608608
$databaseName = $args['database'];
609609
$collectionName = $args['collection'];
610610

611-
$test->assertContains($collectionName, $context->selectDatabase($databaseName)->listCollectionNames());
611+
$test->assertContains($collectionName, $context->getInternalClient()->selectDatabase($databaseName)->listCollectionNames());
612612

613613
return null;
614614

615615
case 'assertCollectionNotExists':
616616
$databaseName = $args['database'];
617617
$collectionName = $args['collection'];
618618

619-
$test->assertNotContains($collectionName, $context->selectDatabase($databaseName)->listCollectionNames());
619+
$test->assertNotContains($collectionName, $context->getInternalClient()->selectDatabase($databaseName)->listCollectionNames());
620620

621621
return null;
622622

@@ -679,7 +679,7 @@ private function getIndexNames(Context $context, string $databaseName, string $c
679679
function (IndexInfo $indexInfo) {
680680
return $indexInfo->getName();
681681
},
682-
iterator_to_array($context->selectCollection($databaseName, $collectionName)->listIndexes())
682+
iterator_to_array($context->getInternalClient()->selectCollection($databaseName, $collectionName)->listIndexes())
683683
);
684684
}
685685

0 commit comments

Comments
 (0)