Skip to content

Commit 7953d0f

Browse files
committed
Implement expectEvents and change stream tests
1 parent e4ef88b commit 7953d0f

File tree

8 files changed

+275
-142
lines changed

8 files changed

+275
-142
lines changed

tests/UnifiedSpecTests/Constraint/IsBsonType.php

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use function array_keys;
2828
use function array_map;
2929
use function count;
30+
use function in_array;
3031
use function is_array;
3132
use function is_bool;
3233
use function is_float;
@@ -42,36 +43,36 @@ final class IsBsonType extends Constraint
4243
use ConstraintTrait;
4344

4445
/** @var array */
45-
private static $knownTypes = [
46-
'double' => 1,
47-
'string' => 1,
48-
'object' => 1,
49-
'array' => 1,
50-
'binData' => 1,
51-
'undefined' => 1,
52-
'objectId' => 1,
53-
'bool' => 1,
54-
'date' => 1,
55-
'null' => 1,
56-
'regex' => 1,
57-
'dbPointer' => 1,
58-
'javascript' => 1,
59-
'symbol' => 1,
60-
'javascriptWithScope' => 1,
61-
'int' => 1,
62-
'timestamp' => 1,
63-
'long' => 1,
64-
'decimal' => 1,
65-
'minKey' => 1,
66-
'maxKey' => 1,
46+
private static $types = [
47+
'double',
48+
'string',
49+
'object',
50+
'array',
51+
'binData',
52+
'undefined',
53+
'objectId',
54+
'bool',
55+
'date',
56+
'null',
57+
'regex',
58+
'dbPointer',
59+
'javascript',
60+
'symbol',
61+
'javascriptWithScope',
62+
'int',
63+
'timestamp',
64+
'long',
65+
'decimal',
66+
'minKey',
67+
'maxKey',
6768
];
6869

6970
/** @var string */
7071
private $type;
7172

7273
public function __construct(string $type)
7374
{
74-
if (! isset(self::$knownTypes[$type])) {
75+
if (! in_array($type, self::$types)) {
7576
throw new RuntimeException(sprintf('Type specified for %s <%s> is not a valid type', self::class, $type));
7677
}
7778

@@ -80,7 +81,7 @@ public function __construct(string $type)
8081

8182
public static function any() : LogicalOr
8283
{
83-
return self::anyOf(...array_keys(self::$knownTypes));
84+
return self::anyOf(...self::$types);
8485
}
8586

8687
public static function anyOf(string ...$types) : Constraint

tests/UnifiedSpecTests/Constraint/Matches.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
use function hex2bin;
2828
use function implode;
2929
use function is_array;
30+
use function is_float;
31+
use function is_int;
3032
use function is_object;
3133
use function isInstanceOf;
3234
use function isType;
@@ -113,8 +115,10 @@ private function assertEquals($expected, $actual, string $keyPath)
113115
$expectedType = is_object($expected) ? get_class($expected) : gettype($expected);
114116
$actualType = is_object($actual) ? get_class($actual) : gettype($actual);
115117

116-
// Workaround for ObjectComparator printing the whole actual object
117-
if ($expectedType !== $actualType) {
118+
/* Early check to work around ObjectComparator printing the entire value
119+
* for a failed type comparison. Avoid doing this if either value is
120+
* numeric to allow for flexible numeric comparisons (e.g. 1 == 1.0). */
121+
if ($expectedType !== $actualType && ! (self::isNumeric($expected) || self::isNumeric($actual))) {
118122
self::failAt(sprintf('%s is not expected type "%s"', $actualType, $expectedType), $keyPath);
119123
}
120124

@@ -363,6 +367,11 @@ private static function getOperatorName(BSONDocument $document) : string
363367
throw new LogicException('should not reach this point');
364368
}
365369

370+
private static function isNumeric($value)
371+
{
372+
return is_int($value) || is_float($value) || $value instanceof Int64;
373+
}
374+
366375
private static function isOperator(BSONDocument $document) : bool
367376
{
368377
if (count($document) !== 1) {
@@ -400,6 +409,11 @@ private static function prepare($bson)
400409
return (int) ((string) $bson);
401410
}
402411

412+
/* TODO: Convert Int64 objects to integers on 32-bit platforms if they
413+
* can be expressed as such. This is necessary to handle flexible
414+
* numeric comparisons if the server returns 32-bit value as a 64-bit
415+
* integer (e.g. cursor ID). */
416+
403417
// Serializable can produce an array or object, so recurse on its output
404418
if ($bson instanceof Serializable) {
405419
return self::prepare($bson->bsonSerialize());

tests/UnifiedSpecTests/Context.php

Lines changed: 29 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,22 @@
77
use MongoDB\Collection;
88
use MongoDB\Database;
99
use MongoDB\Driver\Manager;
10-
use MongoDB\Driver\ReadConcern;
1110
use MongoDB\Driver\ReadPreference;
1211
use MongoDB\Driver\Server;
13-
use MongoDB\Driver\WriteConcern;
1412
use stdClass;
15-
use function array_diff_key;
16-
use function array_fill_keys;
1713
use function array_key_exists;
18-
use function array_keys;
19-
use function assertContains;
14+
use function assertArrayHasKey;
2015
use function assertCount;
21-
use function assertEmpty;
2216
use function assertInstanceOf;
2317
use function assertInternalType;
18+
use function assertNotEmpty;
2419
use function assertNotFalse;
2520
use function assertStringStartsWith;
26-
use function assertThat;
2721
use function count;
2822
use function current;
2923
use function explode;
3024
use function implode;
31-
use function isType;
3225
use function key;
33-
use function logicalOr;
3426
use function parse_url;
3527
use function strlen;
3628
use function strpos;
@@ -102,76 +94,33 @@ public function createEntities(array $entities)
10294
}
10395
}
10496

105-
public static function createReadConcern(stdClass $o) : ReadConcern
97+
public function getEntityMap() : EntityMap
10698
{
107-
self::assertHasOnlyKeys($o, ['level']);
108-
109-
$level = $o->level ?? null;
110-
assertInternalType('string', $level);
111-
112-
return new ReadConcern($level);
99+
return $this->entityMap;
113100
}
114101

115-
public static function createReadPreference(stdClass $o) : ReadPreference
102+
public function getInternalClient() : Client
116103
{
117-
self::assertHasOnlyKeys($o, ['mode', 'tagSets', 'maxStalenessSeconds', 'hedge']);
118-
119-
$mode = $o->mode ?? null;
120-
$tagSets = $o->tagSets ?? null;
121-
$maxStalenessSeconds = $o->maxStalenessSeconds ?? null;
122-
$hedge = $o->hedge ?? null;
123-
124-
assertInternalType('string', $mode);
125-
126-
if (isset($tagSets)) {
127-
assertInternalType('array', $tagSets);
128-
assertContains('object', $tagSets);
129-
}
130-
131-
$options = [];
132-
133-
if (isset($maxStalenessSeconds)) {
134-
assertInternalType('int', $maxStalenessSeconds);
135-
$options['maxStalenessSeconds'] = $maxStalenessSeconds;
136-
}
137-
138-
if (isset($hedge)) {
139-
assertInternalType('object', $hedge);
140-
$options['hedge'] = $hedge;
141-
}
142-
143-
return new ReadPreference($mode, $tagSets, $options);
104+
return $this->internalClient;
144105
}
145106

146-
public static function createWriteConcern(stdClass $o) : WriteConcern
107+
public function assertExpectedEventsForClients(array $expectedEventsForClients)
147108
{
148-
self::assertHasOnlyKeys($o, ['w', 'wtimeoutMS', 'journal']);
109+
assertNotEmpty($expectedEventsForClients);
149110

150-
$w = $o->w ?? -2; /* MONGOC_WRITE_CONCERN_W_DEFAULT */
151-
$wtimeoutMS = $o->wtimeoutMS ?? 0;
152-
$journal = $o->journal ?? null;
111+
foreach ($expectedEventsForClients as $expectedEventsForClient) {
112+
assertInternalType('object', $expectedEventsForClient);
113+
Util::assertHasOnlyKeys($expectedEventsForClient, ['client', 'events']);
153114

154-
assertThat($w, logicalOr(isType('int'), isType('string')));
155-
assertInternalType('int', $wtimeoutMS);
115+
$client = $expectedEventsForClient->client ?? null;
116+
$expectedEvents = $expectedEventsForClient->events ?? null;
156117

157-
$args = [$w, $wtimeoutMS];
118+
assertInternalType('string', $client);
119+
assertArrayHasKey($client, $this->eventObserversByClient);
120+
assertInternalType('array', $expectedEvents);
158121

159-
if (isset($journal)) {
160-
assertInternalType('bool', $journal);
161-
$args[] = $journal;
122+
$this->eventObserversByClient[$client]->assert($expectedEvents);
162123
}
163-
164-
return new WriteConcern(...$args);
165-
}
166-
167-
public function getEntityMap() : EntityMap
168-
{
169-
return $this->entityMap;
170-
}
171-
172-
public function getInternalClient() : Client
173-
{
174-
return $this->internalClient;
175124
}
176125

177126
public function startEventObservers()
@@ -188,16 +137,16 @@ public function stopEventObservers()
188137
}
189138
}
190139

191-
private static function assertHasOnlyKeys($arrayOrObject, array $keys)
140+
public function getEventObserverForClient(string $id) : EventObserver
192141
{
193-
assertThat($arrayOrObject, logicalOr(isType('array'), isType('object')));
194-
$diff = array_diff_key((array) $arrayOrObject, array_fill_keys($keys, 1));
195-
assertEmpty($diff, 'Unsupported keys: ' . implode(',', array_keys($diff)));
142+
assertArrayHasKey($id, $this->eventObserversByClient);
143+
144+
return $this->eventObserversByClient[$id];
196145
}
197146

198147
private function createClient(stdClass $o) : Client
199148
{
200-
self::assertHasOnlyKeys($o, ['id', 'uriOptions', 'useMultipleMongoses', 'observeEvents', 'ignoreCommandMonitoringEvents']);
149+
Util::assertHasOnlyKeys($o, ['id', 'uriOptions', 'useMultipleMongoses', 'observeEvents', 'ignoreCommandMonitoringEvents']);
201150

202151
$useMultipleMongoses = $o->useMultipleMongoses ?? null;
203152
$observeEvents = $o->observeEvents ?? null;
@@ -229,15 +178,15 @@ private function createClient(stdClass $o) : Client
229178
assertInternalType('array', $observeEvents);
230179
assertInternalType('array', $ignoreCommandMonitoringEvents);
231180

232-
$this->eventObserversByClient[$o->id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents);
181+
$this->eventObserversByClient[$o->id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents, $o->id, $this->entityMap);
233182
}
234183

235184
return new Client($uri, $uriOptions);
236185
}
237186

238187
private function createCollection(stdClass $o) : Collection
239188
{
240-
self::assertHasOnlyKeys($o, ['id', 'database', 'collectionName', 'collectionOptions']);
189+
Util::assertHasOnlyKeys($o, ['id', 'database', 'collectionName', 'collectionOptions']);
241190

242191
$collectionName = $o->collectionName ?? null;
243192
$database = $o->database ?? null;
@@ -260,7 +209,7 @@ private function createCollection(stdClass $o) : Collection
260209

261210
private function createDatabase(stdClass $o) : Database
262211
{
263-
self::assertHasOnlyKeys($o, ['id', 'client', 'databaseName', 'databaseOptions']);
212+
Util::assertHasOnlyKeys($o, ['id', 'client', 'databaseName', 'databaseOptions']);
264213

265214
$databaseName = $o->databaseName ?? null;
266215
$client = $o->client ?? null;
@@ -283,21 +232,21 @@ private function createDatabase(stdClass $o) : Database
283232

284233
private static function prepareCollectionOrDatabaseOptions(array $options) : array
285234
{
286-
self::assertHasOnlyKeys($options, ['readConcern', 'readPreference', 'writeConcern']);
235+
Util::assertHasOnlyKeys($options, ['readConcern', 'readPreference', 'writeConcern']);
287236

288237
if (array_key_exists('readConcern', $options)) {
289238
assertInternalType('object', $options['readConcern']);
290-
$options['readConcern'] = self::createReadConcern($options['readConcern']);
239+
$options['readConcern'] = Util::createReadConcern($options['readConcern']);
291240
}
292241

293242
if (array_key_exists('readPreference', $options)) {
294243
assertInternalType('object', $options['readPreference']);
295-
$options['readPreference'] = self::createReadPreference($options['readPreference']);
244+
$options['readPreference'] = Util::createReadPreference($options['readPreference']);
296245
}
297246

298247
if (array_key_exists('writeConcern', $options)) {
299248
assertInternalType('object', $options['writeConcern']);
300-
$options['writeConcern'] = self::createWriteConcern($options['writeConcern']);
249+
$options['writeConcern'] = Util::createWriteConcern($options['writeConcern']);
301250
}
302251

303252
return $options;

0 commit comments

Comments
 (0)