UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2022 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <inttypes.h> 31 32 #include "../../util/util.h" 33 #include "../../test/testutils.h" 34 #include "../../test/webdav.h" 35 #include "../../public/nsapi.h" 36 #include "../../public/webdav.h" 37 #include "../../webdav/webdav.h" 38 39 #include <cx/string.h> 40 #include <cx/utils.h> 41 #include <cx/buffer.h> 42 43 #include "pgtest.h" 44 #include "vfs.h" 45 #include "webdav.h" 46 47 #include <libpq-fe.h> 48 49 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) 50 51 #define MAP_GET(map, key) cxMapGet(map, cx_hash_key_str(key)) 52 #define MAP_PUT(map, key, value) cxMapPut(map, cx_hash_key_str(key), value) 53 54 static char *pg_connstr = "postgresql://localhost/test1"; 55 static int abort_pg_tests = 0; 56 static PGconn *test_connection; 57 static ResourceData resdata; 58 static PgRepository test_repo; 59 60 void debug_print_resources(void) { 61 PGresult *result = PQexec(test_connection, "select * from Resource;"); 62 int n = PQntuples(result); 63 printf("\nntuples: %d\n-----------------------------------------------\n", n); 64 printf("%10s %10s %s\n", "resource_id", "parent_id", "nodename"); 65 for(int i=0;i<n;i++) { 66 char *res_id = PQgetvalue(result, i, 0); 67 char *parent_id = PQgetvalue(result, i, 1); 68 char *nodename = PQgetvalue(result, i, 2); 69 printf("%10s %10s %s\n", res_id, parent_id, nodename); 70 } 71 printf("\n"); 72 } 73 74 static void test_root_lookup(void) { 75 memset(&test_repo, 0, sizeof(PgRepository)); 76 77 int64_t root_id = -1; 78 int err = pg_lookup_root(&resdata, "root", &root_id); 79 test_repo.root_resource_id = root_id; 80 81 if(err || root_id < 0) { 82 abort_pg_tests = 1; 83 } 84 } 85 86 void register_pg_tests(int argc, char **argv, UcxTestSuite *suite) { 87 88 test_connection = PQconnectdb(pg_connstr); 89 if(!test_connection) { 90 abort_pg_tests = 1; 91 } 92 93 if(PQstatus(test_connection) != CONNECTION_OK) { 94 abort_pg_tests = 1; 95 } 96 97 resdata.data = test_connection; 98 test_root_lookup(); 99 100 ucx_test_register(suite, test_pg_conn); 101 if(!abort_pg_tests) { 102 ucx_test_register(suite, test_pg_lookup_root); 103 104 ucx_test_register(suite, test_pg_vfs_open); 105 ucx_test_register(suite, test_pg_vfs_io); 106 ucx_test_register(suite, test_pg_vfs_stat); 107 ucx_test_register(suite, test_pg_vfs_mkdir); 108 ucx_test_register(suite, test_pg_vfs_unlink); 109 ucx_test_register(suite, test_pg_vfs_rmdir); 110 111 ucx_test_register(suite, test_pg_webdav_create_from_resdata); 112 ucx_test_register(suite, test_pg_prepare_tests); 113 ucx_test_register(suite, test_pg_webdav_propfind); 114 ucx_test_register(suite, test_pg_webdav_propfind_allprop); 115 ucx_test_register(suite, test_pg_webdav_proppatch_set); 116 117 PGresult *result = PQexec(test_connection, "BEGIN"); 118 PQclear(result); 119 } 120 } 121 122 static void parse_response_tag(TestMultistatus *ms, xmlNode *node) { 123 // thanks to dav for some of this code 124 CxAllocator *a = (CxAllocator*)ms->mp->allocator; 125 node = node->children; 126 127 cxmutstr href = {NULL, 0}; 128 CxMap *properties = cxHashMapCreate(a, CX_STORE_POINTERS, 16); 129 130 while(node) { 131 if(node->type == XML_ELEMENT_NODE) { 132 if(xstreq(node->name, "href")) { 133 xmlNode *href_node = node->children; 134 if(href_node->type != XML_TEXT_NODE) { 135 return; 136 } 137 href = cx_strdup_a(a, cx_str((const char*)href_node->content)); 138 } else if(xstreq(node->name, "propstat")) { 139 xmlNode *n = node->children; 140 xmlNode *prop_node = NULL; 141 int status_code = 0; 142 while(n) { 143 if(n->type == XML_ELEMENT_NODE) { 144 if(xstreq(n->name, "prop")) { 145 prop_node = n; 146 } else if(xstreq(n->name, "status")) { 147 xmlNode *status_node = n->children; 148 if(status_node->type != XML_TEXT_NODE) { 149 return; 150 } 151 cxmutstr status_str = cx_mutstr((char*)status_node->content); 152 if(status_str.length < 13) { 153 return; 154 } 155 status_str = cx_strsubsl_m(status_str, 9, 3); 156 cxmutstr status_s = cx_strdup(cx_strcast(status_str)); 157 status_code = atoi(status_s.ptr); 158 free(status_s.ptr); 159 } 160 } 161 n = n->next; 162 } 163 164 n = prop_node->children; 165 while(n) { 166 if(n->type == XML_ELEMENT_NODE) { 167 TestProperty *property = cxCalloc(ms->mp->allocator, 1, sizeof(TestProperty)); 168 if(n->ns) { 169 property->prefix = n->ns->prefix ? cx_strdup_a(a, cx_str((const char*)n->ns->prefix)).ptr : NULL; 170 property->namespace = n->ns->href ? cx_strdup_a(a, cx_str((const char*)n->ns->href)).ptr : NULL; 171 } 172 property->name = cx_strdup_a(a, cx_str((const char*)n->name)).ptr; 173 property->node = n; 174 property->status = status_code; 175 xmlNode *value = n->children; 176 if(value && value->type == XML_TEXT_NODE) { 177 property->value = cx_strdup_a(a, cx_str((const char*)value->content)).ptr; 178 } 179 if(property->namespace && property->name) { 180 cxmutstr pname = cx_strcat(2, cx_str(property->namespace), cx_str(property->name)); 181 cxMapPut(properties, cx_hash_key(pname.ptr, pname.length), property); 182 free(pname.ptr); 183 } 184 } 185 n = n->next; 186 } 187 } 188 } 189 node = node->next; 190 } 191 192 TestResponse *resp = cxMalloc(a, sizeof(TestResponse)); 193 resp->href = href.ptr; 194 resp->properties = properties; 195 196 cxMapPut(ms->responses, cx_hash_key(href.ptr, href.length), resp); 197 } 198 199 TestMultistatus* test_parse_multistatus(const char *space, size_t size) { 200 xmlDoc *doc = xmlReadMemory(space, size, NULL, NULL, 0); 201 if(!doc) { 202 return NULL; 203 } 204 205 CxMempool *mp = cxBasicMempoolCreate(64); 206 TestMultistatus *ms = cxMalloc(mp->allocator, sizeof(TestMultistatus)); 207 ms->doc = doc; 208 ms->mp = mp; 209 ms->responses = cxHashMapCreate((CxAllocator*)mp->allocator, CX_STORE_POINTERS, 8); 210 211 // parse response 212 xmlNode *xml_root = xmlDocGetRootElement(doc); 213 xmlNode *node = xml_root->children; 214 while(node) { 215 if(node->type == XML_ELEMENT_NODE) { 216 if(xstreq(node->name, "response")) { 217 parse_response_tag(ms, node); 218 } 219 } 220 node = node->next; 221 } 222 223 return ms; 224 } 225 226 227 void test_multistatus_destroy(TestMultistatus *ms) { 228 if(!ms) return; 229 xmlFreeDoc(ms->doc); 230 cxMempoolDestroy(ms->mp); 231 } 232 233 234 UCX_TEST(test_pg_conn) { 235 char *msg = test_connection ? PQerrorMessage(test_connection) : "no connection"; 236 237 UCX_TEST_BEGIN; 238 239 if(abort_pg_tests) { 240 int msglen = strlen(msg); 241 if(msglen > 0 && msg[msglen-1] == '\n') { 242 msglen--; 243 } 244 fprintf(stdout, "%.*s: ", msglen, msg); 245 UCX_TEST_ASSERT(1 == 0, "skip pg tests"); 246 } else { 247 UCX_TEST_ASSERT(1 == 1, "ok"); 248 } 249 250 UCX_TEST_END; 251 } 252 253 UCX_TEST(test_pg_lookup_root) { 254 UCX_TEST_BEGIN; 255 256 // test already done in test_root_lookup() 257 UCX_TEST_ASSERT(!abort_pg_tests, "Lookup failed"); 258 259 UCX_TEST_END; 260 } 261 262 263 static VFS* create_test_pgvfs(Session *sn, Request *rq) { 264 return pg_vfs_create_from_resourcedata(sn, rq, &test_repo, &resdata); 265 } 266 267 268 UCX_TEST(test_pg_vfs_open) { 269 Session *sn = testutil_session(); 270 Request *rq = testutil_request(sn->pool, "PUT", "/"); 271 rq->vfs = create_test_pgvfs(sn, rq); 272 VFSContext *vfs = vfs_request_context(sn, rq); 273 SYS_FILE file; 274 275 UCX_TEST_BEGIN; 276 277 file = vfs_open(vfs, "/test_notfound1", O_RDONLY); 278 UCX_TEST_ASSERT(!file, "/test_notfound should not exist"); 279 280 file = vfs_open(vfs, "/test_file1", O_RDWR | O_CREAT); 281 UCX_TEST_ASSERT(file, "cannot create file 1"); 282 283 vfs_close(file); 284 285 UCX_TEST_END; 286 287 testutil_destroy_session(sn); 288 } 289 290 UCX_TEST(test_pg_vfs_io) { 291 Session *sn = testutil_session(); 292 Request *rq = testutil_request(sn->pool, "PUT", "/"); 293 rq->vfs = create_test_pgvfs(sn, rq); 294 VFSContext *vfs = vfs_request_context(sn, rq); 295 SYS_FILE file; 296 SYS_FILE file2; 297 298 UCX_TEST_BEGIN; 299 300 file = vfs_open(vfs, "/test_f1", O_WRONLY | O_CREAT); 301 UCX_TEST_ASSERT(file, "cannot open file1"); 302 303 int w = system_fwrite(file, "test1\n", 6); 304 UCX_TEST_ASSERT(w == 6, "fwrite ret (1)"); 305 w = system_fwrite(file, "2", 1); 306 UCX_TEST_ASSERT(w == 1, "fwrite ret (2)"); 307 308 vfs_close(file); 309 310 file = vfs_open(vfs, "/test_f1", O_RDONLY); 311 file2 = vfs_open(vfs, "/test_f2", O_WRONLY | O_CREAT); 312 UCX_TEST_ASSERT(file, "cannot open file1"); 313 UCX_TEST_ASSERT(file2, "cannot open file2"); 314 315 char buf[128]; 316 int r = system_fread(file, buf, 128); 317 UCX_TEST_ASSERT(r == 7, "cannot read from file1"); 318 319 w = system_fwrite(file2, buf, r); 320 UCX_TEST_ASSERT(w == 7, "cannot write to file2"); 321 322 vfs_close(file); 323 vfs_close(file2); 324 325 file2 = vfs_open(vfs, "/test_f2", O_RDONLY); 326 327 r = system_fread(file, buf, 128); 328 UCX_TEST_ASSERT(r == 7, "fread ret"); 329 UCX_TEST_ASSERT(!memcmp(buf, "test1\n2", 7), "wrong buffer content after read"); 330 331 vfs_close(file2); 332 333 334 UCX_TEST_END; 335 336 testutil_destroy_session(sn); 337 } 338 339 UCX_TEST(test_pg_vfs_stat) { 340 Session *sn = testutil_session(); 341 Request *rq = testutil_request(sn->pool, "PUT", "/"); 342 rq->vfs = create_test_pgvfs(sn, rq); 343 VFSContext *vfs = vfs_request_context(sn, rq); 344 345 UCX_TEST_BEGIN; 346 347 // testdata, content doesn't matter 348 char test1[512]; 349 memset(test1, 'x', 512); 350 const int test_len1 = 200; 351 const int test_len2 = 432; 352 353 SYS_FILE f1 = vfs_open(vfs, "/test_s1", O_WRONLY|O_CREAT); 354 UCX_TEST_ASSERT(f1, "cannot open test_s1"); 355 system_fwrite(f1, test1, test_len1); 356 vfs_close(f1); 357 358 SYS_FILE f2 = vfs_open(vfs, "/test_s2", O_RDWR|O_CREAT); 359 UCX_TEST_ASSERT(f2, "cannot open test_s2"); 360 system_fwrite(f2, test1, test_len2); 361 vfs_close(f2); 362 363 struct stat st1, st2; 364 int r1 = vfs_stat(vfs, "/test_s1", &st1); 365 int r2 = vfs_stat(vfs, "/test_s2", &st2); 366 367 UCX_TEST_ASSERT(r1 == 0, "stat1 failed"); 368 UCX_TEST_ASSERT(r2 == 0, "stat2 failed"); 369 370 UCX_TEST_ASSERT(st1.st_size == test_len1, "s1 wrong length"); 371 UCX_TEST_ASSERT(st2.st_size == test_len2, "s2 wrong length"); 372 373 int testfail = vfs_stat(vfs, "/test_stat_fail", &st1); 374 UCX_TEST_ASSERT(testfail != 0, "stat 3 should fail"); 375 376 UCX_TEST_END; 377 378 testutil_destroy_session(sn); 379 } 380 381 UCX_TEST(test_pg_vfs_mkdir) { 382 Session *sn = testutil_session(); 383 Request *rq = testutil_request(sn->pool, "PUT", "/"); 384 rq->vfs = create_test_pgvfs(sn, rq); 385 VFSContext *vfs = vfs_request_context(sn, rq); 386 387 UCX_TEST_BEGIN; 388 389 struct stat s; 390 391 SYS_FILE f1 = vfs_open(vfs, "/test_mkdir/file", O_WRONLY|O_CREAT); 392 UCX_TEST_ASSERT(f1 == NULL, "open should fail"); 393 394 int r = vfs_mkdir(vfs, "/test_mkdir"); 395 UCX_TEST_ASSERT(r == 0, "mkdir failed"); 396 397 r = vfs_stat(vfs, "/test_mkdir", &s); 398 UCX_TEST_ASSERT(r == 0, "stat (1) failed"); 399 400 UCX_TEST_ASSERT(S_ISDIR(s.st_mode), "/test_mkdir is not a directory"); 401 402 f1 = vfs_open(vfs, "/test_mkdir/file", O_WRONLY|O_CREAT); 403 vfs_close(f1); 404 UCX_TEST_ASSERT(f1, "open failed"); 405 406 r = vfs_stat(vfs, "/test_mkdir/file", &s); 407 UCX_TEST_ASSERT(r == 0, "stat (2) failed"); 408 409 r = vfs_mkdir(vfs, "/test_mkdir/test_sub"); 410 UCX_TEST_ASSERT(r == 0, "mkdir failed (2)"); 411 412 r = vfs_stat(vfs, "/test_mkdir/test_sub", &s); 413 UCX_TEST_ASSERT(r == 0, "stat (3) failed"); 414 UCX_TEST_ASSERT(S_ISDIR(s.st_mode), "/test_mkdir/test_sub is not a directory"); 415 416 r = vfs_mkdir(vfs, "/test_mkdir/test_sub/test_sub2/"); 417 UCX_TEST_ASSERT(r == 0, "mkdir failed (4)"); 418 419 r = vfs_stat(vfs, "/test_mkdir/test_sub/test_sub2/", &s); 420 UCX_TEST_ASSERT(r == 0, "stat (4) failed"); 421 UCX_TEST_ASSERT(S_ISDIR(s.st_mode), "/test_mkdir/test_sub/test_sub2/ is not a directory"); 422 423 UCX_TEST_END; 424 425 testutil_destroy_session(sn); 426 } 427 428 UCX_TEST(test_pg_vfs_unlink) { 429 Session *sn = testutil_session(); 430 Request *rq = testutil_request(sn->pool, "PUT", "/"); 431 rq->vfs = create_test_pgvfs(sn, rq); 432 VFSContext *vfs = vfs_request_context(sn, rq); 433 434 UCX_TEST_BEGIN; 435 436 SYS_FILE f1 = vfs_open(vfs, "/test_unlink1", O_WRONLY|O_CREAT); 437 UCX_TEST_ASSERT(f1, "cannot create test file"); 438 system_fwrite(f1, "test", 4); 439 440 PgFile *pgfile = f1->data; 441 Oid oid = pgfile->oid; 442 443 vfs_close(f1); 444 445 int r = vfs_unlink(vfs, "/test_unlink1"); 446 UCX_TEST_ASSERT(r == 0, "unlink failed"); 447 448 f1 = vfs_open(vfs, "/test_unlink1", O_RDONLY); 449 UCX_TEST_ASSERT(f1 == NULL, "test file not deleted"); 450 451 PGresult *result = PQexec(test_connection, "savepoint sp;"); 452 PQclear(result); 453 int pgfd = lo_open(test_connection, oid, INV_READ); 454 UCX_TEST_ASSERT(pgfd < 0, "large object not deleted"); 455 result = PQexec(test_connection, "rollback to savepoint sp;"); 456 PQclear(result); 457 458 r = vfs_unlink(vfs, "/test_unlink1"); 459 UCX_TEST_ASSERT(r, "unlink should fail"); 460 461 UCX_TEST_END; 462 463 testutil_destroy_session(sn); 464 } 465 466 UCX_TEST(test_pg_vfs_rmdir) { 467 Session *sn = testutil_session(); 468 Request *rq = testutil_request(sn->pool, "PUT", "/"); 469 rq->vfs = create_test_pgvfs(sn, rq); 470 VFSContext *vfs = vfs_request_context(sn, rq); 471 472 PQexec(test_connection, "delete from Resource where parent_id is not null;"); 473 474 UCX_TEST_BEGIN; 475 476 int r; 477 SYS_FILE f1; 478 479 // prepare some dirs/files 480 r = vfs_mkdir(vfs, "/rmdir_test"); 481 UCX_TEST_ASSERT(r == 0, "mkdir failed (1)"); 482 r = vfs_mkdir(vfs, "/rmdir_test/subdir1"); 483 UCX_TEST_ASSERT(r == 0, "mkdir failed (2)"); 484 r = vfs_mkdir(vfs, "/rmdir_test/subdir2"); 485 UCX_TEST_ASSERT(r == 0, "mkdir failed (3)"); 486 487 f1 = vfs_open(vfs, "/rmdir_test/subdir2/file", O_CREAT|O_WRONLY); 488 UCX_TEST_ASSERT(f1, "open failed"); 489 vfs_close(f1); 490 491 // test rmdir 492 r = vfs_rmdir(vfs, "/rmdir_test/subdir1"); 493 UCX_TEST_ASSERT(r == 0, "rmdir failed");; 494 495 r = vfs_rmdir(vfs, "/rmdir_test/subdir2"); 496 UCX_TEST_ASSERT(r != 0, "rmdir should fail if the dir is not empty"); 497 498 r = vfs_unlink(vfs, "/rmdir_test/subdir2/file"); 499 UCX_TEST_ASSERT(r == 0, "unlink failed"); 500 501 r = vfs_rmdir(vfs, "/rmdir_test/subdir2"); 502 UCX_TEST_ASSERT(r == 0, "rmdir failed 2"); 503 504 UCX_TEST_END; 505 506 testutil_destroy_session(sn); 507 } 508 509 /* ----------------------------- WebDAV tests ----------------------------- */ 510 511 512 static WebdavBackend* create_test_pgdav(Session *sn, Request *rq) { 513 return pg_webdav_create_from_resdata(sn, rq, &test_repo, &resdata); 514 } 515 516 UCX_TEST(test_pg_webdav_create_from_resdata) { 517 Session *sn = testutil_session(); 518 Request *rq = testutil_request(sn->pool, "PROPFIND", "/"); 519 520 UCX_TEST_BEGIN; 521 522 WebdavBackend *dav = create_test_pgdav(sn, rq); 523 UCX_TEST_ASSERT(dav, "cannot create pg dav backend"); 524 525 UCX_TEST_END; 526 } 527 528 UCX_TEST(test_pg_prepare_tests) { 529 Session *sn = testutil_session(); 530 Request *rq = testutil_request(sn->pool, "PUT", "/"); 531 rq->vfs = create_test_pgvfs(sn, rq); 532 VFSContext *vfs = vfs_request_context(sn, rq); 533 534 UCX_TEST_BEGIN; 535 536 vfs_mkdir(vfs, "/propfind"); 537 vfs_mkdir(vfs, "/proppatch"); 538 SYS_FILE f1; 539 540 int64_t res1_id, res2_id; 541 542 f1 = vfs_open(vfs, "/propfind/res1", O_WRONLY|O_CREAT); 543 UCX_TEST_ASSERT(f1, "res1 create failed"); 544 res1_id = ((PgFile*)f1->data)->resource_id; 545 vfs_close(f1); 546 547 f1 = vfs_open(vfs, "/propfind/res2", O_WRONLY|O_CREAT); 548 UCX_TEST_ASSERT(f1, "res2 create failed"); 549 res2_id = ((PgFile*)f1->data)->resource_id; 550 vfs_close(f1); 551 552 f1 = vfs_open(vfs, "/propfind/res3", O_WRONLY|O_CREAT); 553 UCX_TEST_ASSERT(f1, "res3 create failed"); 554 vfs_close(f1); 555 556 int r = vfs_mkdir(vfs, "/propfind/sub"); 557 UCX_TEST_ASSERT(r == 0, "sub create failed"); 558 559 f1 = vfs_open(vfs, "/propfind/sub/res4", O_WRONLY|O_CREAT); 560 UCX_TEST_ASSERT(f1, "res4 create failed"); 561 vfs_close(f1); 562 563 f1 = vfs_open(vfs, "/proppatch/pp1", O_WRONLY|O_CREAT); 564 UCX_TEST_ASSERT(f1, "pp1 create failed"); 565 vfs_close(f1); 566 567 // 2 properties for res1 568 char idstr[32]; 569 snprintf(idstr, 32, "%" PRId64, res1_id); 570 const char* params[1] = { idstr }; 571 PGresult *result = PQexecParams( 572 test_connection, 573 "insert into Property(resource_id, prefix, xmlns, pname, pvalue) values ($1, ''x'', ''http://example.com/'', ''test'', ''testvalue'');", 574 1, // number of parameters 575 NULL, 576 params, // parameter value 577 NULL, 578 NULL, 579 0); // 0: result in text format 580 581 UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1"); 582 PQclear(result); 583 584 result = PQexecParams( 585 test_connection, 586 "insert into Property(resource_id, prefix, xmlns, pname, pvalue) values ($1, ''x'', ''http://example.com/'', ''prop2'', ''value2'');", 587 1, // number of parameters 588 NULL, 589 params, // parameter value 590 NULL, 591 NULL, 592 0); // 0: result in text format 593 594 UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1"); 595 PQclear(result); 596 597 // 1 property for res2 598 snprintf(idstr, 32, "%" PRId64, res2_id); 599 result = PQexecParams( 600 test_connection, 601 "insert into Property(resource_id, prefix, xmlns, pname, pvalue) values ($1, ''x'', ''http://example.com/'', ''test'', ''res2test'');", 602 1, // number of parameters 603 NULL, 604 params, // parameter value 605 NULL, 606 NULL, 607 0); // 0: result in text format 608 609 UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1"); 610 PQclear(result); 611 612 UCX_TEST_END; 613 614 testutil_destroy_session(sn); 615 } 616 617 UCX_TEST(test_pg_webdav_propfind) { 618 Session *sn; 619 Request *rq; 620 TestIOStream *st; 621 pblock *pb; 622 623 UCX_TEST_BEGIN; 624 625 // test data: 626 // 627 // /propfind/ 628 // /propfind/res1 (2 properties: test, prop2) 629 // /propfind/res2 (1 property: test) 630 // /propfind/res3 631 // /propfind/sub 632 // /propfind/sub/res4 633 634 int ret; 635 // Test 1 636 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND1); 637 rq->davCollection = create_test_pgdav(sn, rq); 638 pblock_nvinsert("depth", "0", rq->headers); 639 640 ret = webdav_propfind(pb, sn, rq); 641 642 UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed"); 643 644 TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); 645 UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml"); 646 647 TestResponse *r1 = MAP_GET(ms->responses, "/propfind/"); 648 UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response"); 649 650 UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count"); 651 652 TestProperty *p = MAP_GET(r1->properties, "DAV:resourcetype"); 653 UCX_TEST_ASSERT(p, "propfind1: missing property ''resourcetype''"); 654 UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property ''resourcetype''"); 655 656 p = MAP_GET(r1->properties, "DAV:getlastmodified"); 657 UCX_TEST_ASSERT(p, "propfind1: missing property ''getlastmodified''"); 658 UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property ''getlastmodified''"); 659 660 testutil_destroy_session(sn); 661 test_multistatus_destroy(ms); 662 testutil_iostream_destroy(st); 663 664 665 // Test 2 666 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND2); 667 rq->davCollection = create_test_pgdav(sn, rq); 668 pblock_nvinsert("depth", "1", rq->headers); 669 670 ret = webdav_propfind(pb, sn, rq); 671 672 //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); 673 674 UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed"); 675 676 ms = test_parse_multistatus(st->buf->space, st->buf->size); 677 UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml"); 678 679 r1 = MAP_GET(ms->responses, "/propfind/"); 680 UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response"); 681 682 UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count"); 683 684 r1 = MAP_GET(ms->responses, "/propfind/res2"); 685 UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res2 response"); 686 687 p = MAP_GET(r1->properties, "http://example.com/test"); 688 UCX_TEST_ASSERT(p, "propfind2: missing property ''test''"); 689 UCX_TEST_ASSERT(p->status == 200, "propfind2: wrong status code for property ''test''"); 690 UCX_TEST_ASSERT(!strcmp(p->value, "res2test"), "propfind2: wrong property value"); 691 692 693 testutil_destroy_session(sn); 694 test_multistatus_destroy(ms); 695 testutil_iostream_destroy(st); 696 697 698 699 // Test 3 700 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND2); 701 rq->davCollection = create_test_pgdav(sn, rq); 702 pblock_nvinsert("depth", "infinity", rq->headers); 703 704 ret = webdav_propfind(pb, sn, rq); 705 706 //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); 707 708 UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (3) failed"); 709 710 ms = test_parse_multistatus(st->buf->space, st->buf->size); 711 UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml"); 712 713 r1 = MAP_GET(ms->responses, "/propfind/"); 714 UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response"); 715 716 UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count"); 717 718 719 r1 = MAP_GET(ms->responses, "/propfind/res1"); 720 UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res1 response"); 721 722 p = MAP_GET(r1->properties, "http://example.com/test"); 723 UCX_TEST_ASSERT(p, "propfind3: missing property ''test''"); 724 UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property ''test''"); 725 UCX_TEST_ASSERT(!strcmp(p->value, "testvalue"), "propfind3: wrong property value"); 726 727 p = MAP_GET(r1->properties, "http://example.com/prop2"); 728 UCX_TEST_ASSERT(p, "propfind3: missing property ''prop2''"); 729 UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property ''prop2''"); 730 UCX_TEST_ASSERT(!strcmp(p->value, "value2"), "propfind3: wrong property value"); 731 732 733 r1 = MAP_GET(ms->responses, "/propfind/sub/res4"); 734 UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res4 response"); 735 736 testutil_destroy_session(sn); 737 test_multistatus_destroy(ms); 738 testutil_iostream_destroy(st); 739 740 UCX_TEST_END; 741 } 742 743 744 UCX_TEST(test_pg_webdav_propfind_allprop) { 745 Session *sn; 746 Request *rq; 747 TestIOStream *st; 748 pblock *pb; 749 750 UCX_TEST_BEGIN; 751 752 // test data: 753 // 754 // /propfind/ 755 // /propfind/res1 (2 properties: test, prop2) 756 // /propfind/res2 (1 property: test) 757 // /propfind/res3 758 // /propfind/sub 759 // /propfind/sub/res4 760 761 int ret; 762 TestResponse *r1; 763 TestProperty *p; 764 // Test 1 765 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_ALLPROP); 766 rq->davCollection = create_test_pgdav(sn, rq); 767 pblock_nvinsert("depth", "0", rq->headers); 768 769 ret = webdav_propfind(pb, sn, rq); 770 771 UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed"); 772 773 TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); 774 UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml"); 775 776 r1 = MAP_GET(ms->responses, "/propfind/"); 777 UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response"); 778 UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count"); 779 780 p = MAP_GET(r1->properties, "DAV:resourcetype"); 781 UCX_TEST_ASSERT(r1, "propfind1: missing resourcetype property"); 782 783 testutil_destroy_session(sn); 784 test_multistatus_destroy(ms); 785 testutil_iostream_destroy(st); 786 787 // Test 2 788 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_ALLPROP); 789 rq->davCollection = create_test_pgdav(sn, rq); 790 pblock_nvinsert("depth", "1", rq->headers); 791 792 ret = webdav_propfind(pb, sn, rq); 793 794 //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); 795 796 UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed"); 797 798 ms = test_parse_multistatus(st->buf->space, st->buf->size); 799 UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml"); 800 801 r1 = MAP_GET(ms->responses, "/propfind/"); 802 UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response"); 803 UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count"); 804 805 r1 = MAP_GET(ms->responses, "/propfind/res1"); 806 UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res1 response"); 807 808 p = MAP_GET(r1->properties, "DAV:resourcetype"); 809 UCX_TEST_ASSERT(r1, "propfind2: missing resourcetype property"); 810 p = MAP_GET(r1->properties, "http://example.com/test"); 811 UCX_TEST_ASSERT(r1, "propfind2: missing test property"); 812 p = MAP_GET(r1->properties, "http://example.com/prop2"); 813 UCX_TEST_ASSERT(r1, "propfind2: missing prop2 property"); 814 815 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind2: missing /propfind/res2 response"); 816 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind2: missing /propfind/res3 response"); 817 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind2: missing /propfind/sub response"); 818 819 testutil_destroy_session(sn); 820 test_multistatus_destroy(ms); 821 testutil_iostream_destroy(st); 822 823 // Test 3 824 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_ALLPROP); 825 rq->davCollection = create_test_pgdav(sn, rq); 826 pblock_nvinsert("depth", "infinity", rq->headers); 827 828 ret = webdav_propfind(pb, sn, rq); 829 830 UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed"); 831 832 ms = test_parse_multistatus(st->buf->space, st->buf->size); 833 UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml"); 834 835 r1 = MAP_GET(ms->responses, "/propfind/"); 836 UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response"); 837 UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count"); 838 839 r1 = MAP_GET(ms->responses, "/propfind/res1"); 840 UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/res1 response"); 841 842 p = MAP_GET(r1->properties, "DAV:resourcetype"); 843 UCX_TEST_ASSERT(r1, "propfind3: missing resourcetype property"); 844 p = MAP_GET(r1->properties, "http://example.com/test"); 845 UCX_TEST_ASSERT(r1, "propfind3: missing test property"); 846 p = MAP_GET(r1->properties, "http://example.com/prop2"); 847 UCX_TEST_ASSERT(r1, "propfind3: missing prop2 property"); 848 849 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind3: missing /propfind/res2 response"); 850 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind3: missing /propfind/res3 response"); 851 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind3: missing /propfind/sub response"); 852 UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/res4"), "propfind3: missing /propfind/sub/res4 response"); 853 854 testutil_destroy_session(sn); 855 test_multistatus_destroy(ms); 856 testutil_iostream_destroy(st); 857 858 859 UCX_TEST_END; 860 } 861 862 UCX_TEST(test_pg_webdav_proppatch_set) { 863 Session *sn; 864 Request *rq; 865 TestIOStream *st; 866 pblock *pb; 867 868 UCX_TEST_BEGIN; 869 870 // test data: 871 // 872 // /propfind/ 873 // /propfind/res1 (2 properties: test, prop2) 874 // /propfind/res2 (1 property: test) 875 // /propfind/res3 876 // /propfind/sub 877 // /propfind/sub/res4 878 879 int ret; 880 TestResponse *r1; 881 TestProperty *p; 882 // Test 1 883 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", "/proppatch/pp1", PG_TEST_PROPPATCH1); 884 rq->davCollection = create_test_pgdav(sn, rq); 885 886 ret = webdav_proppatch(pb, sn, rq); 887 UCX_TEST_ASSERT(ret == REQ_PROCEED, "proppatch1 failed"); 888 889 //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); 890 891 TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); 892 UCX_TEST_ASSERT(ms, "proppatch1 response is not valid xml"); 893 894 testutil_destroy_session(sn); 895 test_multistatus_destroy(ms); 896 testutil_iostream_destroy(st); 897 898 // Test 2: xml property value 899 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", "/proppatch/pp1", PG_TEST_PROPPATCH2); 900 rq->davCollection = create_test_pgdav(sn, rq); 901 902 ret = webdav_proppatch(pb, sn, rq); 903 UCX_TEST_ASSERT(ret == REQ_PROCEED, "proppatch2 failed"); 904 905 //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); 906 907 ms = test_parse_multistatus(st->buf->space, st->buf->size); 908 UCX_TEST_ASSERT(ms, "proppatch2 response is not valid xml"); 909 910 testutil_destroy_session(sn); 911 test_multistatus_destroy(ms); 912 testutil_iostream_destroy(st); 913 914 915 UCX_TEST_END; 916 } 917