Skip to content

Commit ec15782

Browse files
committed
PHPLIB-422: Test resume token errors for all server versions
Inconsistent behavior for advancing the ChangeStream's key after a resume token error will be addressed by PHPLIB-456.
1 parent 35e6181 commit ec15782

File tree

1 file changed

+94
-16
lines changed

1 file changed

+94
-16
lines changed

tests/Operation/WatchFunctionalTest.php

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,12 @@ public function provideNonResumableErrorCodes()
620620
];
621621
}
622622

623-
public function testNextResumeTokenNotFound()
623+
/**
624+
* Prose test: "ChangeStream will throw an exception if the server response
625+
* is missing the resume token (if wire version is < 8, this is a driver-
626+
* side error; for 8+, this is a server-side error)"
627+
*/
628+
public function testResumeTokenNotFoundClientSideError()
624629
{
625630
if (version_compare($this->getServerVersion(), '4.1.8', '>=')) {
626631
$this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)');
@@ -631,16 +636,47 @@ public function testNextResumeTokenNotFound()
631636
$operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions);
632637
$changeStream = $operation->execute($this->getPrimaryServer());
633638

634-
/* Note: we intentionally do not start iteration with rewind() to ensure
635-
* that we test extraction functionality within next(). */
639+
$changeStream->rewind();
640+
641+
/* Insert two documents to ensure the client does not ignore the first
642+
* document's resume token in favor of a postBatchResumeToken */
636643
$this->insertDocument(['x' => 1]);
644+
$this->insertDocument(['x' => 2]);
637645

638646
$this->expectException(ResumeTokenException::class);
639647
$this->expectExceptionMessage('Resume token not found in change document');
640648
$changeStream->next();
641649
}
642650

643-
public function testNextResumeTokenInvalidType()
651+
/**
652+
* Prose test: "ChangeStream will throw an exception if the server response
653+
* is missing the resume token (if wire version is < 8, this is a driver-
654+
* side error; for 8+, this is a server-side error)"
655+
*/
656+
public function testResumeTokenNotFoundServerSideError()
657+
{
658+
if (version_compare($this->getServerVersion(), '4.1.8', '<')) {
659+
$this->markTestSkipped('Server does not reject change streams that modify resume token');
660+
}
661+
662+
$pipeline = [['$project' => ['_id' => 0 ]]];
663+
664+
$operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions);
665+
$changeStream = $operation->execute($this->getPrimaryServer());
666+
667+
$changeStream->rewind();
668+
$this->insertDocument(['x' => 1]);
669+
670+
$this->expectException(ServerException::class);
671+
$changeStream->next();
672+
}
673+
674+
/**
675+
* Prose test: "ChangeStream will throw an exception if the server response
676+
* is missing the resume token (if wire version is < 8, this is a driver-
677+
* side error; for 8+, this is a server-side error)"
678+
*/
679+
public function testResumeTokenInvalidTypeClientSideError()
644680
{
645681
if (version_compare($this->getServerVersion(), '4.1.8', '>=')) {
646682
$this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)');
@@ -651,15 +687,41 @@ public function testNextResumeTokenInvalidType()
651687
$operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions);
652688
$changeStream = $operation->execute($this->getPrimaryServer());
653689

654-
/* Note: we intentionally do not start iteration with rewind() to ensure
655-
* that we test extraction functionality within next(). */
690+
$changeStream->rewind();
691+
692+
/* Insert two documents to ensure the client does not ignore the first
693+
* document's resume token in favor of a postBatchResumeToken */
656694
$this->insertDocument(['x' => 1]);
695+
$this->insertDocument(['x' => 2]);
657696

658697
$this->expectException(ResumeTokenException::class);
659698
$this->expectExceptionMessage('Expected resume token to have type "array or object" but found "string"');
660699
$changeStream->next();
661700
}
662701

702+
/**
703+
* Prose test: "ChangeStream will throw an exception if the server response
704+
* is missing the resume token (if wire version is < 8, this is a driver-
705+
* side error; for 8+, this is a server-side error)"
706+
*/
707+
public function testResumeTokenInvalidTypeServerSideError()
708+
{
709+
if (version_compare($this->getServerVersion(), '4.1.8', '<')) {
710+
$this->markTestSkipped('Server does not reject change streams that modify resume token');
711+
}
712+
713+
$pipeline = [['$project' => ['_id' => ['$literal' => 'foo']]]];
714+
715+
$operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions);
716+
$changeStream = $operation->execute($this->getPrimaryServer());
717+
718+
$changeStream->rewind();
719+
$this->insertDocument(['x' => 1]);
720+
721+
$this->expectException(ServerException::class);
722+
$changeStream->next();
723+
}
724+
663725
public function testMaxAwaitTimeMS()
664726
{
665727
/* On average, an acknowledged write takes about 20 ms to appear in a
@@ -908,10 +970,6 @@ public function testNextAdvancesKey()
908970

909971
public function testResumeTokenNotFoundDoesNotAdvanceKey()
910972
{
911-
if (version_compare($this->getServerVersion(), '4.1.8', '>=')) {
912-
$this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)');
913-
}
914-
915973
$pipeline = [['$project' => ['_id' => 0 ]]];
916974

917975
$operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $pipeline, $this->defaultOptions);
@@ -923,20 +981,40 @@ public function testResumeTokenNotFoundDoesNotAdvanceKey()
923981

924982
$changeStream->rewind();
925983
$this->assertFalse($changeStream->valid());
984+
$this->assertNull($changeStream->key());
926985

927986
try {
928987
$changeStream->next();
929-
$this->fail('ResumeTokenException was not thrown');
930-
} catch (ResumeTokenException $e) {}
988+
$this->fail('Exception for missing resume token was not thrown');
989+
} catch (ResumeTokenException $e) {
990+
/* If a client-side error is thrown (server < 4.1.8), the tailable
991+
* cursor's position is still valid. This may change once PHPLIB-456
992+
* is implemented. */
993+
$expectedValid = true;
994+
$expectedKey = 0;
995+
} catch (ServerException $e) {
996+
/* If a server-side error is thrown (server >= 4.1.8), the tailable
997+
* cursor's position is not valid. */
998+
$expectedValid = false;
999+
$expectedKey = null;
1000+
}
9311001

932-
$this->assertSame(0, $changeStream->key());
1002+
$this->assertSame($expectedValid, $changeStream->valid());
1003+
$this->assertSame($expectedKey, $changeStream->key());
9331004

9341005
try {
9351006
$changeStream->next();
936-
$this->fail('ResumeTokenException was not thrown');
937-
} catch (ResumeTokenException $e) {}
1007+
$this->fail('Exception for missing resume token was not thrown');
1008+
} catch (ResumeTokenException $e) {
1009+
$expectedValid = true;
1010+
$expectedKey = 0;
1011+
} catch (ServerException $e) {
1012+
$expectedValid = false;
1013+
$expectedKey = null;
1014+
}
9381015

939-
$this->assertSame(0, $changeStream->key());
1016+
$this->assertSame($expectedValid, $changeStream->valid());
1017+
$this->assertSame($expectedKey, $changeStream->key());
9401018
}
9411019

9421020
public function testSessionPersistsAfterResume()

0 commit comments

Comments
 (0)