Skip to content

Commit 263151a

Browse files
committed
Implement session tests
1 parent 69bdcf4 commit 263151a

File tree

7 files changed

+240
-61
lines changed

7 files changed

+240
-61
lines changed

tests/UnifiedSpecTests/Constraint/Matches.php

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use LogicException;
66
use MongoDB\BSON\Serializable;
77
use MongoDB\BSON\Type;
8-
use MongoDB\Driver\Session;
98
use MongoDB\Model\BSONArray;
109
use MongoDB\Model\BSONDocument;
1110
use MongoDB\Tests\UnifiedSpecTests\EntityMap;
@@ -16,7 +15,6 @@
1615
use SebastianBergmann\Comparator\Factory;
1716
use Symfony\Bridge\PhpUnit\ConstraintTrait;
1817
use function array_keys;
19-
use function assertInstanceOf;
2018
use function assertInternalType;
2119
use function assertRegExp;
2220
use function assertThat;
@@ -300,16 +298,9 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $
300298

301299
if ($name === '$$sessionLsid') {
302300
assertInternalType('string', $operator['$$sessionLsid'], '$$sessionLsid requires string');
301+
$lsid = $this->entityMap->getLogicalSessionId($operator['$$sessionLsid']);
303302

304-
$session = $this->entityMap[$operator['$$sessionLsid']];
305-
306-
assertInstanceOf(Session::class, $session, '$$sessionLsid requires session entity');
307-
308-
$this->assertEquals(
309-
self::prepare($session->getLogicalSessionId()),
310-
$actual,
311-
$keyPath
312-
);
303+
$this->assertEquals(self::prepare($lsid), $actual, $keyPath);
313304

314305
return;
315306
}

tests/UnifiedSpecTests/Context.php

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44

55
use LogicException;
66
use MongoDB\Client;
7-
use MongoDB\Database;
87
use MongoDB\Driver\Manager;
98
use MongoDB\Driver\ReadPreference;
109
use MongoDB\Driver\Server;
1110
use stdClass;
1211
use function array_key_exists;
1312
use function assertArrayHasKey;
1413
use function assertCount;
15-
use function assertInstanceOf;
1614
use function assertInternalType;
1715
use function assertNotEmpty;
1816
use function assertNotFalse;
@@ -21,6 +19,7 @@
2119
use function current;
2220
use function explode;
2321
use function implode;
22+
use function in_array;
2423
use function key;
2524
use function parse_url;
2625
use function strlen;
@@ -36,6 +35,12 @@
3635
*/
3736
final class Context
3837
{
38+
/** @var string */
39+
private $activeClient;
40+
41+
/** @var string[] */
42+
private $dirtySessions = [];
43+
3944
/** @var EntityMap */
4045
private $entityMap;
4146

@@ -111,6 +116,20 @@ public function getInternalClient() : Client
111116
return $this->internalClient;
112117
}
113118

119+
public function isDirtySession(string $sessionId) : bool
120+
{
121+
return in_array($sessionId, $this->dirtySessions);
122+
}
123+
124+
public function markDirtySession(string $sessionId)
125+
{
126+
if ($this->isDirtySession($sessionId)) {
127+
return;
128+
}
129+
130+
$this->dirtySessions[] = $sessionId;
131+
}
132+
114133
public function isActiveClient(string $clientId) : bool
115134
{
116135
return $this->activeClient === $clientId;
@@ -198,7 +217,12 @@ private function createClient(string $id, stdClass $o)
198217
$this->eventObserversByClient[$id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents, $id, $this);
199218
}
200219

201-
$this->entityMap->set($id, new Client($uri, $uriOptions));
220+
/* TODO: Remove this once PHPC-1645 is implemented. Each client needs
221+
* its own libmongoc client to facilitate txnNumber assertions. */
222+
static $i = 0;
223+
$driverOptions = isset($observeEvents) ? ['i' => $i++] : [];
224+
225+
$this->entityMap->set($id, new Client($uri, $uriOptions, $driverOptions));
202226
}
203227

