Mon, 25 Apr 2022 21:40:26 +0200
add pg propfind cleanup
/* * 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 <ucx/string.h> #include <ucx/utils.h> #include <ucx/buffer.h> #include "pgtest.h" #include "vfs.h" #include "webdav.h" #include <libpq-fe.h> #include <libxml/tree.h> static char *pg_connstr = "postgresql://localhost/test1"; static int abort_pg_tests = 0; static PGconn *test_connection; static ResourceData resdata; 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"); } 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; } ucx_test_register(suite, test_pg_conn); if(!abort_pg_tests) { resdata.data = test_connection; 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); PGresult *result = PQexec(test_connection, "BEGIN"); PQclear(result); } } 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; } static VFS* create_test_pgvfs(Session *sn, Request *rq) { return pg_vfs_create_from_resourcedata(sn, rq, &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; 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"); f1 = vfs_open(vfs, "/test_mkdir/file", O_WRONLY|O_CREAT); UCX_TEST_ASSERT(f1, "open failed"); r = vfs_mkdir(vfs, "/test_mkdir/test_sub"); UCX_TEST_ASSERT(r == 0, "mkdir failed (2)"); 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, &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"); 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); // 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, xmlns, pname, pvalue) values ($1, '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, xmlns, pname, pvalue) values ($1, '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, xmlns, pname, pvalue) values ($1, '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) // /propfind/res2 (1 property) // /propfind/res3 int ret; // Test 1 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND1); rq->davCollection = create_test_pgdav(sn, rq); ret = webdav_propfind(pb, sn, rq); UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed"); xmlDoc *doc = xmlReadMemory( st->buf->space, st->buf->size, NULL, NULL, 0); UCX_TEST_ASSERT(doc, "propfind1: response is not valid xml"); printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); testutil_destroy_session(sn); xmlFreeDoc(doc); testutil_iostream_destroy(st); UCX_TEST_END; }