src/server/plugins/postgresql/pgtest.c

Sun, 24 Apr 2022 18:35:44 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 24 Apr 2022 18:35:44 +0200
branch
webdav
changeset 306
e03737cea6e2
parent 298
8f5c556120a5
child 307
8787cb5ebab3
permissions
-rw-r--r--

add semi functional pg propfind handler

/*
 * 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;
}

mercurial