204228
private function createCollection(string $id, stdClass $o)
@@ -211,8 +235,7 @@ private function createCollection(string $id, stdClass $o)
211235
assertInternalType('string', $collectionName);
212236
assertInternalType('string', $databaseId);
213237

214-
$database = $this->entityMap[$databaseId];
215-
assertInstanceOf(Database::class, $database);
238+
$database = $this->entityMap->getDatabase($databaseId);
216239

217240
$options = [];
218241

@@ -234,8 +257,7 @@ private function createDatabase(string $id, stdClass $o)
234257
assertInternalType('string', $databaseName);
235258
assertInternalType('string', $clientId);
236259

237-
$client = $this->entityMap[$clientId];
238-
assertInstanceOf(Client::class, $client);
260+
$client = $this->entityMap->getClient($clientId);
239261

240262
$options = [];
241263

@@ -253,8 +275,7 @@ private function createSession(string $id, stdClass $o)
253275

254276
$clientId = $o->client ?? null;
255277
assertInternalType('string', $clientId);
256-
$client = $this->entityMap[$clientId];
257-
assertInstanceOf(Client::class, $client);
278+
$client = $this->entityMap->getClient($clientId);
258279

259280
$options = [];
260281

@@ -272,8 +293,7 @@ private function createBucket(string $id, stdClass $o)
272293

273294
$databaseId = $o->database ?? null;
274295
assertInternalType('string', $databaseId);
275-
$database = $this->entityMap[$databaseId];
276-
assertInstanceOf(Database::class, $database);
296+
$database = $this->entityMap->getDatabase($databaseId);
277297

278298
$options = [];
279299

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace MongoDB\Tests\UnifiedSpecTests;
4+
5+
use MongoDB\Driver\Exception\ConnectionException;
6+
use MongoDB\Driver\Monitoring\CommandFailedEvent;
7+
use MongoDB\Driver\Monitoring\CommandStartedEvent;
8+
use MongoDB\Driver\Monitoring\CommandSubscriber;
9+
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
10+
use stdClass;
11+
use function in_array;
12+
use function MongoDB\Driver\Monitoring\addSubscriber;
13+
use function MongoDB\Driver\Monitoring\removeSubscriber;
14+
15+
/**
16+
* Observes whether a session is used in an command that encounters a network
17+
* error. This is primarily used to infer whether a sesson will be marked dirty
18+
* in libmongoc.
19+
*
20+
* TODO: Remove this once CDRIVER-3780 and PHPLIB-528 are implemented.
21+
*/
22+
class DirtySessionObserver implements CommandSubscriber
23+
{
24+
/** @var stdClass */
25+
private $lsid;
26+
27+
/** @var array */
28+
private $requestIds = [];
29+
30+
private $observedNetworkError = false;
31+
32+
public function __construct(stdClass $lsid)
33+
{
34+
$this->lsid = $lsid;
35+
}
36+
37+
/**
38+
* @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php
39+
*/
40+
public function commandFailed(CommandFailedEvent $event)
41+
{
42+
if (! in_array($event->getRequestId(), $this->requestIds)) {
43+
return;
44+
}
45+
46+
if ($event->getError() instanceof ConnectionException) {
47+
$this->observedNetworkError = true;
48+
}
49+
}
50+
51+
/**
52+
* @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php
53+
*/
54+
public function commandStarted(CommandStartedEvent $event)
55+
{
56+
if ($this->lsid == ($event->getCommand()->lsid ?? null)) {
57+
$this->requestIds[] = $event->getRequestId();
58+
}
59+
}
60+
61+
/**
62+
* @see https://www.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php
63+
*/
64+
public function commandSucceeded(CommandSucceededEvent $event)
65+
{
66+
}
67+
68+
public function observedNetworkError() : bool
69+
{
70+
return $this->observedNetworkError;
71+
}
72+
73+
public function start()
74+
{
75+
addSubscriber($this);
76+
}
77+
78+
public function stop()
79+
{
80+
removeSubscriber($this);
81+
}
82+
}

