Sun, 19 Mar 2023 16:48:19 +0100
implement webdav xattr namespace lists
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2022 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <inttypes.h> #include "../../util/util.h" #include "../../test/testutils.h" #include "../../test/webdav.h" #include "../../public/nsapi.h" #include "../../public/webdav.h" #include "../../webdav/webdav.h" #include <cx/string.h> #include <cx/utils.h> #include <cx/buffer.h> #include "pgtest.h" #include "vfs.h" #include "webdav.h" #include <libpq-fe.h> #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) #define MAP_GET(map, key) cxMapGet(map, cx_hash_key_str(key)) #define MAP_PUT(map, key, value) cxMapPut(map, cx_hash_key_str(key), value) static char *pg_connstr = "postgresql://localhost/test1"; static int abort_pg_tests = 0; static PGconn *test_connection; static ResourceData resdata; static PgRepository test_repo; void debug_print_resources(void) { PGresult *result = PQexec(test_connection, "select * from Resource;"); int n = PQntuples(result); printf("\nntuples: %d\n-----------------------------------------------\n", n); printf("%10s %10s %s\n", "resource_id", "parent_id", "nodename"); for(int i=0;i<n;i++) { char *res_id = PQgetvalue(result, i, 0); char *parent_id = PQgetvalue(result, i, 1); char *nodename = PQgetvalue(result, i, 2); printf("%10s %10s %s\n", res_id, parent_id, nodename); } printf("\n"); } static void test_root_lookup(void) { memset(&test_repo, 0, sizeof(PgRepository)); int64_t root_id = -1; int err = pg_lookup_root(&resdata, "root", &root_id); test_repo.root_resource_id = root_id; if(err || root_id < 0) { abort_pg_tests = 1; } } void register_pg_tests(int argc, char **argv, UcxTestSuite *suite) { test_connection = PQconnectdb(pg_connstr); if(!test_connection) { abort_pg_tests = 1; } if(PQstatus(test_connection) != CONNECTION_OK) { abort_pg_tests = 1; } resdata.data = test_connection; test_root_lookup(); ucx_test_register(suite, test_pg_conn); if(!abort_pg_tests) { ucx_test_register(suite, test_pg_lookup_root); ucx_test_register(suite, test_pg_vfs_open); ucx_test_register(suite, test_pg_vfs_io); ucx_test_register(suite, test_pg_vfs_stat); ucx_test_register(suite, test_pg_vfs_mkdir); ucx_test_register(suite, test_pg_vfs_unlink); ucx_test_register(suite, test_pg_vfs_rmdir); ucx_test_register(suite, test_pg_webdav_create_from_resdata); ucx_test_register(suite, test_pg_prepare_tests); ucx_test_register(suite, test_pg_webdav_propfind); ucx_test_register(suite, test_pg_webdav_propfind_allprop); ucx_test_register(suite, test_pg_webdav_proppatch_set); PGresult *result = PQexec(test_connection, "BEGIN"); PQclear(result); } } static void parse_response_tag(TestMultistatus *ms, xmlNode *node) { // thanks to dav for some of this code CxAllocator *a = (CxAllocator*)ms->mp->allocator; node = node->children; cxmutstr href = {NULL, 0}; CxMap *properties = cxHashMapCreate(a, 16); while(node) { if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "href")) { xmlNode *href_node = node->children; if(href_node->type != XML_TEXT_NODE) { return; } href = cx_strdup_a(a, cx_str((const char*)href_node->content)); } else if(xstreq(node->name, "propstat")) { xmlNode *n = node->children; xmlNode *prop_node = NULL; int status_code = 0; while(n) { if(n->type == XML_ELEMENT_NODE) { if(xstreq(n->name, "prop")) { prop_node = n; } else if(xstreq(n->name, "status")) { xmlNode *status_node = n->children; if(status_node->type != XML_TEXT_NODE) { return; } cxmutstr status_str = cx_mutstr((char*)status_node->content); if(status_str.length < 13) { return; } status_str = cx_strsubsl_m(status_str, 9, 3); cxmutstr status_s = cx_strdup(cx_strcast(status_str)); status_code = atoi(status_s.ptr); free(status_s.ptr); } } n = n->next; } n = prop_node->children; while(n) { if(n->type == XML_ELEMENT_NODE) { TestProperty *property = cxCalloc(ms->mp->allocator, 1, sizeof(TestProperty)); if(n->ns) { property->prefix = n->ns->prefix ? cx_strdup_a(a, cx_str((const char*)n->ns->prefix)).ptr : NULL; property->namespace = n->ns->href ? cx_strdup_a(a, cx_str((const char*)n->ns->href)).ptr : NULL; } property->name = cx_strdup_a(a, cx_str((const char*)n->name)).ptr; property->node = n; property->status = status_code; xmlNode *value = n->children; if(value && value->type == XML_TEXT_NODE) { property->value = cx_strdup_a(a, cx_str((const char*)value->content)).ptr; } if(property->namespace && property->name) { cxmutstr pname = cx_strcat(2, cx_str(property->namespace), cx_str(property->name)); cxMapPut(properties, cx_hash_key(pname.ptr, pname.length), property); free(pname.ptr); } } n = n->next; } } } node = node->next; } TestResponse *resp = cxMalloc(a, sizeof(TestResponse)); resp->href = href.ptr; resp->properties = properties; cxMapPut(ms->responses, cx_hash_key(href.ptr, href.length), resp); } TestMultistatus* test_parse_multistatus(const char *space, size_t size) { xmlDoc *doc = xmlReadMemory(space, size, NULL, NULL, 0); if(!doc) { return NULL; } CxMempool *mp = cxBasicMempoolCreate(64); TestMultistatus *ms = cxMalloc(mp->allocator, sizeof(TestMultistatus)); ms->doc = doc; ms->mp = mp; ms->responses = cxHashMapCreate((CxAllocator*)mp->allocator, 8); // parse response xmlNode *xml_root = xmlDocGetRootElement(doc); xmlNode *node = xml_root->children; while(node) { if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "response")) { parse_response_tag(ms, node); } } node = node->next; } return ms; } void test_multistatus_destroy(TestMultistatus *ms) { if(!ms) return; xmlFreeDoc(ms->doc); cxMempoolDestroy(ms->mp); } UCX_TEST(test_pg_conn) { char *msg = test_connection ? PQerrorMessage(test_connection) : "no connection"; UCX_TEST_BEGIN; if(abort_pg_tests) { int msglen = strlen(msg); if(msglen > 0 && msg[msglen-1] == '\n') { msglen--; } fprintf(stdout, "%.*s: ", msglen, msg); UCX_TEST_ASSERT(1 == 0, "skip pg tests"); } else { UCX_TEST_ASSERT(1 == 1, "ok"); } UCX_TEST_END; } UCX_TEST(test_pg_lookup_root) { UCX_TEST_BEGIN; // test already done in test_root_lookup() UCX_TEST_ASSERT(!abort_pg_tests, "Lookup failed"); UCX_TEST_END; } static VFS* create_test_pgvfs(Session *sn, Request *rq) { return pg_vfs_create_from_resourcedata(sn, rq, &test_repo, &resdata); } UCX_TEST(test_pg_vfs_open) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); SYS_FILE file; UCX_TEST_BEGIN; file = vfs_open(vfs, "/test_notfound1", O_RDONLY); UCX_TEST_ASSERT(!file, "/test_notfound should not exist"); file = vfs_open(vfs, "/test_file1", O_RDWR | O_CREAT); UCX_TEST_ASSERT(file, "cannot create file 1"); vfs_close(file); UCX_TEST_END; testutil_destroy_session(sn); } UCX_TEST(test_pg_vfs_io) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); SYS_FILE file; SYS_FILE file2; UCX_TEST_BEGIN; file = vfs_open(vfs, "/test_f1", O_WRONLY | O_CREAT); UCX_TEST_ASSERT(file, "cannot open file1"); int w = system_fwrite(file, "test1\n", 6); UCX_TEST_ASSERT(w == 6, "fwrite ret (1)"); w = system_fwrite(file, "2", 1); UCX_TEST_ASSERT(w == 1, "fwrite ret (2)"); vfs_close(file); file = vfs_open(vfs, "/test_f1", O_RDONLY); file2 = vfs_open(vfs, "/test_f2", O_WRONLY | O_CREAT); UCX_TEST_ASSERT(file, "cannot open file1"); UCX_TEST_ASSERT(file2, "cannot open file2"); char buf[128]; int r = system_fread(file, buf, 128); UCX_TEST_ASSERT(r == 7, "cannot read from file1"); w = system_fwrite(file2, buf, r); UCX_TEST_ASSERT(w == 7, "cannot write to file2"); vfs_close(file); vfs_close(file2); file2 = vfs_open(vfs, "/test_f2", O_RDONLY); r = system_fread(file, buf, 128); UCX_TEST_ASSERT(r == 7, "fread ret"); UCX_TEST_ASSERT(!memcmp(buf, "test1\n2", 7), "wrong buffer content after read"); vfs_close(file2); UCX_TEST_END; testutil_destroy_session(sn); } UCX_TEST(test_pg_vfs_stat) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); UCX_TEST_BEGIN; // testdata, content doesn't matter char test1[512]; memset(test1, 'x', 512); const int test_len1 = 200; const int test_len2 = 432; SYS_FILE f1 = vfs_open(vfs, "/test_s1", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "cannot open test_s1"); system_fwrite(f1, test1, test_len1); vfs_close(f1); SYS_FILE f2 = vfs_open(vfs, "/test_s2", O_RDWR|O_CREAT); UCX_TEST_ASSERT(f2, "cannot open test_s2"); system_fwrite(f2, test1, test_len2); vfs_close(f2); struct stat st1, st2; int r1 = vfs_stat(vfs, "/test_s1", &st1); int r2 = vfs_stat(vfs, "/test_s2", &st2); UCX_TEST_ASSERT(r1 == 0, "stat1 failed"); UCX_TEST_ASSERT(r2 == 0, "stat2 failed"); UCX_TEST_ASSERT(st1.st_size == test_len1, "s1 wrong length"); UCX_TEST_ASSERT(st2.st_size == test_len2, "s2 wrong length"); int testfail = vfs_stat(vfs, "/test_stat_fail", &st1); UCX_TEST_ASSERT(testfail != 0, "stat 3 should fail"); UCX_TEST_END; testutil_destroy_session(sn); } UCX_TEST(test_pg_vfs_mkdir) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); UCX_TEST_BEGIN; struct stat s; SYS_FILE f1 = vfs_open(vfs, "/test_mkdir/file", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1 == NULL, "open should fail"); int r = vfs_mkdir(vfs, "/test_mkdir"); UCX_TEST_ASSERT(r == 0, "mkdir failed"); r = vfs_stat(vfs, "/test_mkdir", &s); UCX_TEST_ASSERT(r == 0, "stat (1) failed"); UCX_TEST_ASSERT(S_ISDIR(s.st_mode), "/test_mkdir is not a directory"); f1 = vfs_open(vfs, "/test_mkdir/file", O_WRONLY|O_CREAT); vfs_close(f1); UCX_TEST_ASSERT(f1, "open failed"); r = vfs_stat(vfs, "/test_mkdir/file", &s); UCX_TEST_ASSERT(r == 0, "stat (2) failed"); r = vfs_mkdir(vfs, "/test_mkdir/test_sub"); UCX_TEST_ASSERT(r == 0, "mkdir failed (2)"); r = vfs_stat(vfs, "/test_mkdir/test_sub", &s); UCX_TEST_ASSERT(r == 0, "stat (3) failed"); UCX_TEST_ASSERT(S_ISDIR(s.st_mode), "/test_mkdir/test_sub is not a directory"); r = vfs_mkdir(vfs, "/test_mkdir/test_sub/test_sub2/"); UCX_TEST_ASSERT(r == 0, "mkdir failed (4)"); r = vfs_stat(vfs, "/test_mkdir/test_sub/test_sub2/", &s); UCX_TEST_ASSERT(r == 0, "stat (4) failed"); UCX_TEST_ASSERT(S_ISDIR(s.st_mode), "/test_mkdir/test_sub/test_sub2/ is not a directory"); UCX_TEST_END; testutil_destroy_session(sn); } UCX_TEST(test_pg_vfs_unlink) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); UCX_TEST_BEGIN; SYS_FILE f1 = vfs_open(vfs, "/test_unlink1", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "cannot create test file"); system_fwrite(f1, "test", 4); PgFile *pgfile = f1->data; Oid oid = pgfile->oid; vfs_close(f1); int r = vfs_unlink(vfs, "/test_unlink1"); UCX_TEST_ASSERT(r == 0, "unlink failed"); f1 = vfs_open(vfs, "/test_unlink1", O_RDONLY); UCX_TEST_ASSERT(f1 == NULL, "test file not deleted"); PGresult *result = PQexec(test_connection, "savepoint sp;"); PQclear(result); int pgfd = lo_open(test_connection, oid, INV_READ); UCX_TEST_ASSERT(pgfd < 0, "large object not deleted"); result = PQexec(test_connection, "rollback to savepoint sp;"); PQclear(result); r = vfs_unlink(vfs, "/test_unlink1"); UCX_TEST_ASSERT(r, "unlink should fail"); UCX_TEST_END; testutil_destroy_session(sn); } UCX_TEST(test_pg_vfs_rmdir) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); PQexec(test_connection, "delete from Resource where parent_id is not null;"); UCX_TEST_BEGIN; int r; SYS_FILE f1; // prepare some dirs/files r = vfs_mkdir(vfs, "/rmdir_test"); UCX_TEST_ASSERT(r == 0, "mkdir failed (1)"); r = vfs_mkdir(vfs, "/rmdir_test/subdir1"); UCX_TEST_ASSERT(r == 0, "mkdir failed (2)"); r = vfs_mkdir(vfs, "/rmdir_test/subdir2"); UCX_TEST_ASSERT(r == 0, "mkdir failed (3)"); f1 = vfs_open(vfs, "/rmdir_test/subdir2/file", O_CREAT|O_WRONLY); UCX_TEST_ASSERT(f1, "open failed"); vfs_close(f1); // test rmdir r = vfs_rmdir(vfs, "/rmdir_test/subdir1"); UCX_TEST_ASSERT(r == 0, "rmdir failed");; r = vfs_rmdir(vfs, "/rmdir_test/subdir2"); UCX_TEST_ASSERT(r != 0, "rmdir should fail if the dir is not empty"); r = vfs_unlink(vfs, "/rmdir_test/subdir2/file"); UCX_TEST_ASSERT(r == 0, "unlink failed"); r = vfs_rmdir(vfs, "/rmdir_test/subdir2"); UCX_TEST_ASSERT(r == 0, "rmdir failed 2"); UCX_TEST_END; testutil_destroy_session(sn); } /* ----------------------------- WebDAV tests ----------------------------- */ static WebdavBackend* create_test_pgdav(Session *sn, Request *rq) { return pg_webdav_create_from_resdata(sn, rq, &test_repo, &resdata); } UCX_TEST(test_pg_webdav_create_from_resdata) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PROPFIND", "/"); UCX_TEST_BEGIN; WebdavBackend *dav = create_test_pgdav(sn, rq); UCX_TEST_ASSERT(dav, "cannot create pg dav backend"); UCX_TEST_END; } UCX_TEST(test_pg_prepare_tests) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PUT", "/"); rq->vfs = create_test_pgvfs(sn, rq); VFSContext *vfs = vfs_request_context(sn, rq); UCX_TEST_BEGIN; vfs_mkdir(vfs, "/propfind"); vfs_mkdir(vfs, "/proppatch"); SYS_FILE f1; int64_t res1_id, res2_id; f1 = vfs_open(vfs, "/propfind/res1", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "res1 create failed"); res1_id = ((PgFile*)f1->data)->resource_id; vfs_close(f1); f1 = vfs_open(vfs, "/propfind/res2", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "res2 create failed"); res2_id = ((PgFile*)f1->data)->resource_id; vfs_close(f1); f1 = vfs_open(vfs, "/propfind/res3", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "res3 create failed"); vfs_close(f1); int r = vfs_mkdir(vfs, "/propfind/sub"); UCX_TEST_ASSERT(r == 0, "sub create failed"); f1 = vfs_open(vfs, "/propfind/sub/res4", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "res4 create failed"); vfs_close(f1); f1 = vfs_open(vfs, "/proppatch/pp1", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "pp1 create failed"); vfs_close(f1); // 2 properties for res1 char idstr[32]; snprintf(idstr, 32, "%" PRId64, res1_id); const char* params[1] = { idstr }; PGresult *result = PQexecParams( test_connection, "insert into Property(resource_id, prefix, xmlns, pname, pvalue) values ($1, 'x', 'http://example.com/', 'test', 'testvalue');", 1, // number of parameters NULL, params, // parameter value NULL, NULL, 0); // 0: result in text format UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1"); PQclear(result); result = PQexecParams( test_connection, "insert into Property(resource_id, prefix, xmlns, pname, pvalue) values ($1, 'x', 'http://example.com/', 'prop2', 'value2');", 1, // number of parameters NULL, params, // parameter value NULL, NULL, 0); // 0: result in text format UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1"); PQclear(result); // 1 property for res2 snprintf(idstr, 32, "%" PRId64, res2_id); result = PQexecParams( test_connection, "insert into Property(resource_id, prefix, xmlns, pname, pvalue) values ($1, 'x', 'http://example.com/', 'test', 'res2test');", 1, // number of parameters NULL, params, // parameter value NULL, NULL, 0); // 0: result in text format UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1"); PQclear(result); UCX_TEST_END; testutil_destroy_session(sn); } UCX_TEST(test_pg_webdav_propfind) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; UCX_TEST_BEGIN; // test data: // // /propfind/ // /propfind/res1 (2 properties: test, prop2) // /propfind/res2 (1 property: test) // /propfind/res3 // /propfind/sub // /propfind/sub/res4 int ret; // Test 1 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND1); rq->davCollection = create_test_pgdav(sn, rq); pblock_nvinsert("depth", "0", rq->headers); ret = webdav_propfind(pb, sn, rq); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed"); TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml"); TestResponse *r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response"); UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count"); TestProperty *p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(p, "propfind1: missing property 'resourcetype'"); UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'resourcetype'"); p = MAP_GET(r1->properties, "DAV:getlastmodified"); UCX_TEST_ASSERT(p, "propfind1: missing property 'getlastmodified'"); UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'getlastmodified'"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); // Test 2 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND2); rq->davCollection = create_test_pgdav(sn, rq); pblock_nvinsert("depth", "1", rq->headers); ret = webdav_propfind(pb, sn, rq); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed"); ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml"); r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response"); UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count"); r1 = MAP_GET(ms->responses, "/propfind/res2"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res2 response"); p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(p, "propfind2: missing property 'test'"); UCX_TEST_ASSERT(p->status == 200, "propfind2: wrong status code for property 'test'"); UCX_TEST_ASSERT(!strcmp(p->value, "res2test"), "propfind2: wrong property value"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); // Test 3 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND2); rq->davCollection = create_test_pgdav(sn, rq); pblock_nvinsert("depth", "infinity", rq->headers); ret = webdav_propfind(pb, sn, rq); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (3) failed"); ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml"); r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response"); UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count"); r1 = MAP_GET(ms->responses, "/propfind/res1"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res1 response"); p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(p, "propfind3: missing property 'test'"); UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property 'test'"); UCX_TEST_ASSERT(!strcmp(p->value, "testvalue"), "propfind3: wrong property value"); p = MAP_GET(r1->properties, "http://example.com/prop2"); UCX_TEST_ASSERT(p, "propfind3: missing property 'prop2'"); UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property 'prop2'"); UCX_TEST_ASSERT(!strcmp(p->value, "value2"), "propfind3: wrong property value"); r1 = MAP_GET(ms->responses, "/propfind/sub/res4"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res4 response"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); UCX_TEST_END; } UCX_TEST(test_pg_webdav_propfind_allprop) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; UCX_TEST_BEGIN; // test data: // // /propfind/ // /propfind/res1 (2 properties: test, prop2) // /propfind/res2 (1 property: test) // /propfind/res3 // /propfind/sub // /propfind/sub/res4 int ret; TestResponse *r1; TestProperty *p; // Test 1 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_ALLPROP); rq->davCollection = create_test_pgdav(sn, rq); pblock_nvinsert("depth", "0", rq->headers); ret = webdav_propfind(pb, sn, rq); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed"); TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml"); r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response"); UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count"); p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(r1, "propfind1: missing resourcetype property"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); // Test 2 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_ALLPROP); rq->davCollection = create_test_pgdav(sn, rq); pblock_nvinsert("depth", "1", rq->headers); ret = webdav_propfind(pb, sn, rq); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed"); ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml"); r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response"); UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count"); r1 = MAP_GET(ms->responses, "/propfind/res1"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res1 response"); p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(r1, "propfind2: missing resourcetype property"); p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(r1, "propfind2: missing test property"); p = MAP_GET(r1->properties, "http://example.com/prop2"); UCX_TEST_ASSERT(r1, "propfind2: missing prop2 property"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind2: missing /propfind/res2 response"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind2: missing /propfind/res3 response"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind2: missing /propfind/sub response"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); // Test 3 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_ALLPROP); rq->davCollection = create_test_pgdav(sn, rq); pblock_nvinsert("depth", "infinity", rq->headers); ret = webdav_propfind(pb, sn, rq); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed"); ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml"); r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response"); UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count"); r1 = MAP_GET(ms->responses, "/propfind/res1"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/res1 response"); p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(r1, "propfind3: missing resourcetype property"); p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(r1, "propfind3: missing test property"); p = MAP_GET(r1->properties, "http://example.com/prop2"); UCX_TEST_ASSERT(r1, "propfind3: missing prop2 property"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind3: missing /propfind/res2 response"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind3: missing /propfind/res3 response"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind3: missing /propfind/sub response"); UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/res4"), "propfind3: missing /propfind/sub/res4 response"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); UCX_TEST_END; } UCX_TEST(test_pg_webdav_proppatch_set) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; UCX_TEST_BEGIN; // test data: // // /propfind/ // /propfind/res1 (2 properties: test, prop2) // /propfind/res2 (1 property: test) // /propfind/res3 // /propfind/sub // /propfind/sub/res4 int ret; TestResponse *r1; TestProperty *p; // Test 1 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", "/proppatch/pp1", PG_TEST_PROPPATCH1); rq->davCollection = create_test_pgdav(sn, rq); ret = webdav_proppatch(pb, sn, rq); UCX_TEST_ASSERT(ret == REQ_PROCEED, "proppatch1 failed"); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "proppatch1 response is not valid xml"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); // Test 2: xml property value init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", "/proppatch/pp1", PG_TEST_PROPPATCH2); rq->davCollection = create_test_pgdav(sn, rq); ret = webdav_proppatch(pb, sn, rq); UCX_TEST_ASSERT(ret == REQ_PROCEED, "proppatch2 failed"); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "proppatch2 response is not valid xml"); testutil_destroy_session(sn); test_multistatus_destroy(ms); testutil_iostream_destroy(st); UCX_TEST_END; }