4
4
5
5
use MongoDB \ChangeStream ;
6
6
use MongoDB \BSON \TimestampInterface ;
7
+ use MongoDB \Driver \Cursor ;
7
8
use MongoDB \Driver \Manager ;
8
9
use MongoDB \Driver \ReadPreference ;
9
10
use MongoDB \Driver \Server ;
@@ -33,6 +34,98 @@ public function setUp()
33
34
$ this ->createCollection ();
34
35
}
35
36
37
+ /**
38
+ * Prose test: "ChangeStream must continuously track the last seen
39
+ * resumeToken"
40
+ */
41
+ public function testGetResumeToken ()
42
+ {
43
+ if ($ this ->isPostBatchResumeTokenSupported ()) {
44
+ $ this ->markTestSkipped ('postBatchResumeToken is supported ' );
45
+ }
46
+
47
+ $ operation = new Watch ($ this ->manager , $ this ->getDatabaseName (), $ this ->getCollectionName (), [], $ this ->defaultOptions );
48
+ $ changeStream = $ operation ->execute ($ this ->getPrimaryServer ());
49
+
50
+ $ changeStream ->rewind ();
51
+ $ this ->assertFalse ($ changeStream ->valid ());
52
+ $ this ->assertNull ($ changeStream ->getResumeToken ());
53
+
54
+ $ this ->insertDocument (['x ' => 1 ]);
55
+ $ this ->insertDocument (['x ' => 2 ]);
56
+
57
+ $ changeStream ->next ();
58
+ $ this ->assertSameDocument ($ changeStream ->current ()->_id , $ changeStream ->getResumeToken ());
59
+
60
+ $ changeStream ->next ();
61
+ $ this ->assertSameDocument ($ changeStream ->current ()->_id , $ changeStream ->getResumeToken ());
62
+
63
+ $ this ->insertDocument (['x ' => 3 ]);
64
+
65
+ $ changeStream ->next ();
66
+ $ this ->assertSameDocument ($ changeStream ->current ()->_id , $ changeStream ->getResumeToken ());
67
+ }
68
+
69
+ /**
70
+ * Prose test: "ChangeStream must continuously track the last seen
71
+ * resumeToken"
72
+ */
73
+ public function testGetResumeTokenWithPostBatchResumeToken ()
74
+ {
75
+ if ( ! $ this ->isPostBatchResumeTokenSupported ()) {
76
+ $ this ->markTestSkipped ('postBatchResumeToken is not supported ' );
77
+ }
78
+
79
+ $ operation = new Watch ($ this ->manager , $ this ->getDatabaseName (), $ this ->getCollectionName (), [], $ this ->defaultOptions );
80
+
81
+ $ events = [];
82
+
83
+ (new CommandObserver )->observe (
84
+ function () use ($ operation , &$ changeStream ) {
85
+ $ changeStream = $ operation ->execute ($ this ->getPrimaryServer ());
86
+ },
87
+ function (array $ event ) use (&$ events ) {
88
+ $ events [] = $ event ;
89
+ }
90
+ );
91
+
92
+ $ this ->assertCount (1 , $ events );
93
+ $ this ->assertSame ('aggregate ' , $ events [0 ]['started ' ]->getCommandName ());
94
+ $ postBatchResumeToken = $ this ->getPostBatchResumeTokenFromReply ($ events [0 ]['succeeded ' ]->getReply ());
95
+
96
+ $ changeStream ->rewind ();
97
+ $ this ->assertFalse ($ changeStream ->valid ());
98
+ $ this ->assertSameDocument ($ postBatchResumeToken , $ changeStream ->getResumeToken ());
99
+
100
+ $ this ->insertDocument (['x ' => 1 ]);
101
+ $ this ->insertDocument (['x ' => 2 ]);
102
+
103
+ $ events = [];
104
+
105
+ (new CommandObserver )->observe (
106
+ function () use ($ changeStream ) {
107
+ $ changeStream ->next ();
108
+ },
109
+ function (array $ event ) use (&$ events ) {
110
+ $ events [] = $ event ;
111
+ }
112
+ );
113
+
114
+ $ this ->assertCount (1 , $ events );
115
+ $ this ->assertSame ('getMore ' , $ events [0 ]['started ' ]->getCommandName ());
116
+ $ postBatchResumeToken = $ this ->getPostBatchResumeTokenFromReply ($ events [0 ]['succeeded ' ]->getReply ());
117
+
118
+ $ changeStream ->next ();
119
+ $ this ->assertSameDocument ($ changeStream ->current ()->_id , $ changeStream ->getResumeToken ());
120
+
121
+ $ changeStream ->next ();
122
+ $ this ->assertSameDocument ($ postBatchResumeToken , $ changeStream ->getResumeToken ());
123
+ }
124
+
125
+ /**
126
+ * Prose test: "ChangeStream will resume after a killCursors command is
127
+ * issued for its child cursor."
128
+ */
36
129
public function testNextResumesAfterCursorNotFound ()
37
130
{
38
131
$ operation = new Watch ($ this ->manager , $ this ->getDatabaseName (), $ this ->getCollectionName (), [], $ this ->defaultOptions );
@@ -127,7 +220,6 @@ public function testResumeBeforeReceivingAnyResultsIncludesPostBatchResumeToken(
127
220
128
221
$ operation = new Watch ($ this ->manager , $ this ->getDatabaseName (), $ this ->getCollectionName (), [], $ this ->defaultOptions );
129
222
130
- $ operationTime = null ;
131
223
$ events = [];
132
224
133
225
(new CommandObserver )->observe (
@@ -141,12 +233,7 @@ function (array $event) use (&$events) {
141
233
142
234
$ this ->assertCount (1 , $ events );
143
235
$ this ->assertSame ('aggregate ' , $ events [0 ]['started ' ]->getCommandName ());
144
- $ reply = $ events [0 ]['succeeded ' ]->getReply ();
145
- $ this ->assertObjectHasAttribute ('cursor ' , $ reply );
146
- $ this ->assertInternalType ('object ' , $ reply ->cursor );
147
- $ this ->assertObjectHasAttribute ('postBatchResumeToken ' , $ reply ->cursor );
148
- $ postBatchResumeToken = $ reply ->cursor ->postBatchResumeToken ;
149
- $ this ->assertInternalType ('object ' , $ postBatchResumeToken );
236
+ $ postBatchResumeToken = $ this ->getPostBatchResumeTokenFromReply ($ events [0 ]['succeeded ' ]->getReply ());
150
237
151
238
$ this ->assertFalse ($ changeStream ->valid ());
152
239
$ this ->killChangeStreamCursor ($ changeStream );
@@ -190,6 +277,11 @@ private function assertResumeAfter($expectedResumeToken, stdClass $command)
190
277
$ this ->assertEquals ($ expectedResumeToken , $ command ->pipeline [0 ]->{'$changeStream ' }->resumeAfter );
191
278
}
192
279
280
+ /**
281
+ * Prose test: "$changeStream stage for ChangeStream against a server >=4.0
282
+ * and <4.0.7 that has not received any results yet MUST include a
283
+ * startAtOperationTime option when resuming a changestream."
284
+ */
193
285
public function testResumeBeforeReceivingAnyResultsIncludesStartAtOperationTime ()
194
286
{
195
287
if ( ! $ this ->isStartAtOperationTimeSupported ()) {
@@ -202,7 +294,6 @@ public function testResumeBeforeReceivingAnyResultsIncludesStartAtOperationTime(
202
294
203
295
$ operation = new Watch ($ this ->manager , $ this ->getDatabaseName (), $ this ->getCollectionName (), [], $ this ->defaultOptions );
204
296
205
- $ operationTime = null ;
206
297
$ events = [];
207
298
208
299
(new CommandObserver )->observe (
@@ -554,6 +645,10 @@ public function testNonEmptyPipeline()
554
645
$ this ->assertSameDocument ($ expectedResult , $ changeStream ->current ());
555
646
}
556
647
648
+ /**
649
+ * Prose test: "Ensure that a cursor returned from an aggregate command with
650
+ * a cursor id and an initial empty batch is not closed on the driver side."
651
+ */
557
652
public function testInitialCursorIsNotClosed ()
558
653
{
559
654
$ operation = new Watch ($ this ->manager , $ this ->getDatabaseName (), $ this ->getCollectionName (), []);
@@ -567,7 +662,7 @@ public function testInitialCursorIsNotClosed()
567
662
* internal Cursor and call isDead(). */
568
663
$ this ->assertNotEquals ('0 ' , (string ) $ changeStream ->getCursorId ());
569
664
570
- $ rc = new ReflectionClass (' MongoDB\ ChangeStream' );
665
+ $ rc = new ReflectionClass (ChangeStream::class );
571
666
$ rp = $ rc ->getProperty ('iterator ' );
572
667
$ rp ->setAccessible (true );
573
668
@@ -577,7 +672,7 @@ public function testInitialCursorIsNotClosed()
577
672
578
673
$ cursor = $ iterator ->getInnerIterator ();
579
674
580
- $ this ->assertInstanceOf (' MongoDB\Driver\ Cursor' , $ cursor );
675
+ $ this ->assertInstanceOf (Cursor::class , $ cursor );
581
676
$ this ->assertFalse ($ cursor ->isDead ());
582
677
}
583
678
@@ -1109,6 +1204,16 @@ function(array $event) use (&$commands) {
1109
1204
$ this ->assertEmpty ($ commands );
1110
1205
}
1111
1206
1207
+ private function getPostBatchResumeTokenFromReply (stdClass $ reply )
1208
+ {
1209
+ $ this ->assertObjectHasAttribute ('cursor ' , $ reply );
1210
+ $ this ->assertInternalType ('object ' , $ reply ->cursor );
1211
+ $ this ->assertObjectHasAttribute ('postBatchResumeToken ' , $ reply ->cursor );
1212
+ $ this ->assertInternalType ('object ' , $ reply ->cursor ->postBatchResumeToken );
1213
+
1214
+ return $ reply ->cursor ->postBatchResumeToken ;
1215
+ }
1216
+
1112
1217
private function insertDocument ($ document )
1113
1218
{
1114
1219
$ insertOne = new InsertOne (
0 commit comments