tests/UnifiedSpecTests/EntityMap.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use MongoDB\Tests\UnifiedSpecTests\Constraint\IsBsonType;
1313
use PHPUnit\Framework\Assert;
1414
use PHPUnit\Framework\Constraint\Constraint;
15+
use stdClass;
1516
use function array_key_exists;
1617
use function assertArrayHasKey;
1718
use function assertArrayNotHasKey;
@@ -26,6 +27,14 @@ class EntityMap implements ArrayAccess
2627
/** @var array */
2728
private $map = [];
2829

30+
/**
31+
* Track lsids so they can be accessed after Session::getLogicalSessionId()
32+
* has been called.
33+
*
34+
* @var stdClass[]
35+
*/
36+
private $lsidsBySession = [];
37+
2938
/** @var Constraint */
3039
private static $isSupportedType;
3140

@@ -82,6 +91,10 @@ public function set(string $id, $value, string $parentId = null)
8291
assertArrayNotHasKey($id, $this->map, sprintf('Entity already exists for "%s" and cannot be replaced', $id));
8392
assertThat($value, self::isSupportedType());
8493

94+
if ($value instanceof Session) {
95+
$this->lsidsBySession[$id] = $value->getLogicalSessionId();
96+
}
97+
8598
$parent = $parentId === null ? null : $this->map[$parentId];
8699

87100
$this->map[$id] = new class ($id, $value, $parent) {
@@ -112,6 +125,31 @@ public function getRoot() : self
112125
};
113126
}
114127

128+
public function getClient(string $clientId) : Client
129+
{
130+
return $this[$clientId];
131+
}
132+
133+
public function getCollection(string $collectionId) : Collection
134+
{
135+
return $this[$collectionId];
136+
}
137+
138+
public function getDatabase(string $databaseId) : Database
139+
{
140+
return $this[$databaseId];
141+
}
142+
143+
public function getSession(string $sessionId) : Session
144+
{
145+
return $this[$sessionId];
146+
}
147+
148+
public function getLogicalSessionId(string $sessionId) : stdClass
149+
{
150+
return $this->lsidsBySession[$sessionId];
151+
}
152+
115153
public function getRootClientIdOf(string $id) : ?string
116154
{
117155
$root = $this->map[$id]->getRoot();

tests/UnifiedSpecTests/EventObserver.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
use PHPUnit\Framework\Assert;
1313
use stdClass;
1414
use function array_fill_keys;
15+
use function array_reverse;
1516
use function assertArrayHasKey;
1617
use function assertCount;
1718
use function assertInstanceOf;
1819
use function assertInternalType;
1920
use function assertNotEmpty;
2021
use function assertNotNull;
22+
use function assertObjectHasAttribute;
2123
use function assertSame;
2224
use function assertThat;
2325
use function count;
@@ -150,11 +152,6 @@ public function commandSucceeded(CommandSucceededEvent $event)
150152
$this->actualEvents[] = $event;
151153
}
152154

153-
public function getActualEvents()
154-
{
155-
return $this->actualEvents;
156-
}
157-
158155
public function start()
159156
{
160157
addSubscriber($this);
@@ -165,6 +162,27 @@ public function stop()
165162
removeSubscriber($this);
166163
}
167164

165+
public function getLsidsOnLastTwoCommands() : array
166+
{
167+
$lsids = [];
168+
169+
foreach (array_reverse($this->actualEvents) as $event) {
170+
if (! $event instanceof CommandStartedEvent) {
171+
continue;
172+
}
173+
174+
$command = $event->getCommand();
175+
assertObjectHasAttribute('lsid', $command);
176+
$lsids[] = $command->lsid;
177+
178+
if (count($lsids) === 2) {
179+
return $lsids;
180+
}
181+
}
182+
183+
Assert::fail('Not enough CommandStartedEvents observed');
184+
}
185+
168186
public function assert(array $expectedEvents)
169187
{
170188
assertCount(count($expectedEvents), $this->actualEvents);

0 commit comments

Comments
 (0)