1<?php 2 3namespace Sabre\DAV\Sync; 4 5use 6 Sabre\DAV, 7 Sabre\HTTP; 8 9require_once __DIR__ . '/MockSyncCollection.php'; 10 11class PluginTest extends \Sabre\DAVServerTest { 12 13 protected $collection; 14 15 function setUp() { 16 17 parent::setUp(); 18 $this->server->addPlugin(new Plugin()); 19 20 } 21 22 function testGetInfo() { 23 24 $this->assertArrayHasKey( 25 'name', 26 (new Plugin())->getPluginInfo() 27 ); 28 29 } 30 31 function setUpTree() { 32 33 $this->collection = 34 new MockSyncCollection('coll', [ 35 new DAV\SimpleFile('file1.txt','foo'), 36 new DAV\SimpleFile('file2.txt','bar'), 37 ]); 38 $this->tree = [ 39 $this->collection, 40 new DAV\SimpleCollection('normalcoll', []) 41 ]; 42 43 } 44 45 function testSupportedReportSet() { 46 47 $result = $this->server->getProperties('/coll', ['{DAV:}supported-report-set']); 48 $this->assertFalse($result['{DAV:}supported-report-set']->has('{DAV:}sync-collection')); 49 50 // Making a change 51 $this->collection->addChange(['file1.txt'], [], []); 52 53 $result = $this->server->getProperties('/coll', ['{DAV:}supported-report-set']); 54 $this->assertTrue($result['{DAV:}supported-report-set']->has('{DAV:}sync-collection')); 55 56 } 57 58 function testGetSyncToken() { 59 60 $result = $this->server->getProperties('/coll', ['{DAV:}sync-token']); 61 $this->assertFalse(isset($result['{DAV:}sync-token'])); 62 63 // Making a change 64 $this->collection->addChange(['file1.txt'], [], []); 65 66 $result = $this->server->getProperties('/coll', ['{DAV:}sync-token']); 67 $this->assertTrue(isset($result['{DAV:}sync-token'])); 68 69 // non-sync-enabled collection 70 $this->collection->addChange(['file1.txt'], [], []); 71 72 $result = $this->server->getProperties('/normalcoll', ['{DAV:}sync-token']); 73 $this->assertFalse(isset($result['{DAV:}sync-token'])); 74 } 75 76 function testSyncInitialSyncCollection() { 77 78 // Making a change 79 $this->collection->addChange(['file1.txt'], [], []); 80 81 $request = new HTTP\Request('REPORT', '/coll/', ['Content-Type' => 'application/xml']); 82 83 $body = <<<BLA 84<?xml version="1.0" encoding="utf-8" ?> 85<D:sync-collection xmlns:D="DAV:"> 86 <D:sync-token/> 87 <D:sync-level>1</D:sync-level> 88 <D:prop> 89 <D:getcontentlength/> 90 </D:prop> 91</D:sync-collection> 92BLA; 93 94 $request->setBody($body); 95 96 $response = $this->request($request); 97 98 $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); 99 100 $multiStatus = $this->server->xml->parse($response->getBodyAsString()); 101 102 // Checking the sync-token 103 $this->assertEquals( 104 'http://sabre.io/ns/sync/1', 105 $multiStatus->getSyncToken() 106 ); 107 108 $responses = $multiStatus->getResponses(); 109 $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); 110 111 $response = $responses[0]; 112 113 $this->assertNull($response->getHttpStatus()); 114 $this->assertEquals('/coll/file1.txt', $response->getHref()); 115 $this->assertEquals([ 116 200 => [ 117 '{DAV:}getcontentlength' => 3, 118 ] 119 ], $response->getResponseProperties()); 120 121 $response = $responses[1]; 122 123 $this->assertNull($response->getHttpStatus()); 124 $this->assertEquals('/coll/file2.txt', $response->getHref()); 125 $this->assertEquals([ 126 200 => [ 127 '{DAV:}getcontentlength' => 3, 128 ] 129 ], $response->getResponseProperties()); 130 131 } 132 133 function testSubsequentSyncSyncCollection() { 134 135 // Making a change 136 $this->collection->addChange(['file1.txt'], [], []); 137 // Making another change 138 $this->collection->addChange([], ['file2.txt'], ['file3.txt']); 139 140 $request = HTTP\Sapi::createFromServerArray([ 141 'REQUEST_METHOD' => 'REPORT', 142 'REQUEST_URI' => '/coll/', 143 'CONTENT_TYPE' => 'application/xml', 144 ]); 145 146 $body = <<<BLA 147<?xml version="1.0" encoding="utf-8" ?> 148<D:sync-collection xmlns:D="DAV:"> 149 <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> 150 <D:sync-level>infinite</D:sync-level> 151 <D:prop> 152 <D:getcontentlength/> 153 </D:prop> 154</D:sync-collection> 155BLA; 156 157 $request->setBody($body); 158 159 $response = $this->request($request); 160 161 $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); 162 163 $multiStatus = $this->server->xml->parse($response->getBodyAsString()); 164 165 // Checking the sync-token 166 $this->assertEquals( 167 'http://sabre.io/ns/sync/2', 168 $multiStatus->getSyncToken() 169 ); 170 171 $responses = $multiStatus->getResponses(); 172 $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); 173 174 $response = $responses[0]; 175 176 $this->assertNull($response->getHttpStatus()); 177 $this->assertEquals('/coll/file2.txt', $response->getHref()); 178 $this->assertEquals([ 179 200 => [ 180 '{DAV:}getcontentlength' => 3, 181 ] 182 ], $response->getResponseProperties()); 183 184 $response = $responses[1]; 185 186 $this->assertEquals('404', $response->getHttpStatus()); 187 $this->assertEquals('/coll/file3.txt', $response->getHref()); 188 $this->assertEquals([], $response->getResponseProperties()); 189 190 } 191 192 function testSubsequentSyncSyncCollectionLimit() { 193 194 // Making a change 195 $this->collection->addChange(['file1.txt'], [], []); 196 // Making another change 197 $this->collection->addChange([], ['file2.txt'], ['file3.txt']); 198 199 $request = HTTP\Sapi::createFromServerArray([ 200 'REQUEST_METHOD' => 'REPORT', 201 'REQUEST_URI' => '/coll/', 202 'CONTENT_TYPE' => 'application/xml', 203 ]); 204 205 $body = <<<BLA 206<?xml version="1.0" encoding="utf-8" ?> 207<D:sync-collection xmlns:D="DAV:"> 208 <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> 209 <D:sync-level>infinite</D:sync-level> 210 <D:prop> 211 <D:getcontentlength/> 212 </D:prop> 213 <D:limit><D:nresults>1</D:nresults></D:limit> 214</D:sync-collection> 215BLA; 216 217 $request->setBody($body); 218 219 $response = $this->request($request); 220 221 $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); 222 223 $multiStatus = $this->server->xml->parse( 224 $response->getBodyAsString() 225 ); 226 227 // Checking the sync-token 228 $this->assertEquals( 229 'http://sabre.io/ns/sync/2', 230 $multiStatus->getSyncToken() 231 ); 232 233 $responses = $multiStatus->getResponses(); 234 $this->assertEquals(1, count($responses), 'We expected exactly 1 {DAV:}response'); 235 236 $response = $responses[0]; 237 238 $this->assertEquals('404', $response->getHttpStatus()); 239 $this->assertEquals('/coll/file3.txt', $response->getHref()); 240 $this->assertEquals([], $response->getResponseProperties()); 241 242 } 243 244 function testSubsequentSyncSyncCollectionDepthFallBack() { 245 246 // Making a change 247 $this->collection->addChange(['file1.txt'], [], []); 248 // Making another change 249 $this->collection->addChange([], ['file2.txt'], ['file3.txt']); 250 251 $request = HTTP\Sapi::createFromServerArray([ 252 'REQUEST_METHOD' => 'REPORT', 253 'REQUEST_URI' => '/coll/', 254 'CONTENT_TYPE' => 'application/xml', 255 'HTTP_DEPTH' => "1", 256 ]); 257 258 $body = <<<BLA 259<?xml version="1.0" encoding="utf-8" ?> 260<D:sync-collection xmlns:D="DAV:"> 261 <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> 262 <D:prop> 263 <D:getcontentlength/> 264 </D:prop> 265</D:sync-collection> 266BLA; 267 268 $request->setBody($body); 269 270 $response = $this->request($request); 271 272 $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); 273 274 $multiStatus = $this->server->xml->parse( 275 $response->getBodyAsString() 276 ); 277 278 // Checking the sync-token 279 $this->assertEquals( 280 'http://sabre.io/ns/sync/2', 281 $multiStatus->getSyncToken() 282 ); 283 284 $responses = $multiStatus->getResponses(); 285 $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); 286 287 $response = $responses[0]; 288 289 $this->assertNull($response->getHttpStatus()); 290 $this->assertEquals('/coll/file2.txt', $response->getHref()); 291 $this->assertEquals([ 292 200 => [ 293 '{DAV:}getcontentlength' => 3, 294 ] 295 ], $response->getResponseProperties()); 296 297 $response = $responses[1]; 298 299 $this->assertEquals('404', $response->getHttpStatus()); 300 $this->assertEquals('/coll/file3.txt', $response->getHref()); 301 $this->assertEquals([], $response->getResponseProperties()); 302 303 } 304 305 function testSyncNoSyncInfo() { 306 307 $request = HTTP\Sapi::createFromServerArray([ 308 'REQUEST_METHOD' => 'REPORT', 309 'REQUEST_URI' => '/coll/', 310 'CONTENT_TYPE' => 'application/xml', 311 ]); 312 313 $body = <<<BLA 314<?xml version="1.0" encoding="utf-8" ?> 315<D:sync-collection xmlns:D="DAV:"> 316 <D:sync-token/> 317 <D:sync-level>1</D:sync-level> 318 <D:prop> 319 <D:getcontentlength/> 320 </D:prop> 321</D:sync-collection> 322BLA; 323 324 $request->setBody($body); 325 326 $response = $this->request($request); 327 328 // The default state has no sync-token, so this report should not yet 329 // be supported. 330 $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); 331 332 } 333 334 function testSyncNoSyncCollection() { 335 336 $request = HTTP\Sapi::createFromServerArray([ 337 'REQUEST_METHOD' => 'REPORT', 338 'REQUEST_URI' => '/normalcoll/', 339 'CONTENT_TYPE' => 'application/xml', 340 ]); 341 342 $body = <<<BLA 343<?xml version="1.0" encoding="utf-8" ?> 344<D:sync-collection xmlns:D="DAV:"> 345 <D:sync-token/> 346 <D:sync-level>1</D:sync-level> 347 <D:prop> 348 <D:getcontentlength/> 349 </D:prop> 350</D:sync-collection> 351BLA; 352 353 $request->setBody($body); 354 355 $response = $this->request($request); 356 357 // The default state has no sync-token, so this report should not yet 358 // be supported. 359 $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); 360 361 } 362 363 function testSyncInvalidToken() { 364 365 $this->collection->addChange(['file1.txt'], [], []); 366 $request = HTTP\Sapi::createFromServerArray([ 367 'REQUEST_METHOD' => 'REPORT', 368 'REQUEST_URI' => '/coll/', 369 'CONTENT_TYPE' => 'application/xml', 370 ]); 371 372 $body = <<<BLA 373<?xml version="1.0" encoding="utf-8" ?> 374<D:sync-collection xmlns:D="DAV:"> 375 <D:sync-token>http://sabre.io/ns/sync/invalid</D:sync-token> 376 <D:sync-level>1</D:sync-level> 377 <D:prop> 378 <D:getcontentlength/> 379 </D:prop> 380</D:sync-collection> 381BLA; 382 383 $request->setBody($body); 384 385 $response = $this->request($request); 386 387 // The default state has no sync-token, so this report should not yet 388 // be supported. 389 $this->assertEquals(403, $response->status, 'Full response body:' . $response->body); 390 391 } 392 function testSyncInvalidTokenNoPrefix() { 393 394 $this->collection->addChange(['file1.txt'], [], []); 395 $request = HTTP\Sapi::createFromServerArray([ 396 'REQUEST_METHOD' => 'REPORT', 397 'REQUEST_URI' => '/coll/', 398 'CONTENT_TYPE' => 'application/xml', 399 ]); 400 401 $body = <<<BLA 402<?xml version="1.0" encoding="utf-8" ?> 403<D:sync-collection xmlns:D="DAV:"> 404 <D:sync-token>invalid</D:sync-token> 405 <D:sync-level>1</D:sync-level> 406 <D:prop> 407 <D:getcontentlength/> 408 </D:prop> 409</D:sync-collection> 410BLA; 411 412 $request->setBody($body); 413 414 $response = $this->request($request); 415 416 // The default state has no sync-token, so this report should not yet 417 // be supported. 418 $this->assertEquals(403, $response->status, 'Full response body:' . $response->body); 419 420 } 421 422 function testSyncNoSyncToken() { 423 424 $request = HTTP\Sapi::createFromServerArray([ 425 'REQUEST_METHOD' => 'REPORT', 426 'REQUEST_URI' => '/coll/', 427 'CONTENT_TYPE' => 'application/xml', 428 ]); 429 430 $body = <<<BLA 431<?xml version="1.0" encoding="utf-8" ?> 432<D:sync-collection xmlns:D="DAV:"> 433 <D:sync-level>1</D:sync-level> 434 <D:prop> 435 <D:getcontentlength/> 436 </D:prop> 437</D:sync-collection> 438BLA; 439 440 $request->setBody($body); 441 442 $response = $this->request($request); 443 444 // The default state has no sync-token, so this report should not yet 445 // be supported. 446 $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); 447 448 } 449 450 function testSyncNoProp() { 451 452 $this->collection->addChange(['file1.txt'], [], []); 453 $request = HTTP\Sapi::createFromServerArray([ 454 'REQUEST_METHOD' => 'REPORT', 455 'REQUEST_URI' => '/coll/', 456 'CONTENT_TYPE' => 'application/xml', 457 ]); 458 459 $body = <<<BLA 460<?xml version="1.0" encoding="utf-8" ?> 461<D:sync-collection xmlns:D="DAV:"> 462 <D:sync-token /> 463 <D:sync-level>1</D:sync-level> 464</D:sync-collection> 465BLA; 466 467 $request->setBody($body); 468 469 $response = $this->request($request); 470 471 // The default state has no sync-token, so this report should not yet 472 // be supported. 473 $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); 474 475 } 476 477 function testIfConditions() { 478 479 $this->collection->addChange(['file1.txt'], [], []); 480 $request = HTTP\Sapi::createFromServerArray([ 481 'REQUEST_METHOD' => 'DELETE', 482 'REQUEST_URI' => '/coll/file1.txt', 483 'HTTP_IF' => '</coll> (<http://sabre.io/ns/sync/1>)', 484 ]); 485 $response = $this->request($request); 486 487 // If a 403 is thrown this works correctly. The file in questions 488 // doesn't allow itself to be deleted. 489 // If the If conditions failed, it would have been a 412 instead. 490 $this->assertEquals(403, $response->status); 491 492 } 493 494 function testIfConditionsNot() { 495 496 $this->collection->addChange(['file1.txt'], [], []); 497 $request = HTTP\Sapi::createFromServerArray([ 498 'REQUEST_METHOD' => 'DELETE', 499 'REQUEST_URI' => '/coll/file1.txt', 500 'HTTP_IF' => '</coll> (Not <http://sabre.io/ns/sync/2>)', 501 ]); 502 $response = $this->request($request); 503 504 // If a 403 is thrown this works correctly. The file in questions 505 // doesn't allow itself to be deleted. 506 // If the If conditions failed, it would have been a 412 instead. 507 $this->assertEquals(403, $response->status); 508 509 } 510 511 function testIfConditionsNoSyncToken() { 512 513 $this->collection->addChange(['file1.txt'], [], []); 514 $request = HTTP\Sapi::createFromServerArray([ 515 'REQUEST_METHOD' => 'DELETE', 516 'REQUEST_URI' => '/coll/file1.txt', 517 'HTTP_IF' => '</coll> (<opaquelocktoken:foo>)', 518 ]); 519 $response = $this->request($request); 520 521 $this->assertEquals(412, $response->status); 522 523 } 524} 525