diff --git a/tests/Sync/Sync/InconsistencyTest.php b/tests/Sync/Sync/InconsistencyTest.php new file mode 100644 index 0000000..0ee3b72 --- /dev/null +++ b/tests/Sync/Sync/InconsistencyTest.php @@ -0,0 +1,146 @@ +emptyTestFolder('INBOX', 'mail'); + $this->registerDevice(); + + // Append two mail messages + $uid1 = $this->appendMail('INBOX', 'mail.sync1'); + $this->appendMail('INBOX', 'mail.sync2'); + + // Initial sync + $folderId = '38b950ebd62cd9a66929c89615d0fc04'; + $syncKey = 0; + $request = << + + + + + {$syncKey} + {$folderId} + + + + EOF; + + $response = $this->request($request, 'Sync'); + $this->assertEquals(200, $response->getStatusCode()); + $syncKey++; + + $request = << + + + + + {$syncKey} + {$folderId} + 1 + 1 + 2 + + 0 + 1 + + 2 + 51200 + 0 + + + + + + EOF; + + $response = $this->request($request, 'Sync'); + + $this->assertEquals(200, $response->getStatusCode()); + + $dom = $this->fromWbxml($response->getBody()); + $xpath = $this->xpath($dom); + + $root = "//ns:Sync/ns:Collections/ns:Collection"; + $this->assertSame('1', $xpath->query("{$root}/ns:Status")->item(0)->nodeValue); + $this->assertSame(strval(++$syncKey), $xpath->query("{$root}/ns:SyncKey")->item(0)->nodeValue); + $this->assertSame($folderId, $xpath->query("{$root}/ns:CollectionId")->item(0)->nodeValue); + $this->assertSame(2, $xpath->query("{$root}/ns:Commands/ns:Add")->count()); + + // Initial sync is complete + // We now artifically create a sync inconsistency be deleting the content part of the first mail. + // This replicates a situation that we've seen, but don't know yet how it was created in the first place. + $rcube = \rcube::get_instance(); + $db = $rcube->get_dbh(); + $result = $db->query( + "DELETE FROM `syncroton_content`" + . " WHERE `contentid` = ?", + "$folderId::$uid1" + ); + $this->assertNull($db->is_error($result)); + + // Now sync again + $request = << + + + + + {$syncKey} + {$folderId} + 1 + 1 + + 0 + 1 + + 2 + 51200 + 0 + + + + + + EOF; + + $response = $this->request($request, 'Sync'); + + $this->assertEquals(200, $response->getStatusCode()); + + $dom = $this->fromWbxml($response->getBody()); + $xpath = $this->xpath($dom); + + $root = "//ns:Sync/ns:Collections/ns:Collection"; + $this->assertSame('1', $xpath->query("{$root}/ns:Status")->item(0)->nodeValue); + $this->assertSame(strval(++$syncKey), $xpath->query("{$root}/ns:SyncKey")->item(0)->nodeValue); + $this->assertSame($folderId, $xpath->query("{$root}/ns:CollectionId")->item(0)->nodeValue); + $this->assertSame(1, $xpath->query("{$root}/ns:Commands/ns:Add")->count()); + + + //Assert that we have all content parts back + $sync = \kolab_sync::get_instance(); + $device = $sync->storage()->device_get(self::$deviceId); + + $result = $db->query( + "SELECT `contentid` FROM `syncroton_content`" + . " WHERE `device_id` = ?", + $device['ID'] + ); + $data = []; + while ($state = $db->fetch_assoc($result)) { + $data[] = $state; + } + $this->assertSame(2, count($data)); + + return $syncKey; + } + +}