From ad5cda866abf2fc50e7a76fa394de8795c1cd06a Mon Sep 17 00:00:00 2001 From: Fabian Sauter Date: Thu, 3 Aug 2023 12:47:20 +0200 Subject: [PATCH] Added unit tests for special file names --- test/CMakeLists.txt | 5 + test/data/test_file.txt | 1 + ..._hello_\303\244\303\274\303\266p_2585.txt" | 1 + ...03\266p_2585_\344\275\240\345\245\275.txt" | 1 + test/file_upload_tests.cpp | 100 ++++++++++++++++++ test/httpServer.cpp | 22 ++++ test/httpServer.hpp | 1 + test/ssl_tests.cpp | 4 +- 8 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 test/data/test_file.txt create mode 100644 "test/data/test_file_hello_\303\244\303\274\303\266p_2585.txt" create mode 100644 "test/data/test_file_hello_\303\244\303\274\303\266p_2585_\344\275\240\345\245\275.txt" create mode 100644 test/file_upload_tests.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d4aa16ef9..de9eee226 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -64,6 +64,7 @@ add_cpr_test(interceptor_multi) add_cpr_test(multiperform) add_cpr_test(resolve) add_cpr_test(multiasync) +add_cpr_test(file_upload) if (ENABLE_SSL_TESTS) add_cpr_test(ssl) @@ -79,4 +80,8 @@ if (ENABLE_SSL_TESTS) add_custom_command(TARGET ssl_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/keys/server.pub $/data/keys/server.pub) endif() +add_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file_hello_äüöp_2585_你好.txt $/data/test_file_hello_äüöp_2585_你好.txt) +add_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file_hello_äüöp_2585.txt $/data/test_file_hello_äüöp_2585.txt) +add_custom_command(TARGET file_upload_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/data/test_file.txt $/data/test_file.txt) + file(INSTALL data DESTINATION data) diff --git a/test/data/test_file.txt b/test/data/test_file.txt new file mode 100644 index 000000000..b67d8e82d --- /dev/null +++ b/test/data/test_file.txt @@ -0,0 +1 @@ +test content: hello_äüöp_2585_你好 \ No newline at end of file diff --git "a/test/data/test_file_hello_\303\244\303\274\303\266p_2585.txt" "b/test/data/test_file_hello_\303\244\303\274\303\266p_2585.txt" new file mode 100644 index 000000000..b67d8e82d --- /dev/null +++ "b/test/data/test_file_hello_\303\244\303\274\303\266p_2585.txt" @@ -0,0 +1 @@ +test content: hello_äüöp_2585_你好 \ No newline at end of file diff --git "a/test/data/test_file_hello_\303\244\303\274\303\266p_2585_\344\275\240\345\245\275.txt" "b/test/data/test_file_hello_\303\244\303\274\303\266p_2585_\344\275\240\345\245\275.txt" new file mode 100644 index 000000000..b67d8e82d --- /dev/null +++ "b/test/data/test_file_hello_\303\244\303\274\303\266p_2585_\344\275\240\345\245\275.txt" @@ -0,0 +1 @@ +test content: hello_äüöp_2585_你好 \ No newline at end of file diff --git a/test/file_upload_tests.cpp b/test/file_upload_tests.cpp new file mode 100644 index 000000000..66fcaf3e5 --- /dev/null +++ b/test/file_upload_tests.cpp @@ -0,0 +1,100 @@ +#include + +#include +#include +#include + +#include +#include + +#include "cpr/api.h" +#include "cpr/file.h" +#include "cpr/multipart.h" +#include "httpServer.hpp" + +namespace { +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp) +cpr::HttpServer* server = new cpr::HttpServer(); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +std::optional baseDirPath{std::nullopt}; +} // namespace + +cpr::fs::path GetBasePath(const std::string& execPath) { + return cpr::fs::path(cpr::fs::path{execPath}.parent_path().string() + "/").make_preferred(); +} + + +TEST(FileUploadTests, AsciiFileName) { + // Ensure 'baseDirPath' has been set + EXPECT_NE(baseDirPath, std::nullopt); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + cpr::fs::path filePath = baseDirPath->append("test_file.txt"); + + cpr::Multipart mp{{cpr::Part("file_name", cpr::File(filePath.string()))}}; + cpr::Url url{server->GetBaseUrl() + "/post_file_upload.html"}; + cpr::Response response = cpr::Post(url, mp); + + // Expected file content + std::ifstream ifs(filePath.string()); + std::string expected_text = "{\n \"file_name\": \"" + filePath.filename().string() + "=" + std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())) + "\"\n}"; + + EXPECT_EQ(expected_text, response.text); + EXPECT_EQ(cpr::Url{server->GetBaseUrl() + "/post_file_upload.html"}, response.url); + EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); + EXPECT_EQ(201, response.status_code); + EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); +} + +TEST(FileUploadTests, NonAsciiFileName) { + // Ensure 'baseDirPath' has been set + EXPECT_NE(baseDirPath, std::nullopt); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + cpr::fs::path filePath = baseDirPath->append("test_file_hello_äüöp_2585.txt"); + + cpr::Multipart mp{{cpr::Part("file_name", cpr::File(filePath.string()))}}; + cpr::Url url{server->GetBaseUrl() + "/post_file_upload.html"}; + cpr::Response response = cpr::Post(url, mp); + + // Expected file content + std::ifstream ifs(filePath.string()); + std::string expected_text = "{\n \"file_name\": \"" + filePath.filename().string() + "=" + std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())) + "\"\n}"; + + EXPECT_EQ(expected_text, response.text); + EXPECT_EQ(cpr::Url{server->GetBaseUrl() + "/post_file_upload.html"}, response.url); + EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); + EXPECT_EQ(201, response.status_code); + EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); +} + +TEST(FileUploadTests, ChineseFileName) { + // Ensure 'baseDirPath' has been set + EXPECT_NE(baseDirPath, std::nullopt); + + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + cpr::fs::path filePath = baseDirPath->append("test_file_hello_äüöp_2585_你好.txt"); + + cpr::Multipart mp{{cpr::Part("file_name", cpr::File(filePath.string()))}}; + cpr::Url url{server->GetBaseUrl() + "/post_file_upload.html"}; + cpr::Response response = cpr::Post(url, mp); + + // Expected file content + std::ifstream ifs(filePath.string()); + std::string expected_text = "{\n \"file_name\": \"" + filePath.filename().string() + "=" + std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())) + "\"\n}"; + + EXPECT_EQ(expected_text, response.text); + EXPECT_EQ(cpr::Url{server->GetBaseUrl() + "/post_file_upload.html"}, response.url); + EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); + EXPECT_EQ(201, response.status_code); + EXPECT_EQ(cpr::ErrorCode::OK, response.error.code); +} + +int main(int argc, char** argv) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + baseDirPath = std::make_optional(cpr::fs::path{GetBasePath(argv[0]).string() + "data/"}); + + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(server); + return RUN_ALL_TESTS(); +} diff --git a/test/httpServer.cpp b/test/httpServer.cpp index 9514de51f..32d41cad6 100644 --- a/test/httpServer.cpp +++ b/test/httpServer.cpp @@ -475,6 +475,26 @@ void HttpServer::OnRequestFormPost(mg_connection* conn, mg_http_message* msg) { mg_http_reply(conn, 201, headers.c_str(), response.c_str()); } +void HttpServer::OnRequestFileUploadPost(mg_connection* conn, mg_http_message* msg) { + size_t pos{0}; + mg_http_part part{}; + + std::string headers = "Content-Type: application/json\r\n"; + std::string response{}; + response += "{\n"; + while ((pos = mg_http_next_multipart(msg->body, pos, &part)) > 0) { + response += " \"" + std::string(part.name.ptr, part.name.len) + "\": \""; + if (!std::string(part.filename.ptr, part.filename.len).empty()) { + response += std::string(part.filename.ptr, part.filename.len) + "="; + } + response += std::string(part.body.ptr, part.body.len) + "\",\n"; + } + response.erase(response.find_last_not_of(",\n") + 1); + response += "\n}"; + + mg_http_reply(conn, 201, headers.c_str(), response.c_str()); +} + void HttpServer::OnRequestDelete(mg_connection* conn, mg_http_message* msg) { bool has_json_header = false; for (mg_http_header& header : msg->headers) { @@ -887,6 +907,8 @@ void HttpServer::OnRequest(mg_connection* conn, mg_http_message* msg) { OnRequestJsonPost(conn, msg); } else if (uri == "/form_post.html") { OnRequestFormPost(conn, msg); + } else if (uri == "/post_file_upload.html") { + OnRequestFileUploadPost(conn, msg); } else if (uri == "/post_reflect.html") { OnRequestPostReflect(conn, msg); } else if (uri == "/delete.html") { diff --git a/test/httpServer.hpp b/test/httpServer.hpp index 735ff4097..7941f0f29 100644 --- a/test/httpServer.hpp +++ b/test/httpServer.hpp @@ -45,6 +45,7 @@ class HttpServer : public AbstractServer { static void OnRequestBodyGet(mg_connection* conn, mg_http_message* msg); static void OnRequestJsonPost(mg_connection* conn, mg_http_message* msg); static void OnRequestFormPost(mg_connection* conn, mg_http_message* msg); + static void OnRequestFileUploadPost(mg_connection* conn, mg_http_message* msg); static void OnRequestDelete(mg_connection* conn, mg_http_message* msg); static void OnRequestDeleteNotAllowed(mg_connection* conn, mg_http_message* msg); static void OnRequestPut(mg_connection* conn, mg_http_message* msg); diff --git a/test/ssl_tests.cpp b/test/ssl_tests.cpp index 92131888d..f5cc32614 100644 --- a/test/ssl_tests.cpp +++ b/test/ssl_tests.cpp @@ -154,13 +154,13 @@ TEST(SslTests, LoadCertFromBufferTestSimpel) { } #endif -fs::path getBasePath(const std::string& execPath) { +fs::path GetBasePath(const std::string& execPath) { return fs::path(fs::path{execPath}.parent_path().string() + "/").make_preferred(); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - fs::path baseDirPath = fs::path{getBasePath(argv[0]).string() + "data/"}; + fs::path baseDirPath = fs::path{GetBasePath(argv[0]).string() + "data/"}; fs::path serverCertPath = fs::path{baseDirPath}.append("certificates/server.crt"); fs::path serverKeyPath = fs::path{baseDirPath}.append("keys/server.key"); server = new HttpsServer(std::move(baseDirPath), std::move(serverCertPath), std::move(serverKeyPath));