chore: add CMake test infrastructure and placeholder unit test files for future coverage

This commit is contained in:
2025-11-28 05:41:42 +00:00
parent 4afdcf567b
commit 53a6179ede
38 changed files with 5192 additions and 21 deletions
@@ -0,0 +1,155 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_copy)
TEST(copy_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
const char *content = "original content";
storage_write_file(ctx->fixture->storage, "/source.txt", content, strlen(content));
http_request_init(&ctx->request);
strncpy(ctx->request.method, "COPY", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/source.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dest.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/dest.txt");
ASSERT_TRUE(exists);
exists = storage_exists(ctx->fixture->storage, "/source.txt");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(copy_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/srcfolder");
storage_write_file(ctx->fixture->storage, "/srcfolder/file1.txt", "a", 1);
storage_write_file(ctx->fixture->storage, "/srcfolder/file2.txt", "b", 1);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "COPY", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/srcfolder", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dstfolder");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/dstfolder");
ASSERT_TRUE(exists);
exists = storage_exists(ctx->fixture->storage, "/dstfolder/file1.txt");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(copy_source_not_found) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "COPY", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/nonexistent.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dest.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(copy_missing_destination_header) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/source.txt", "data", 4);
endpoint_test_execute(ctx, "COPY", "/source.txt", NULL, 0);
ASSERT_EQUAL(HTTP_400_BAD_REQUEST, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(copy_overwrite_true) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/src.txt", "source", 6);
storage_write_file(ctx->fixture->storage, "/dst.txt", "destination", 11);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "COPY", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/src.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dst.txt");
http_request_add_header(&ctx->request, "Overwrite", "T");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(copy_overwrite_false_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/src.txt", "source", 6);
storage_write_file(ctx->fixture->storage, "/dst.txt", "destination", 11);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "COPY", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/src.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dst.txt");
http_request_add_header(&ctx->request, "Overwrite", "F");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_412_PRECONDITION_FAILED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(copy_dest_parent_missing) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/src.txt", "data", 4);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "COPY", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/src.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/missing/dst.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_409_CONFLICT, ctx->response.status);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,132 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_delete)
TEST(delete_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/todelete.txt", "content", 7);
endpoint_test_execute(ctx, "DELETE", "/todelete.txt", NULL, 0);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/todelete.txt");
ASSERT_FALSE(exists);
endpoint_test_destroy(ctx);
}
TEST(delete_empty_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/emptyfolder");
endpoint_test_execute(ctx, "DELETE", "/emptyfolder", NULL, 0);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/emptyfolder");
ASSERT_FALSE(exists);
endpoint_test_destroy(ctx);
}
TEST(delete_collection_with_contents) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
storage_write_file(ctx->fixture->storage, "/folder/file1.txt", "a", 1);
storage_write_file(ctx->fixture->storage, "/folder/file2.txt", "b", 1);
storage_create_collection(ctx->fixture->storage, "/folder/sub");
storage_write_file(ctx->fixture->storage, "/folder/sub/deep.txt", "c", 1);
endpoint_test_execute(ctx, "DELETE", "/folder", NULL, 0);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/folder");
ASSERT_FALSE(exists);
endpoint_test_destroy(ctx);
}
TEST(delete_nonexistent_returns_404) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "DELETE", "/doesnotexist.txt", NULL, 0);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(delete_root_not_allowed) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "DELETE", "/", NULL, 0);
ASSERT_NOT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(delete_locked_resource_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/locked.txt", "data", 4);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/locked.txt", &lock);
endpoint_test_execute(ctx, "DELETE", "/locked.txt", NULL, 0);
ASSERT_EQUAL(HTTP_423_LOCKED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(delete_with_valid_lock_token) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/locked2.txt", "data", 4);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/locked2.txt", &lock);
char if_header[256];
snprintf(if_header, sizeof(if_header), "(<%s>)", lock.token);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "DELETE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/locked2.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "If", if_header);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
endpoint_test_destroy(ctx);
}
+132
View File
@@ -0,0 +1,132 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_get)
TEST(get_existing_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
const char *content = "Hello, World!";
storage_write_file(ctx->fixture->storage, "/hello.txt", content, strlen(content));
endpoint_test_execute(ctx, "GET", "/hello.txt", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_NOT_NULL(ctx->response.body);
ASSERT_EQUAL(strlen(content), ctx->response.body_len);
ASSERT_MEM_EQ(content, ctx->response.body, strlen(content));
endpoint_test_destroy(ctx);
}
TEST(get_nonexistent_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "GET", "/nonexistent.txt", NULL, 0);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(get_collection_returns_listing) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
storage_write_file(ctx->fixture->storage, "/folder/file1.txt", "a", 1);
storage_write_file(ctx->fixture->storage, "/folder/file2.txt", "b", 1);
endpoint_test_execute(ctx, "GET", "/folder/", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(get_sets_content_type) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/test.html", "<html></html>", 13);
endpoint_test_execute(ctx, "GET", "/test.html", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_TRUE(endpoint_response_has_header(ctx, "Content-Type", NULL));
endpoint_test_destroy(ctx);
}
TEST(get_sets_content_length) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
const char *content = "exact length test";
storage_write_file(ctx->fixture->storage, "/length.txt", content, strlen(content));
endpoint_test_execute(ctx, "GET", "/length.txt", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
const char *cl = endpoint_response_get_header(ctx, "Content-Length");
ASSERT_NOT_NULL(cl);
ASSERT_EQUAL((int)strlen(content), atoi(cl));
endpoint_test_destroy(ctx);
}
TEST(get_binary_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
unsigned char binary[] = {0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD};
storage_write_file(ctx->fixture->storage, "/binary.bin", (char *)binary, sizeof(binary));
endpoint_test_execute(ctx, "GET", "/binary.bin", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_EQUAL(sizeof(binary), ctx->response.body_len);
ASSERT_MEM_EQ(binary, ctx->response.body, sizeof(binary));
endpoint_test_destroy(ctx);
}
TEST(get_empty_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/empty.txt", "", 0);
endpoint_test_execute(ctx, "GET", "/empty.txt", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_EQUAL(0, ctx->response.body_len);
endpoint_test_destroy(ctx);
}
TEST(get_root_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "GET", "/", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(get_path_traversal_blocked) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "GET", "/../etc/passwd", NULL, 0);
ASSERT_NOT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,229 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_lock)
TEST(lock_file_exclusive) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/file.txt", "content", 7);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<lockinfo xmlns=\"DAV:\">"
"<lockscope><exclusive/></lockscope>"
"<locktype><write/></locktype>"
"<owner><href>http://example.com/user</href></owner>"
"</lockinfo>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/file.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
http_request_add_header(&ctx->request, "Timeout", "Second-3600");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_TRUE(endpoint_response_has_header(ctx, "Lock-Token", NULL));
endpoint_test_destroy(ctx);
}
TEST(lock_file_shared) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/shared.txt", "x", 1);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<lockinfo xmlns=\"DAV:\">"
"<lockscope><shared/></lockscope>"
"<locktype><write/></locktype>"
"</lockinfo>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/shared.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(lock_collection_depth_infinity) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
storage_write_file(ctx->fixture->storage, "/folder/child.txt", "x", 1);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<lockinfo xmlns=\"DAV:\">"
"<lockscope><exclusive/></lockscope>"
"<locktype><write/></locktype>"
"</lockinfo>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/folder", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "infinity");
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(lock_not_found) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<lockinfo xmlns=\"DAV:\">"
"<lockscope><exclusive/></lockscope>"
"<locktype><write/></locktype>"
"</lockinfo>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/nonexistent.txt", sizeof(ctx->request.uri) - 1);
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(lock_already_locked_exclusive) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/locked.txt", "x", 1);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/locked.txt", &lock);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<lockinfo xmlns=\"DAV:\">"
"<lockscope><exclusive/></lockscope>"
"<locktype><write/></locktype>"
"</lockinfo>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/locked.txt", sizeof(ctx->request.uri) - 1);
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_423_LOCKED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(lock_refresh) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/refresh.txt", "x", 1);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 100;
lock_manager_acquire(ctx->fixture->locks, "/refresh.txt", &lock);
char if_header[256];
snprintf(if_header, sizeof(if_header), "(<%s>)", lock.token);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/refresh.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "If", if_header);
http_request_add_header(&ctx->request, "Timeout", "Second-7200");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(lock_response_contains_lockdiscovery) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/discover.txt", "x", 1);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<lockinfo xmlns=\"DAV:\">"
"<lockscope><exclusive/></lockscope>"
"<locktype><write/></locktype>"
"</lockinfo>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "LOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/discover.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_NOT_NULL(ctx->response.body);
ASSERT_TRUE(strstr(ctx->response.body, "lockdiscovery") != NULL);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,113 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_mkcol)
TEST(mkcol_create_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "MKCOL", "/newcollection", NULL, 0);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/newcollection");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(mkcol_nested_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/parent");
endpoint_test_execute(ctx, "MKCOL", "/parent/child", NULL, 0);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/parent/child");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(mkcol_parent_missing_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "MKCOL", "/missing/child", NULL, 0);
ASSERT_EQUAL(HTTP_409_CONFLICT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(mkcol_already_exists_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/existing");
endpoint_test_execute(ctx, "MKCOL", "/existing", NULL, 0);
ASSERT_EQUAL(HTTP_405_METHOD_NOT_ALLOWED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(mkcol_file_exists_at_path_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/isfile", "content", 7);
endpoint_test_execute(ctx, "MKCOL", "/isfile", NULL, 0);
ASSERT_EQUAL(HTTP_405_METHOD_NOT_ALLOWED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(mkcol_with_body_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "MKCOL", "/withbody", "some body", 9);
ASSERT_EQUAL(HTTP_415_UNSUPPORTED_MEDIA_TYPE, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(mkcol_root_not_allowed) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "MKCOL", "/", NULL, 0);
ASSERT_NOT_EQUAL(HTTP_201_CREATED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(mkcol_deep_nesting) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/a");
storage_create_collection(ctx->fixture->storage, "/a/b");
storage_create_collection(ctx->fixture->storage, "/a/b/c");
endpoint_test_execute(ctx, "MKCOL", "/a/b/c/d", NULL, 0);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/a/b/c/d");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,209 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_move)
TEST(move_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/original.txt", "content", 7);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/original.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/moved.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/moved.txt");
ASSERT_TRUE(exists);
exists = storage_exists(ctx->fixture->storage, "/original.txt");
ASSERT_FALSE(exists);
endpoint_test_destroy(ctx);
}
TEST(move_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/oldfolder");
storage_write_file(ctx->fixture->storage, "/oldfolder/file.txt", "data", 4);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/oldfolder", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/newfolder");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/newfolder");
ASSERT_TRUE(exists);
exists = storage_exists(ctx->fixture->storage, "/newfolder/file.txt");
ASSERT_TRUE(exists);
exists = storage_exists(ctx->fixture->storage, "/oldfolder");
ASSERT_FALSE(exists);
endpoint_test_destroy(ctx);
}
TEST(move_source_not_found) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/nonexistent.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dest.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(move_missing_destination_header) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/source.txt", "data", 4);
endpoint_test_execute(ctx, "MOVE", "/source.txt", NULL, 0);
ASSERT_EQUAL(HTTP_400_BAD_REQUEST, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(move_overwrite_true) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/src.txt", "source", 6);
storage_write_file(ctx->fixture->storage, "/dst.txt", "destination", 11);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/src.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dst.txt");
http_request_add_header(&ctx->request, "Overwrite", "T");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(move_overwrite_false_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/src.txt", "source", 6);
storage_write_file(ctx->fixture->storage, "/dst.txt", "destination", 11);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/src.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/dst.txt");
http_request_add_header(&ctx->request, "Overwrite", "F");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_412_PRECONDITION_FAILED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(move_dest_parent_missing) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/src.txt", "data", 4);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/src.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/missing/dst.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_409_CONFLICT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(move_locked_source_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/locked.txt", "data", 4);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/locked.txt", &lock);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/locked.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/unlocked.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_423_LOCKED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(move_rename_in_place) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
storage_write_file(ctx->fixture->storage, "/folder/old.txt", "data", 4);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "MOVE", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/folder/old.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Destination", "/folder/new.txt");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/folder/new.txt");
ASSERT_TRUE(exists);
exists = storage_exists(ctx->fixture->storage, "/folder/old.txt");
ASSERT_FALSE(exists);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,83 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_options)
TEST(options_returns_200) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "OPTIONS", "/", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(options_includes_dav_header) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "OPTIONS", "/", NULL, 0);
ASSERT_TRUE(endpoint_response_has_header(ctx, "DAV", NULL));
endpoint_test_destroy(ctx);
}
TEST(options_includes_allow_header) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "OPTIONS", "/", NULL, 0);
const char *allow = endpoint_response_get_header(ctx, "Allow");
ASSERT_NOT_NULL(allow);
ASSERT_TRUE(strstr(allow, "GET") != NULL);
ASSERT_TRUE(strstr(allow, "PUT") != NULL);
ASSERT_TRUE(strstr(allow, "DELETE") != NULL);
ASSERT_TRUE(strstr(allow, "PROPFIND") != NULL);
ASSERT_TRUE(strstr(allow, "MKCOL") != NULL);
endpoint_test_destroy(ctx);
}
TEST(options_star_uri) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "OPTIONS", "*", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(options_on_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/testcol");
endpoint_test_execute(ctx, "OPTIONS", "/testcol", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
ASSERT_TRUE(endpoint_response_has_header(ctx, "DAV", NULL));
endpoint_test_destroy(ctx);
}
TEST(options_on_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/testfile.txt", "data", 4);
endpoint_test_execute(ctx, "OPTIONS", "/testfile.txt", NULL, 0);
ASSERT_EQUAL(HTTP_200_OK, ctx->response.status);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,185 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_propfind)
TEST(propfind_file_depth_0) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/test.txt", "content", 7);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/test.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "0");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
ASSERT_NOT_NULL(ctx->response.body);
ASSERT_TRUE(strstr(ctx->response.body, "multistatus") != NULL);
endpoint_test_destroy(ctx);
}
TEST(propfind_collection_depth_0) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/folder", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "0");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(propfind_collection_depth_1) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
storage_write_file(ctx->fixture->storage, "/folder/file1.txt", "a", 1);
storage_write_file(ctx->fixture->storage, "/folder/file2.txt", "b", 1);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/folder", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "1");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
ASSERT_TRUE(strstr(ctx->response.body, "file1.txt") != NULL);
ASSERT_TRUE(strstr(ctx->response.body, "file2.txt") != NULL);
endpoint_test_destroy(ctx);
}
TEST(propfind_collection_depth_infinity) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/root");
storage_create_collection(ctx->fixture->storage, "/root/sub");
storage_write_file(ctx->fixture->storage, "/root/sub/deep.txt", "x", 1);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/root", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "infinity");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
ASSERT_TRUE(strstr(ctx->response.body, "deep.txt") != NULL);
endpoint_test_destroy(ctx);
}
TEST(propfind_not_found) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/nonexistent", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "0");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(propfind_allprop) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/props.txt", "content", 7);
const char *body = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propfind xmlns=\"DAV:\"><allprop/></propfind>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/props.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "0");
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
ASSERT_TRUE(strstr(ctx->response.body, "getcontentlength") != NULL);
endpoint_test_destroy(ctx);
}
TEST(propfind_propname) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/propname.txt", "x", 1);
const char *body = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propfind xmlns=\"DAV:\"><propname/></propfind>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/propname.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "0");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(propfind_root) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPFIND", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Depth", "0");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,190 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_proppatch)
TEST(proppatch_set_property) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/file.txt", "content", 7);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propertyupdate xmlns=\"DAV:\">"
"<set><prop>"
"<displayname>My File</displayname>"
"</prop></set>"
"</propertyupdate>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPPATCH", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/file.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(proppatch_remove_property) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/file.txt", "content", 7);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propertyupdate xmlns=\"DAV:\">"
"<remove><prop>"
"<displayname/>"
"</prop></remove>"
"</propertyupdate>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPPATCH", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/file.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(proppatch_not_found) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propertyupdate xmlns=\"DAV:\">"
"<set><prop><displayname>x</displayname></prop></set>"
"</propertyupdate>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPPATCH", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/nonexistent.txt", sizeof(ctx->request.uri) - 1);
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(proppatch_custom_namespace) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/custom.txt", "x", 1);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propertyupdate xmlns=\"DAV:\" xmlns:custom=\"http://example.com/ns\">"
"<set><prop>"
"<custom:myprop>myvalue</custom:myprop>"
"</prop></set>"
"</propertyupdate>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPPATCH", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/custom.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(proppatch_on_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propertyupdate xmlns=\"DAV:\">"
"<set><prop><displayname>My Folder</displayname></prop></set>"
"</propertyupdate>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPPATCH", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/folder", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Content-Type", "application/xml");
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_207_MULTI_STATUS, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(proppatch_locked_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/locked.txt", "x", 1);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/locked.txt", &lock);
const char *body =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<propertyupdate xmlns=\"DAV:\">"
"<set><prop><displayname>x</displayname></prop></set>"
"</propertyupdate>";
http_request_init(&ctx->request);
strncpy(ctx->request.method, "PROPPATCH", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/locked.txt", sizeof(ctx->request.uri) - 1);
ctx->request.body = strdup(body);
ctx->request.body_len = strlen(body);
ctx->request.content_length = strlen(body);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_423_LOCKED, ctx->response.status);
endpoint_test_destroy(ctx);
}
+130
View File
@@ -0,0 +1,130 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_put)
TEST(put_create_new_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
const char *content = "New file content";
endpoint_test_execute(ctx, "PUT", "/newfile.txt", content, strlen(content));
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/newfile.txt");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(put_overwrite_existing_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/existing.txt", "old", 3);
const char *new_content = "new content";
endpoint_test_execute(ctx, "PUT", "/existing.txt", new_content, strlen(new_content));
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(put_parent_missing_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "PUT", "/nonexistent/file.txt", "data", 4);
ASSERT_EQUAL(HTTP_409_CONFLICT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(put_into_collection) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/folder");
const char *content = "file in folder";
endpoint_test_execute(ctx, "PUT", "/folder/document.txt", content, strlen(content));
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/folder/document.txt");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(put_binary_data) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
unsigned char binary[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
endpoint_test_execute(ctx, "PUT", "/image.png", (char *)binary, sizeof(binary));
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(put_empty_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "PUT", "/empty.txt", "", 0);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
int exists = storage_exists(ctx->fixture->storage, "/empty.txt");
ASSERT_TRUE(exists);
endpoint_test_destroy(ctx);
}
TEST(put_collection_uri_fails) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_create_collection(ctx->fixture->storage, "/mycol");
endpoint_test_execute(ctx, "PUT", "/mycol/", "data", 4);
ASSERT_NOT_EQUAL(HTTP_201_CREATED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(put_path_traversal_blocked) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
endpoint_test_execute(ctx, "PUT", "/../../../tmp/evil.txt", "malicious", 9);
ASSERT_NOT_EQUAL(HTTP_201_CREATED, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(put_large_file) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
size_t size = 1024 * 1024;
char *large_content = malloc(size);
ASSERT_NOT_NULL(large_content);
memset(large_content, 'X', size);
endpoint_test_execute(ctx, "PUT", "/largefile.bin", large_content, size);
ASSERT_EQUAL(HTTP_201_CREATED, ctx->response.status);
free(large_content);
endpoint_test_destroy(ctx);
}
@@ -0,0 +1,169 @@
// retoor <retoor@molodetz.nl>
#include "../../test_framework.h"
#include "../http_endpoints.h"
TEST_SUITE(webdav_unlock)
TEST(unlock_success) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/locked.txt", "content", 7);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/locked.txt", &lock);
char lock_token_header[256];
snprintf(lock_token_header, sizeof(lock_token_header), "<%s>", lock.token);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "UNLOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/locked.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Lock-Token", lock_token_header);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
int is_locked = lock_manager_is_locked(ctx->fixture->locks, "/locked.txt");
ASSERT_FALSE(is_locked);
endpoint_test_destroy(ctx);
}
TEST(unlock_missing_token_header) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/file.txt", "x", 1);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/file.txt", &lock);
endpoint_test_execute(ctx, "UNLOCK", "/file.txt", NULL, 0);
ASSERT_EQUAL(HTTP_400_BAD_REQUEST, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(unlock_wrong_token) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/file.txt", "x", 1);
lock_info_t lock;
memset(&lock, 0, sizeof(lock));
strncpy(lock.owner, "user1", sizeof(lock.owner) - 1);
lock.scope = LOCK_SCOPE_EXCLUSIVE;
lock.type = LOCK_TYPE_WRITE;
lock.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/file.txt", &lock);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "UNLOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/file.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Lock-Token", "<wrong-token>");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_409_CONFLICT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(unlock_not_locked) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/notlocked.txt", "x", 1);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "UNLOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/notlocked.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Lock-Token", "<some-token>");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_409_CONFLICT, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(unlock_not_found) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "UNLOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/nonexistent.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Lock-Token", "<some-token>");
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_404_NOT_FOUND, ctx->response.status);
endpoint_test_destroy(ctx);
}
TEST(unlock_shared_lock) {
endpoint_test_ctx_t *ctx = endpoint_test_create();
ASSERT_NOT_NULL(ctx);
storage_write_file(ctx->fixture->storage, "/shared.txt", "x", 1);
lock_info_t lock1;
memset(&lock1, 0, sizeof(lock1));
strncpy(lock1.owner, "user1", sizeof(lock1.owner) - 1);
lock1.scope = LOCK_SCOPE_SHARED;
lock1.type = LOCK_TYPE_WRITE;
lock1.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/shared.txt", &lock1);
lock_info_t lock2;
memset(&lock2, 0, sizeof(lock2));
strncpy(lock2.owner, "user2", sizeof(lock2.owner) - 1);
lock2.scope = LOCK_SCOPE_SHARED;
lock2.type = LOCK_TYPE_WRITE;
lock2.timeout = 3600;
lock_manager_acquire(ctx->fixture->locks, "/shared.txt", &lock2);
char lock_token_header[256];
snprintf(lock_token_header, sizeof(lock_token_header), "<%s>", lock1.token);
http_request_init(&ctx->request);
strncpy(ctx->request.method, "UNLOCK", sizeof(ctx->request.method) - 1);
strncpy(ctx->request.uri, "/shared.txt", sizeof(ctx->request.uri) - 1);
http_request_add_header(&ctx->request, "Lock-Token", lock_token_header);
http_response_init(&ctx->response);
webdav_handle_request(ctx->fixture->storage, ctx->fixture->locks,
ctx->fixture->props, &ctx->request, &ctx->response);
ASSERT_EQUAL(HTTP_204_NO_CONTENT, ctx->response.status);
int is_locked = lock_manager_is_locked(ctx->fixture->locks, "/shared.txt");
ASSERT_TRUE(is_locked);
endpoint_test_destroy(ctx);
}