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