diff --git a/.gitignore b/.gitignore index f5e46b698cdb99eb18c7bac8e226c7692325cbd7..2df735637f2c21743cd290bccc49bee78a57da95 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,9 @@ flycheck_*.el # Python/Sphinx env/ + +# Conan +conan.lock +conanbuildinfo* +conaninfo.txt +graph_info.json diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..449e4256f907780bfbd0ce3d6a23853f20de0ae6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Changed + +* Integer values are 32 bit now. + +### Deprecated + +### Removed + +### Fixed + +### Security diff --git a/CMakeLists.txt b/CMakeLists.txt index e0caaeb6cc52d914648f1c6f2acc48a196d447c8..e4c84cfc7a4482175b39037dc6b29cdd60e8f695 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. # -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.13) project(libcaosdb_inttests DESCRIPTION "Integration tests for the C++ client libraries of the CaosDB project which run against the CaosDB server." @@ -54,6 +54,7 @@ add_subdirectory(test) ####################################################### option(AUTOFORMATTING "call clang-format at configure time" ON) if(AUTOFORMATTING) + message("Autoformatting is on. To disable, call cmake with '-D AUTOFORMATTING=OFF'.") file(GLOB format_test_sources test/*.cpp test/*.h) execute_process(COMMAND clang-format -i --verbose ${format_test_sources} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a1e463721011de1db6145c4c2fcb96342b01c2d6 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +# ** header v3.0 +# This file is a part of the CaosDB Project. +# +# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> +# Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# ** end header + +# This Makefile is a wrapper for several other scripts. + + +CLANG-FORMAT = clang-format-11 +CLANG-TIDY = clang-tidy-11 + +CLANG_TIDY_CHECKS = "--checks=*,-fuchsia-*,-llvmlibc-*,-cert-err58-cpp,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-owning-memory,-modernize-use-trailing-return-type,-google-readability-avoid-underscore-in-googletest-name,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-readability-function-cognitive-complexity,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-non-private-member-variables-in-classes,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay" +CLANG_TIDY_CMD = $(CLANG-TIDY) \ + --header-filter=caosdb/.*[^\(\.pb\.h\)]$ \ + --fix \ + $(CLANG_TIDY_CHECKS) + + +.PHONY: help +help: + @echo "Targets:" + @echo " conan-install - Install locally with Conan." + @echo " style - auto-format the source files." + +conan-install: + conan install . -s "compiler.libcxx=libstdc++11" +.PHONY: conan-install + +format: conan-install + $(CLANG-FORMAT) -i --verbose \ + $$(find test/ -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.h.in") + $(CLANG_TIDY_CMD) $$(find test/ -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.h.in") +.PHONY: format diff --git a/README.md b/README.md index 9d2b880004b551f8e32eb0254e47377744584e69..a1e449184088f9eb762c8cbbae16d0360c7ef71f 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,7 @@ Create a local conan package from the caosdb-cpplib repository - CAOSDB_SERVER_GRPC_PORT_HTTPS - CAOSDB_SERVER_CERT 6. Run with `ctest` in the build directory. + +## Formatting, style, linting ## + +`make format` diff --git a/conanfile.txt b/conanfile.txt index ccd05f44e4855c4d4b13dc1fbc7df8554dd4e8ad..2c69dd54038c69510decea33197146eff5974066 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,5 +1,5 @@ [requires] -caosdb/0.0.11 +caosdb/0.0.15 gtest/1.11.0 [generators] diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ecb011e99179399e62f646f3027951aff0163466..41e6bf3e29e115914f6732e87cb44818ed9c28ae 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,9 +23,11 @@ ####################################################################### set(test_cases test_connection + test_list_properties + test_properties test_transaction - #test_file_transmission TODO add once fixed test_ccaosdb + test_issues ) @@ -54,6 +56,7 @@ add_compiler_flag("-g") ####################################################### option(LINTING "clang-tidy and iwye" ON) if(LINTING) + message("Linting is on. To disable, call cmake with '-D LINTING=OFF'.") ### set paranoid compiler flags #add_compiler_flag("-Wall") #add_compiler_flag("-Wextra") @@ -80,10 +83,9 @@ if(LINTING) message(STATUS "clang-tidy: ${clang_tidy}") set(_CMAKE_CXX_CLANG_TIDY "${clang_tidy}" "--header-filter=caosdb/.*[^\(\.pb\.h\)]$" - "--warnings-as-errors=*" "--fix") set(_CMAKE_CXX_CLANG_TIDY_CHECKS - "--checks=*,-fuchsia-*,-llvmlibc-*,-cert-err58-cpp,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-owning-memory,-modernize-use-trailing-return-type,-google-readability-avoid-underscore-in-googletest-name,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-readability-function-cognitive-complexity,-cppcoreguidelines-non-private-member-variables-in-classes,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay") + "--checks=*,-fuchsia-*,-llvmlibc-*,-cert-err58-cpp,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-owning-memory,-modernize-use-trailing-return-type,-google-readability-avoid-underscore-in-googletest-name,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-readability-function-cognitive-complexity,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-non-private-member-variables-in-classes,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay") endif() else() message(STATUS "LINTING is OFF") diff --git a/test/caosdb_test_utility.h b/test/caosdb_test_utility.h index 4cde3dc5059acf60a04b071154b44a59b67e9833..4c97c203a8ab55c9d239d824cd274fe3b99aa60b 100644 --- a/test/caosdb_test_utility.h +++ b/test/caosdb_test_utility.h @@ -33,16 +33,16 @@ */ #define EXPECT_THROW_MESSAGE(statement, exeption_type, message) \ EXPECT_THROW( \ - try { statement; } catch (const exeption_type &e) { \ - EXPECT_EQ(std::string(e.what()), message); \ - throw; \ - }, \ - exeption_type) + try { statement; } catch (const exeption_type &e) { \ + EXPECT_EQ(std::string(e.what()), message); \ + throw; \ + }, \ + exeption_type) #define ASSERT_THROW_MESSAGE(statement, exeption_type, message) \ ASSERT_THROW( \ - try { statement; } catch (const exeption_type &e) { \ - ASSERT_EQ(std::string(e.what()), message); \ - throw; \ - }, \ - exeption_type) + try { statement; } catch (const exeption_type &e) { \ + ASSERT_EQ(std::string(e.what()), message); \ + throw; \ + }, \ + exeption_type) #endif diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp index f56e92d620d3a18b1ffa7262f34d139963a3987a..ff01b9ba95d8dcded153aa9f94260a98cf5b3c53 100644 --- a/test/test_ccaosdb.cpp +++ b/test/test_ccaosdb.cpp @@ -18,15 +18,87 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "caosdb/message_code.h" // for MessageCode -#include "caosdb/status_code.h" // for StatusCode -#include "ccaosdb.h" // for caosdb_info_version_info, caosdb_... -#include "gtest/gtest-message.h" // for Message -#include "gtest/gtest-test-part.h" // for TestPartResult -#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, SuiteApiResolver -#include <memory> // for allocator, unique_ptr - -TEST(test_ccaosdb, connection_ssl_authentication_success) { +#include "caosdb/file_transmission/file_writer.h" // for FileWriter +#include "caosdb/message_code.h" // for ENTITY_DOES_NOT_EXIST +#include "caosdb/status_code.h" // for GO_ON, StatusCode +#include "ccaosdb.h" // for caosdb_connection_... +#include <boost/filesystem/operations.hpp> // for remove +#include <boost/filesystem/path.hpp> // for path +#include <boost/filesystem/path_traits.hpp> // for filesystem +#include <cstring> // for strcmp +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult +#include <gtest/gtest_pred_impl.h> // for EXPECT_EQ, TestInfo +#include <iostream> // for operator<<, endl +#include <string> // for allocator, string + +namespace fs = boost::filesystem; +class test_ccaosdb : public ::testing::Test { +protected: + fs::path test_upload_file_1; + fs::path test_download_file_1; + + void SetUp() override { + DeleteEntities(); + test_upload_file_1 = fs::path("test_upload_file_1_delete_me.dat"); + test_download_file_1 = fs::path("test_download_file_1_delete_me.dat"); + + // fill the file that shall be uploaded + caosdb::transaction::FileWriter writer(test_upload_file_1); + std::string buffer(1024, 'c'); + for (int i = 0; i < 20; i++) { + writer.write(buffer); + } + } + + void TearDown() override { + // delete files + fs::remove(test_upload_file_1); + fs::remove(test_download_file_1); + + DeleteEntities(); + } + + /** + * Delete everything with id>99, C style + */ + static void DeleteEntities() { + caosdb_connection_connection connection; + caosdb_connection_connection_manager_get_default_connection(&connection); + + caosdb_transaction_transaction query_transaction; + caosdb_connection_connection_create_transaction(&connection, + &query_transaction); + caosdb_transaction_transaction_query(&query_transaction, + "FIND ENTITY WITH id > 99"); + caosdb_transaction_transaction_execute(&query_transaction); + + caosdb_transaction_result_set result_set; + caosdb_transaction_transaction_get_result_set(&query_transaction, + &result_set); + int count[] = {-1}; // NOLINT + caosdb_transaction_result_set_size(&result_set, count); + if (*count > 0) { + caosdb_transaction_transaction delete_transaction; + caosdb_connection_connection_create_transaction(&connection, + &delete_transaction); + caosdb_entity_entity entity; + char *id = nullptr; // NOLINT + for (int i = 0; i < *count; i++) { + caosdb_transaction_result_set_at(&result_set, &entity, i); + caosdb_entity_entity_get_id(&entity, &id); + caosdb_transaction_transaction_delete_by_id(&delete_transaction, id); + } + + caosdb_transaction_transaction_execute(&delete_transaction); + caosdb_transaction_delete_transaction(&delete_transaction); + } + + caosdb_transaction_delete_transaction(&query_transaction); + } +}; + +TEST_F(test_ccaosdb, connection_ssl_authentication_success) { caosdb_connection_connection connection; caosdb_connection_connection_manager_get_default_connection(&connection); @@ -37,14 +109,14 @@ TEST(test_ccaosdb, connection_ssl_authentication_success) { auto major = caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR(); auto minor = caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR(); const auto *const pre_release = - caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE(); + caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE(); EXPECT_EQ(major, version_info.major); EXPECT_EQ(minor, version_info.minor); EXPECT_STREQ(pre_release, version_info.pre_release); } -TEST(test_ccaosdb, test_count_query) { +TEST_F(test_ccaosdb, test_count_query) { caosdb_connection_connection connection; int return_code( @@ -75,7 +147,7 @@ TEST(test_ccaosdb, test_count_query) { EXPECT_EQ(return_code, 0); } -TEST(test_ccaosdb, test_query) { +TEST_F(test_ccaosdb, test_query) { caosdb_connection_connection connection; int return_code( caosdb_connection_connection_manager_get_default_connection(&connection)); @@ -108,7 +180,7 @@ TEST(test_ccaosdb, test_query) { EXPECT_EQ(return_code, 0); } -TEST(test_ccaosdb, test_single_id_retrieve) { +TEST_F(test_ccaosdb, test_single_id_retrieve) { caosdb_connection_connection connection; int return_code( caosdb_connection_connection_manager_get_default_connection(&connection)); @@ -145,8 +217,8 @@ TEST(test_ccaosdb, test_single_id_retrieve) { EXPECT_EQ(return_code, 0); EXPECT_EQ(*count, 0); - char out[255] = {"255"}; // NOLINT - return_code = caosdb_entity_entity_get_name(&entity, out); + char *out = nullptr; // NOLINT + return_code = caosdb_entity_entity_get_name(&entity, &out); EXPECT_EQ(return_code, 0); EXPECT_STREQ(out, "unit"); @@ -154,7 +226,7 @@ TEST(test_ccaosdb, test_single_id_retrieve) { EXPECT_EQ(return_code, 0); } -TEST(test_ccaosdb, test_multi_id_retrieve) { +TEST_F(test_ccaosdb, test_multi_id_retrieve) { caosdb_connection_connection connection; int return_code( caosdb_connection_connection_manager_get_default_connection(&connection)); @@ -193,8 +265,8 @@ TEST(test_ccaosdb, test_multi_id_retrieve) { EXPECT_EQ(return_code, 0); EXPECT_EQ(*count, 0); - char out[255] = {"255"}; // NOLINT - return_code = caosdb_entity_entity_get_name(&entity, out); + char *out = nullptr; // NOLINT + return_code = caosdb_entity_entity_get_name(&entity, &out); EXPECT_EQ(return_code, 0); EXPECT_STREQ(out, "unit"); @@ -216,3 +288,375 @@ TEST(test_ccaosdb, test_multi_id_retrieve) { return_code = caosdb_transaction_delete_transaction(&transaction); EXPECT_EQ(return_code, 0); } + +TEST_F(test_ccaosdb, test_insert_update_delete) { + + caosdb_connection_connection connection; + caosdb_connection_connection_manager_get_default_connection(&connection); + + caosdb_entity_entity original_entity; + caosdb_entity_create_entity(&original_entity); + caosdb_entity_entity_set_name(&original_entity, "TestName"); + caosdb_entity_entity_set_role(&original_entity, "PROPERTY"); + caosdb_entity_entity_set_datatype(&original_entity, "TEXT", false, false); + + caosdb_transaction_transaction insert_transaction; + caosdb_connection_connection_create_transaction(&connection, + &insert_transaction); + caosdb_transaction_transaction_insert_entity(&insert_transaction, + &original_entity); + int return_code(caosdb_transaction_transaction_execute(&insert_transaction)); + EXPECT_EQ(return_code, 0); + + // Insert the entity and check results + caosdb_transaction_result_set insert_result_set; + caosdb_transaction_transaction_get_result_set(&insert_transaction, + &insert_result_set); + int count[] = {-1}; // NOLINT + caosdb_transaction_result_set_size(&insert_result_set, count); + EXPECT_EQ(*count, 1); + caosdb_entity_entity inserted_entity; + caosdb_transaction_result_set_at(&insert_result_set, &inserted_entity, 0); + char *in = nullptr; // NOLINT + caosdb_entity_entity_get_id(&inserted_entity, &in); + + // Retrieve it again for checks and update + caosdb_transaction_transaction retrieve_transaction_1; + caosdb_connection_connection_create_transaction(&connection, + &retrieve_transaction_1); + caosdb_transaction_transaction_retrieve_by_id(&retrieve_transaction_1, in); + return_code = caosdb_transaction_transaction_execute(&retrieve_transaction_1); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set retrieve_results_1; + caosdb_transaction_transaction_get_result_set(&retrieve_transaction_1, + &retrieve_results_1); + caosdb_transaction_result_set_size(&retrieve_results_1, count); + EXPECT_EQ(*count, 1); + caosdb_entity_entity retrieved_entity_1; + caosdb_transaction_result_set_at(&retrieve_results_1, &retrieved_entity_1, 0); + char *out = nullptr; // NOLINT + caosdb_entity_entity_get_id(&retrieved_entity_1, &out); + EXPECT_EQ(strcmp(in, out), 0); + + caosdb_entity_entity_get_name(&original_entity, &in); + caosdb_entity_entity_get_name(&retrieved_entity_1, &out); + EXPECT_EQ(strcmp(in, out), 0); + + caosdb_entity_entity_get_role(&original_entity, &in); + caosdb_entity_entity_get_role(&retrieved_entity_1, &out); + EXPECT_EQ(strcmp(in, out), 0); + + bool is_list_in[] = {false}; // NOLINT + bool is_ref_in[] = {false}; // NOLINT + caosdb_entity_entity_get_datatype(&original_entity, &in, is_ref_in, + is_list_in); + bool is_list_out[] = {false}; // NOLINT + bool is_ref_out[] = {false}; // NOLINT + caosdb_entity_entity_get_datatype(&retrieved_entity_1, &out, is_ref_out, + is_list_out); + EXPECT_EQ(strcmp(in, out), 0); + EXPECT_EQ(*is_list_in, *is_list_out); + EXPECT_EQ(*is_ref_in, *is_ref_out); + + // Change name and update + return_code = + caosdb_entity_entity_set_name(&retrieved_entity_1, "TestNameNew"); + EXPECT_EQ(return_code, 0); + caosdb_transaction_transaction update_transaction; + caosdb_connection_connection_create_transaction(&connection, + &update_transaction); + caosdb_transaction_transaction_update_entity(&update_transaction, + &retrieved_entity_1); + return_code = caosdb_transaction_transaction_execute(&update_transaction); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set update_results; + caosdb_transaction_transaction_get_result_set(&update_transaction, + &update_results); + caosdb_transaction_result_set_size(&update_results, count); + EXPECT_EQ(*count, 1); + caosdb_entity_entity updated_entity; + caosdb_transaction_result_set_at(&update_results, &updated_entity, 0); + caosdb_entity_entity_get_id(&inserted_entity, &in); + caosdb_entity_entity_get_id(&updated_entity, &out); + EXPECT_EQ(strcmp(in, out), 0); + + // Retrieve again + caosdb_transaction_transaction retrieve_transaction_2; + caosdb_connection_connection_create_transaction(&connection, + &retrieve_transaction_2); + caosdb_transaction_transaction_retrieve_by_id(&retrieve_transaction_2, out); + return_code = caosdb_transaction_transaction_execute(&retrieve_transaction_2); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set retrieve_results_2; + caosdb_transaction_transaction_get_result_set(&retrieve_transaction_2, + &retrieve_results_2); + caosdb_transaction_result_set_size(&retrieve_results_2, count); + EXPECT_EQ(*count, 1); + caosdb_entity_entity retrieved_entity_2; + caosdb_transaction_result_set_at(&retrieve_results_2, &retrieved_entity_2, 0); + + caosdb_entity_entity_get_id(&retrieved_entity_2, &out); + EXPECT_EQ(strcmp(in, out), 0); + + caosdb_entity_entity_get_name(&retrieved_entity_1, &in); + caosdb_entity_entity_get_name(&retrieved_entity_2, &out); + EXPECT_EQ(strcmp(in, out), 0); + + // Doesn't have the old name anymore + caosdb_entity_entity_get_name(&original_entity, &in); + EXPECT_FALSE((strcmp(in, out) == 0)); + + // Everything else hasn't changed + caosdb_entity_entity_get_role(&original_entity, &in); + caosdb_entity_entity_get_role(&retrieved_entity_2, &out); + EXPECT_EQ(strcmp(in, out), 0); + + caosdb_entity_entity_get_datatype(&original_entity, &in, is_ref_in, + is_list_in); + caosdb_entity_entity_get_datatype(&retrieved_entity_2, &out, is_ref_out, + is_list_out); + EXPECT_EQ(strcmp(in, out), 0); + EXPECT_EQ(*is_ref_in, *is_ref_out); + EXPECT_EQ(*is_list_in, *is_list_out); + + // Now delete + caosdb_transaction_transaction delete_transaction; + caosdb_connection_connection_create_transaction(&connection, + &delete_transaction); + caosdb_entity_entity_get_id(&retrieved_entity_2, &in); + caosdb_transaction_transaction_delete_by_id(&delete_transaction, in); + return_code = caosdb_transaction_transaction_execute(&delete_transaction); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set delete_results; + caosdb_transaction_transaction_get_result_set(&delete_transaction, + &delete_results); + caosdb_transaction_result_set_size(&delete_results, count); + EXPECT_EQ(*count, 1); + + caosdb_entity_entity deleted_entity; + caosdb_transaction_result_set_at(&delete_results, &deleted_entity, 0); + caosdb_entity_entity_get_id(&deleted_entity, &out); + EXPECT_EQ(strcmp(in, out), 0); + + // Try to retrieve again + caosdb_transaction_transaction retrieve_transaction_3; + caosdb_connection_connection_create_transaction(&connection, + &retrieve_transaction_3); + caosdb_transaction_transaction_retrieve_by_id(&retrieve_transaction_3, out); + return_code = caosdb_transaction_transaction_execute(&retrieve_transaction_3); + EXPECT_TRUE((return_code > 0)); + + caosdb_transaction_result_set retrieve_results_3; + caosdb_transaction_transaction_get_result_set(&retrieve_transaction_3, + &retrieve_results_3); + caosdb_transaction_result_set_size(&retrieve_results_3, count); + EXPECT_EQ(*count, 1); + caosdb_entity_entity retrieved_entity_3; + caosdb_transaction_result_set_at(&retrieve_results_3, &retrieved_entity_3, 0); + caosdb_entity_entity_get_errors_size(&retrieved_entity_3, count); + EXPECT_EQ(*count, 1); + caosdb_entity_message err_msg; + caosdb_entity_entity_get_error(&retrieved_entity_3, &err_msg, 0); + caosdb_entity_message_get_code(&err_msg, count); + EXPECT_EQ(*count, caosdb::entity::MessageCode::ENTITY_DOES_NOT_EXIST); + + // Delete everything + caosdb_transaction_delete_transaction(&retrieve_transaction_3); + caosdb_transaction_delete_transaction(&delete_transaction); + caosdb_transaction_delete_transaction(&retrieve_transaction_2); + caosdb_transaction_delete_transaction(&update_transaction); + caosdb_transaction_delete_transaction(&retrieve_transaction_1); + caosdb_transaction_delete_transaction(&insert_transaction); + caosdb_entity_delete_entity(&original_entity); +} + +TEST_F(test_ccaosdb, test_insert_with_prop_and_parent) { + + caosdb_connection_connection connection; + caosdb_connection_connection_manager_get_default_connection(&connection); + + // Create and insert a property, ... + std::cout << "Creating a property..." << std::endl; + caosdb_entity_entity original_prop; + caosdb_entity_create_entity(&original_prop); + + caosdb_entity_entity_set_name(&original_prop, "TestProp"); + caosdb_entity_entity_set_role(&original_prop, "PROPERTY"); + caosdb_entity_entity_set_datatype(&original_prop, "TEXT", false, false); + + std::cout << "Inserting a property..." << std::endl; + caosdb_transaction_transaction prop_insertion; + caosdb_connection_connection_create_transaction(&connection, &prop_insertion); + caosdb_transaction_transaction_insert_entity(&prop_insertion, &original_prop); + int return_code(caosdb_transaction_transaction_execute(&prop_insertion)); + EXPECT_EQ(return_code, 0); + + std::cout << "Checking result..." << std::endl; + caosdb_transaction_result_set prop_results; + caosdb_transaction_transaction_get_result_set(&prop_insertion, &prop_results); + caosdb_entity_entity inserted_prop; + caosdb_transaction_result_set_at(&prop_results, &inserted_prop, 0); + char *prop_id = nullptr; // NOLINT + caosdb_entity_entity_get_id(&inserted_prop, &prop_id); + + // ... a record type with this property, ... + caosdb_entity_entity original_rt; + caosdb_entity_create_entity(&original_rt); + + caosdb_entity_entity_set_name(&original_rt, "TestType"); + caosdb_entity_entity_set_role(&original_rt, "RECORD_TYPE"); + + caosdb_entity_property rt_prop; + caosdb_entity_create_property(&rt_prop); + caosdb_entity_property_set_id(&rt_prop, prop_id); + caosdb_entity_entity_append_property(&original_rt, &rt_prop); + + caosdb_transaction_transaction rt_insertion; + caosdb_connection_connection_create_transaction(&connection, &rt_insertion); + caosdb_transaction_transaction_insert_entity(&rt_insertion, &original_rt); + return_code = caosdb_transaction_transaction_execute(&rt_insertion); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set rt_results; + caosdb_transaction_transaction_get_result_set(&rt_insertion, &rt_results); + caosdb_entity_entity inserted_rt; + caosdb_transaction_result_set_at(&rt_results, &inserted_rt, 0); + char *rt_id = nullptr; // NOLINT + caosdb_entity_entity_get_id(&inserted_rt, &rt_id); + + // ... and a record with this record type as a parent. + caosdb_entity_entity original_rec; + caosdb_entity_create_entity(&original_rec); + caosdb_entity_entity_set_name(&original_rec, "TestRec"); + caosdb_entity_entity_set_role(&original_rec, "RECORD"); + caosdb_entity_parent rec_parent; + caosdb_entity_create_parent(&rec_parent); + caosdb_entity_parent_set_id(&rec_parent, rt_id); + caosdb_entity_entity_append_parent(&original_rec, &rec_parent); + caosdb_entity_property rec_prop; + caosdb_entity_create_property(&rec_prop); + caosdb_entity_property_set_id(&rec_prop, prop_id); + caosdb_entity_property_set_string_value(&rec_prop, "Bla"); + caosdb_entity_entity_append_property(&original_rec, &rec_prop); + + caosdb_transaction_transaction rec_insertion; + caosdb_connection_connection_create_transaction(&connection, &rec_insertion); + caosdb_transaction_transaction_insert_entity(&rec_insertion, &original_rec); + return_code = caosdb_transaction_transaction_execute(&rec_insertion); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set rec_results; + caosdb_transaction_transaction_get_result_set(&rec_insertion, &rec_results); + caosdb_entity_entity inserted_rec; + caosdb_transaction_result_set_at(&rec_results, &inserted_rec, 0); + char *rec_id = nullptr; // NOLINT + caosdb_entity_entity_get_id(&inserted_rec, &rec_id); + + // Retrieve the record again for comparison + caosdb_transaction_transaction retrieve_transaction; + caosdb_connection_connection_create_transaction(&connection, + &retrieve_transaction); + caosdb_transaction_transaction_retrieve_by_id(&retrieve_transaction, rec_id); + return_code = caosdb_transaction_transaction_execute(&retrieve_transaction); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set retrieve_result; + caosdb_transaction_transaction_get_result_set(&retrieve_transaction, + &retrieve_result); + caosdb_entity_entity retrieved_rec; + caosdb_transaction_result_set_at(&retrieve_result, &retrieved_rec, 0); + char *in = nullptr; // NOLINT + char *out = nullptr; // NOLINT + caosdb_entity_entity_get_name(&original_rec, &in); + caosdb_entity_entity_get_name(&retrieved_rec, &out); + EXPECT_EQ(strcmp(in, out), 0); + int count[] = {0}; // NOLINT + caosdb_entity_entity_get_properties_size(&retrieved_rec, count); + EXPECT_EQ(*count, 1); + caosdb_entity_entity_get_parents_size(&retrieved_rec, count); + EXPECT_EQ(*count, 1); + + caosdb_entity_parent retrieved_parent; + caosdb_entity_entity_get_parent(&retrieved_rec, &retrieved_parent, 0); + caosdb_entity_entity_get_name(&original_rt, &in); + caosdb_entity_parent_get_name(&retrieved_parent, &out); + EXPECT_EQ(strcmp(in, out), 0); + caosdb_entity_parent_get_id(&retrieved_parent, &out); + EXPECT_EQ(strcmp(rt_id, out), 0); + + caosdb_entity_property retrieved_property; + caosdb_entity_entity_get_property(&retrieved_rec, &retrieved_property, 0); + caosdb_entity_entity_get_name(&original_prop, &in); + caosdb_entity_property_get_name(&retrieved_property, &out); + EXPECT_EQ(strcmp(in, out), 0); + caosdb_entity_property_get_string_value(&rec_prop, &in); + caosdb_entity_property_get_string_value(&retrieved_property, &out); + EXPECT_EQ(strcmp(in, out), 0); + + caosdb_transaction_delete_transaction(&retrieve_transaction); + caosdb_transaction_delete_transaction(&rec_insertion); + caosdb_entity_delete_property(&rec_prop); + caosdb_entity_delete_parent(&rec_parent); + caosdb_entity_delete_entity(&original_rec); + caosdb_transaction_delete_transaction(&rt_insertion); + caosdb_entity_delete_property(&rt_prop); + caosdb_entity_delete_entity(&original_rt); + caosdb_transaction_delete_transaction(&prop_insertion); + caosdb_entity_delete_entity(&original_prop); +} + +TEST_F(test_ccaosdb, test_up_n_download_file) { + + caosdb_connection_connection connection; + caosdb_connection_connection_manager_get_default_connection(&connection); + + caosdb_entity_entity original_entity; + caosdb_entity_create_entity(&original_entity); + caosdb_entity_entity_set_role(&original_entity, "FILE"); + caosdb_entity_entity_set_local_path(&original_entity, + test_upload_file_1.c_str()); + caosdb_entity_entity_set_file_path(&original_entity, "/some/file"); + + caosdb_transaction_transaction insert_transaction; + caosdb_connection_connection_create_transaction(&connection, + &insert_transaction); + caosdb_transaction_transaction_insert_entity(&insert_transaction, + &original_entity); + int return_code(caosdb_transaction_transaction_execute(&insert_transaction)); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set rec_results; + caosdb_transaction_transaction_get_result_set(&insert_transaction, + &rec_results); + caosdb_entity_entity inserted_rec; + caosdb_transaction_result_set_at(&rec_results, &inserted_rec, 0); + char *rec_id = nullptr; // NOLINT + caosdb_entity_entity_get_id(&inserted_rec, &rec_id); + + caosdb_entity_entity download_entity; + caosdb_entity_create_entity(&download_entity); + caosdb_entity_entity_set_role(&download_entity, "FILE"); + caosdb_entity_entity_set_local_path(&download_entity, "lol"); + caosdb_entity_entity_set_file_path(&download_entity, "/some/file"); + + caosdb_transaction_transaction retrieve_transaction_1; + caosdb_connection_connection_create_transaction(&connection, + &retrieve_transaction_1); + EXPECT_EQ(return_code, 0); + // return_code = caosdb_transaction_transaction_retrieve_by_id( + // &retrieve_transaction_1, rec_id); + return_code = caosdb_transaction_transaction_retrieve_and_download_file_by_id( + &retrieve_transaction_1, rec_id, test_download_file_1.c_str()); + EXPECT_EQ(return_code, caosdb::StatusCode::GO_ON); + return_code = caosdb_transaction_transaction_execute(&retrieve_transaction_1); + EXPECT_EQ(return_code, 0); + + caosdb_transaction_result_set result_set; + return_code = caosdb_transaction_transaction_get_result_set( + &retrieve_transaction_1, &result_set); + EXPECT_EQ(return_code, 0); +} diff --git a/test/test_connection.cpp b/test/test_connection.cpp index 9a3f91168ed23497b700d9119bcba48af4c43e38..169fc9df1a2bd5d3e71aabf9a7b563402f733895 100644 --- a/test/test_connection.cpp +++ b/test/test_connection.cpp @@ -26,13 +26,13 @@ #include "caosdb/constants.h" // for COMPATIBLE_SERVER_VERSION_M... #include "caosdb/exceptions.h" // for AuthenticationError, Connec... #include "caosdb/info.h" // for VersionInfo -#include "caosdb/utility.h" // for get_env_var +#include "caosdb/utility.h" // for get_env_fallback #include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE -#include <gtest/gtest-message.h> // NOLINT TODO ?? for Message -#include <gtest/gtest-test-part.h> // NOLINT for SuiteApiResolver, TestPartR... -#include "gtest/gtest_pred_impl.h" // NOLINT for Test, TestInfo, TEST, EXPEC... -#include <memory> // for allocator, unique_ptr, __sh... -#include <string> // for stoi, string +#include "gtest/gtest_pred_impl.h" // NOLINT TODO how to fix this? for Test, TestInfo, TEST, EXPEC... +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestPartR... +#include <memory> // for allocator, unique_ptr, __sh... +#include <string> // for stoi, string namespace caosdb::connection { using caosdb::authentication::PlainPasswordAuthenticator; @@ -62,10 +62,10 @@ TEST(test_connection, connect_somehost_42347_fails) { TEST(test_connection, connection_insecure_authentication_error_anonymous) { const auto *port_str = - caosdb::utility::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTP", "8080"); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_GRPC_PORT_HTTP", "8080"); auto port = std::stoi(port_str); const auto *const host = - caosdb::utility::get_env_var("CAOSDB_SERVER_HOST", "localhost"); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_HOST", "localhost"); auto config = InsecureConnectionConfiguration(host, port); auto connection = Connection(config); @@ -75,12 +75,12 @@ TEST(test_connection, connection_insecure_authentication_error_anonymous) { TEST(test_connection, connection_ssl_authentication_error_anonymous) { const auto *port_str = - caosdb::utility::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443"); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443"); auto port = std::stoi(port_str); const auto *const host = - caosdb::utility::get_env_var("CAOSDB_SERVER_HOST", "localhost"); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_HOST", "localhost"); const auto path = - caosdb::utility::get_env_var("CAOSDB_SERVER_CERT", std::string()); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_CERT", std::string()); auto cert = PemFileCertificateProvider(path); auto config = TlsConnectionConfiguration(host, port, cert); @@ -94,12 +94,12 @@ TEST(test_connection, connection_ssl_authentication_error_anonymous) { TEST(test_connection, connection_ssl_authentication_error_wrong_credentials) { const auto *port_str = - caosdb::utility::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443"); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443"); auto port = std::stoi(port_str); const auto *const host = - caosdb::utility::get_env_var("CAOSDB_SERVER_HOST", "localhost"); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_HOST", "localhost"); const auto path = - caosdb::utility::get_env_var("CAOSDB_SERVER_CERT", std::string()); + caosdb::utility::get_env_fallback("CAOSDB_SERVER_CERT", std::string()); const auto *const user = "not-a-user-23461237"; const auto *const password = "very-c-cred"; @@ -109,10 +109,10 @@ TEST(test_connection, connection_ssl_authentication_error_wrong_credentials) { auto connection = Connection(config); EXPECT_THROW_MESSAGE( - connection.RetrieveVersionInfo(), AuthenticationError, - "The attempt to execute this transaction has not been executed at all " - "because the authentication did not succeed. Original error: " - "Authentication failed. Username or password wrong."); + connection.RetrieveVersionInfo(), AuthenticationError, + "The attempt to execute this transaction has not been executed at all " + "because the authentication did not succeed. Original error: " + "Authentication failed. Username or password wrong."); } TEST(test_connection, connection_ssl_authentication_success) { @@ -121,7 +121,7 @@ TEST(test_connection, connection_ssl_authentication_success) { auto major = caosdb::COMPATIBLE_SERVER_VERSION_MAJOR; auto minor = caosdb::COMPATIBLE_SERVER_VERSION_MINOR; const auto pre_release = - std::string(caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE); + std::string(caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE); const auto &v_info = connection->RetrieveVersionInfo(); EXPECT_EQ(major, v_info.GetMajor()); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp deleted file mode 100644 index eab5b785fcd15510b7aebce0d8a72bc78fb05fff..0000000000000000000000000000000000000000 --- a/test/test_file_transmission.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * This file is a part of the CaosDB Project. - * - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - */ -#include "caosdb/connection.h" // for Connection, ConnectionManager -#include "caosdb/entity.h" -#include "caosdb/entity/v1alpha1/main.pb.h" -#include "caosdb/transaction.h" // for Transaction, UniqueRe... -#include "gtest/gtest-message.h" // for Message -#include "gtest/gtest-test-part.h" // for TestPartResult, SuiteApiResolver -#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, AssertionResult -#include <memory> // for unique_ptr, allocator, __shar... -#include <string> // for string -namespace caosdb::transaction { -using caosdb::entity::Entity; -using caosdb::entity::v1alpha1::FileDownloadResponse; -using caosdb::entity::v1alpha1::FileUploadResponse; -using caosdb::entity::v1alpha1::RegisterFileDownloadResponse; -using caosdb::entity::v1alpha1::RegisterFileUploadResponse; -using caosdb::entity::v1alpha1::RegistrationStatus; -using caosdb::entity::v1alpha1::TransmissionStatus; - - -// TODO(tf) this file is currently not used (see CMakeLists.txt) -// Is it still necessary or is it obsolete due to test_transaction.cpp? -// RegisterFileDownloadResponse is currently not defined by proto or the h -// file. - -class test_file_transmission : public ::testing::Test { -protected: - void SetUp() override {} - - void TearDown() override { - // TODO(tf): delete all created entities - } -}; - -TEST_F(test_file_transmission, register_file_upload) { - const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); - - auto transaction(connection->CreateTransaction()); - RegisterFileUploadResponse response; - EXPECT_EQ(response.status(), - RegistrationStatus::REGISTRATION_STATUS_UNSPECIFIED); - - transaction->RegisterUploadFile(&response); - - EXPECT_EQ(response.status(), - RegistrationStatus::REGISTRATION_STATUS_ACCEPTED); - EXPECT_FALSE(response.registration_id().empty()); -} - -TEST_F(test_file_transmission, file_upload) { - const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); - - auto transaction(connection->CreateTransaction()); - RegisterFileUploadResponse registration_response; - - transaction->RegisterUploadFile(®istration_response); - ASSERT_EQ(registration_response.status(), - RegistrationStatus::REGISTRATION_STATUS_ACCEPTED); - auto registration_id = registration_response.registration_id(); - - FileUploadResponse upload_response; - transaction->UploadFile(&upload_response, registration_id); - - EXPECT_EQ(upload_response.status(), - TransmissionStatus::TRANSMISSION_STATUS_GO_ON); -} - -TEST_F(test_file_transmission, file_insertion) { - const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); - - auto transaction(connection->CreateTransaction()); - RegisterFileUploadResponse registration_response; - - transaction->RegisterUploadFile(®istration_response); - ASSERT_EQ(registration_response.status(), - RegistrationStatus::REGISTRATION_STATUS_ACCEPTED); - auto registration_id = registration_response.registration_id(); - - FileUploadResponse upload_response; - transaction->UploadFile(&upload_response, registration_id); - - Entity file_entity; - file_entity.SetRole("File"); - file_entity.SetFileTransmissionId(registration_id, "test.txt"); - file_entity.SetFilePath("test.txt"); - - transaction->InsertEntity(&file_entity); - transaction->Execute(); - - auto cleanup_transaction(connection->CreateTransaction()); - cleanup_transaction->DeleteById(transaction->GetResultSet().At(0).GetId()); - cleanup_transaction->Execute(); -} - -TEST_F(test_file_transmission, file_download) { - const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); - - auto upload_transaction(connection->CreateTransaction()); - RegisterFileUploadResponse upload_registration_response; - - upload_transaction->RegisterUploadFile(&upload_registration_response); - ASSERT_EQ(upload_registration_response.status(), - RegistrationStatus::REGISTRATION_STATUS_ACCEPTED); - auto registration_id = upload_registration_response.registration_id(); - - FileUploadResponse upload_response; - upload_transaction->UploadFile(&upload_response, registration_id); - - Entity file_entity; - file_entity.SetRole("File"); - file_entity.SetFileTransmissionId(registration_id, "test.txt"); - file_entity.SetFilePath("test.txt"); - - upload_transaction->InsertEntity(&file_entity); - upload_transaction->Execute(); - - // Download by entity_id - auto download_transaction(connection->CreateTransaction()); - RegisterFileDownloadResponse download_registration_response; - RegisterFileDownloadRequest download_registration_request; - download_registration_request.add_files()->set_entity_id( - upload_transaction->GetResultSet().At(0).GetId()); - download_transaction->RegisterDownloadFile(download_registration_request, - &download_registration_response); - ASSERT_EQ(download_registration_response.status(), - RegistrationStatus::REGISTRATION_STATUS_ACCEPTED); - - FileDownloadResponse download_response; - download_transaction->DownloadFile(&download_response, - download_registration_response); - - EXPECT_EQ(download_response.chunk().data(), "this is some data"); - - // CLEANUP - auto cleanup_transaction(connection->CreateTransaction()); - cleanup_transaction->DeleteById( - upload_transaction->GetResultSet().At(0).GetId()); - cleanup_transaction->Execute(); -} - -} // namespace caosdb::transaction diff --git a/test/test_issues.cpp b/test/test_issues.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8d56d8744d118c9beec5602afab9632211648c8 --- /dev/null +++ b/test/test_issues.cpp @@ -0,0 +1,192 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "caosdb/connection.h" // for Connection, Connec... +#include "caosdb/data_type.h" // for AtomicDataType +#include "caosdb/entity.h" // for Entity, Parent, Role +#include "caosdb/transaction.h" // for Transaction, Entity +#include "caosdb/transaction_status.h" // for TransactionStatus +#include <cstdint> // for int32_t +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-spi.h> // for EXPECT_NONFATAL_FA... +#include <gtest/gtest-test-part.h> // for TestPartResult +#include <gtest/gtest_pred_impl.h> // for AssertionResult +#include <iostream> // for operator<<, endl +#include <memory> // for unique_ptr, allocator +#include <string> // for operator+, operator<< +#include <vector> // for vector + +namespace caosdb::transaction { +using caosdb::entity::AtomicDataType; +using caosdb::entity::Entity; +using caosdb::entity::Parent; +using caosdb::entity::Role; + +class test_issues : public ::testing::Test { +public: + // public utility functions + // //////////////////////////////////////////////////////// + + static void DeleteEntities() { + // delete all entities + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + auto query_transaction(connection->CreateTransaction()); + query_transaction->Query("FIND ENTITY WITH id > 99"); + query_transaction->Execute(); + if (query_transaction->GetResultSet().size() > 0) { + std::cout << "Cleanup: Deleting " + << query_transaction->GetResultSet().size() << " entities." + << std::endl; + auto delete_transaction(connection->CreateTransaction()); + for (const Entity &entity : query_transaction->GetResultSet()) { + delete_transaction->DeleteById(entity.GetId()); + } + delete_transaction->Execute(); + } + } + +protected: + // Fixture methods ////////////////////////////////////////////////////////// + + void SetUp() override { DeleteEntities(); } + + void TearDown() override { DeleteEntities(); } +}; + +/* + * test error-prone updates + */ +TEST_F(test_issues, server_issue_170) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + // Insert original + auto insert_transaction(connection->CreateTransaction()); + + Entity original; + original.SetRole(Role::PROPERTY); + original.SetName("Prop 1"); + original.SetDataType(AtomicDataType::DOUBLE); + insert_transaction->InsertEntity(&original); + insert_transaction->ExecuteAsynchronously(); + auto insert_status = insert_transaction->WaitForIt(); + ASSERT_TRUE(insert_status.IsTerminated()); + ASSERT_FALSE(insert_status.IsError()); + + const auto &insert_result_set = insert_transaction->GetResultSet(); + auto id = insert_result_set.at(0).GetId(); + EXPECT_FALSE(id.empty()); + + // Retrieve original with ID + auto retrieve_transaction(connection->CreateTransaction()); + retrieve_transaction->RetrieveById(id); + retrieve_transaction->Execute(); + auto update_entity(retrieve_transaction->GetResultSet().at(0)); + + // UPDATE + auto update_transaction(connection->CreateTransaction()); + update_entity.SetDataType(AtomicDataType::INTEGER, true); + update_entity.SetValue(std::vector<int32_t>{1, 1, 2, 3, 5, 8, 13}); + + update_transaction->UpdateEntity(&update_entity); + update_transaction->ExecuteAsynchronously(); + + auto update_status = update_transaction->WaitForIt(); + EXPECT_TRUE(update_status.IsTerminated()); + EXPECT_FALSE(update_status.IsError()); +} + +/* + * Insert a Record with a parent, which has a wrong name. + * + * This must result in a server error. + */ +TEST_F(test_issues, server_issue_171) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + auto insert_transaction(connection->CreateTransaction()); + + // insert RT + Entity rt; + rt.SetRole(Role::RECORD_TYPE); + rt.SetName("Test_RT_Name"); + insert_transaction->InsertEntity(&rt); + insert_transaction->ExecuteAsynchronously(); + + auto insert_status = insert_transaction->WaitForIt(); + ASSERT_TRUE(insert_status.IsTerminated()); + ASSERT_FALSE(insert_status.IsError()); + + const auto &insert_result_set = insert_transaction->GetResultSet(); + const auto &inserted_rt = insert_result_set.at(0); + + // create Record with parent + Entity rec; + rec.SetRole(Role::RECORD); + rec.SetName("TestRec"); + + Parent parent; + parent.SetId(inserted_rt.GetId()); + parent.SetName(rt.GetName() + "_wrong"); + rec.AppendParent(parent); + + // insert Record + auto rec_transaction(connection->CreateTransaction()); + rec_transaction->InsertEntity(&rec); + rec_transaction->ExecuteAsynchronously(); + + auto rec_insert_status = rec_transaction->WaitForIt(); + ASSERT_TRUE(rec_insert_status.IsTerminated()); + EXPECT_NONFATAL_FAILURE({ EXPECT_TRUE(rec_insert_status.IsError()); }, + "rec_insert_status.IsError"); + + const auto &rec_result_set = rec_transaction->GetResultSet(); + const auto &inserted_rec = rec_result_set.at(0); + + std::cout << inserted_rec.ToString() << std::endl; +} + +/* + * Inserting a non-file property with an empty file path should work. + * + * The file attributes should be ignored by the server. + */ +TEST_F(test_issues, server_issue_174) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + auto insert_transaction(connection->CreateTransaction()); + + // Create and insert RT + Entity rt; + rt.SetRole(Role::RECORD_TYPE); + rt.SetName("Not a FILE"); + rt.SetFilePath(""); + insert_transaction->InsertEntity(&rt); + insert_transaction->ExecuteAsynchronously(); + + auto insert_status = insert_transaction->WaitForIt(); + ASSERT_TRUE(insert_status.IsTerminated()); + // TODO(tf) Remove the EXPECT_NONFATAL_FAILURE after fixing #174. + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(insert_status.IsError()), ""); +} + +} // namespace caosdb::transaction diff --git a/test/test_list_properties.cpp b/test/test_list_properties.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32f02e2fad8f46199d4240e86e9e1d31c86e936d --- /dev/null +++ b/test/test_list_properties.cpp @@ -0,0 +1,229 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ +#include "caosdb/connection.h" // for Connection, ConnectionManager +#include "caosdb/data_type.h" // for AtomicDataType +#include "caosdb/entity.h" // for Entity, Messages, Message +#include "caosdb/transaction.h" // for Entity, Transaction,... +#include "caosdb/transaction_status.h" // for TransactionStatus, StatusCode +#include "caosdb/value.h" // for value +#include <cstdint> // for int64_t +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver +#include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ, AssertionResult +#include <iostream> // for cout +#include <memory> // for unique_ptr, allocator, __shar... +#include <string> // for string +#include <vector> // for vector + +namespace caosdb::entity { + +class test_list_properties : public ::testing::Test { +protected: + void SetUp() override { DeleteEntities(); } + + void TearDown() override { DeleteEntities(); } + + static void DeleteEntities() { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + auto query_transaction(connection->CreateTransaction()); + query_transaction->Query("FIND ENTITY WITH id > 99"); + query_transaction->Execute(); + if (query_transaction->GetResultSet().size() > 0) { + auto delete_transaction(connection->CreateTransaction()); + for (const Entity &entity : query_transaction->GetResultSet()) { + delete_transaction->DeleteById(entity.GetId()); + } + delete_transaction->Execute(); + } + } +}; + +TEST_F(test_list_properties, insert_list_of_text) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + auto insertion_prop(connection->CreateTransaction()); + + Entity abstract_list_property; + abstract_list_property.SetRole(Role::PROPERTY); + abstract_list_property.SetName("TestProp"); + abstract_list_property.SetDataType(DataType::ListOf(AtomicDataType::TEXT)); + abstract_list_property.SetValue( + std::vector<std::string>{"item1", "item2", "item3"}); + + insertion_prop->InsertEntity(&abstract_list_property); + std::cout << "response " << insertion_prop->ResponseToString(); + insertion_prop->Execute(); + EXPECT_TRUE(insertion_prop->GetStatus().IsTerminated()); + + auto insertion_rt(connection->CreateTransaction()); + + Property list_property; + list_property.SetId(insertion_prop->GetResultSet().at(0).GetId()); + list_property.SetValue(std::vector<std::string>{"item4", "item5", "item6"}); + + Entity entity; + entity.SetRole(Role::RECORD_TYPE); + entity.SetName("TestRT"); + entity.AppendProperty(list_property); + + insertion_rt->InsertEntity(&entity); + std::cout << "response " << insertion_rt->ResponseToString(); + insertion_rt->Execute(); + EXPECT_TRUE(insertion_rt->GetStatus().IsTerminated()); + EXPECT_FALSE(insertion_rt->GetStatus().IsError()); + + // retrieve and check again + auto retrieval(connection->CreateTransaction()); + retrieval->RetrieveById(insertion_rt->GetResultSet().at(0).GetId()); + retrieval->Execute(); + + EXPECT_TRUE(retrieval->GetStatus().IsTerminated()); + EXPECT_FALSE(retrieval->GetStatus().IsError()); + + const auto &same_entity = retrieval->GetResultSet().at(0); + const auto &data_type = same_entity.GetProperties().at(0).GetDataType(); + const auto &value = same_entity.GetProperties().at(0).GetValue(); + + EXPECT_TRUE(data_type.IsList()); + EXPECT_TRUE(data_type.AsList().IsListOfAtomic()); + EXPECT_EQ(data_type.AsList().GetAtomicDataType(), AtomicDataType::TEXT); + + EXPECT_TRUE(value.IsList()); + EXPECT_EQ(value.AsList().size(), 3); + EXPECT_TRUE(value.AsList().at(1).IsString()); + EXPECT_EQ(value.AsList().at(1).AsString(), "item5"); +} + +TEST_F(test_list_properties, insert_list_of_int) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + auto insertion_prop(connection->CreateTransaction()); + + Entity abstract_list_property; + abstract_list_property.SetRole(Role::PROPERTY); + abstract_list_property.SetName("TestProp"); + abstract_list_property.SetDataType(DataType::ListOf(AtomicDataType::INTEGER)); + abstract_list_property.SetValue(std::vector<int64_t>{1, 2, 3}); + + insertion_prop->InsertEntity(&abstract_list_property); + std::cout << "response " << insertion_prop->ResponseToString(); + insertion_prop->Execute(); + EXPECT_TRUE(insertion_prop->GetStatus().IsTerminated()); + + auto insertion_rt(connection->CreateTransaction()); + + Property list_property; + list_property.SetId(insertion_prop->GetResultSet().at(0).GetId()); + list_property.SetValue(std::vector<int64_t>{4, 5, 6}); + + Entity entity; + entity.SetRole(Role::RECORD_TYPE); + entity.SetName("TestRT"); + entity.AppendProperty(list_property); + + insertion_rt->InsertEntity(&entity); + std::cout << "response " << insertion_rt->ResponseToString(); + insertion_rt->Execute(); + EXPECT_TRUE(insertion_rt->GetStatus().IsTerminated()); + EXPECT_FALSE(insertion_rt->GetStatus().IsError()); + + // retrieve and check again + auto retrieval(connection->CreateTransaction()); + retrieval->RetrieveById(insertion_rt->GetResultSet().at(0).GetId()); + retrieval->Execute(); + + EXPECT_TRUE(retrieval->GetStatus().IsTerminated()); + EXPECT_FALSE(retrieval->GetStatus().IsError()); + + const auto &same_entity = retrieval->GetResultSet().at(0); + const auto &data_type = same_entity.GetProperties().at(0).GetDataType(); + const auto &value = same_entity.GetProperties().at(0).GetValue(); + + EXPECT_TRUE(data_type.IsList()); + EXPECT_TRUE(data_type.AsList().IsListOfAtomic()); + EXPECT_EQ(data_type.AsList().GetAtomicDataType(), AtomicDataType::INTEGER); + + EXPECT_TRUE(value.IsList()); + EXPECT_EQ(value.AsList().size(), 3); + EXPECT_TRUE(value.AsList().at(1).IsInteger()); + EXPECT_EQ(value.AsList().at(1).AsInteger(), 5); +} + +TEST_F(test_list_properties, insert_list_of_bool) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + auto insertion_prop(connection->CreateTransaction()); + + Entity abstract_list_property; + abstract_list_property.SetRole(Role::PROPERTY); + abstract_list_property.SetName("TestProp"); + abstract_list_property.SetDataType(DataType::ListOf(AtomicDataType::BOOLEAN)); + abstract_list_property.SetValue(std::vector<bool>{true, true, false}); + + insertion_prop->InsertEntity(&abstract_list_property); + std::cout << "response " << insertion_prop->ResponseToString(); + insertion_prop->Execute(); + EXPECT_TRUE(insertion_prop->GetStatus().IsTerminated()); + + auto insertion_rt(connection->CreateTransaction()); + + Property list_property; + list_property.SetId(insertion_prop->GetResultSet().at(0).GetId()); + list_property.SetValue(std::vector<bool>{false, false, true}); + + Entity entity; + entity.SetRole(Role::RECORD_TYPE); + entity.SetName("TestRT"); + entity.AppendProperty(list_property); + + insertion_rt->InsertEntity(&entity); + std::cout << "response " << insertion_rt->ResponseToString(); + insertion_rt->Execute(); + EXPECT_TRUE(insertion_rt->GetStatus().IsTerminated()); + EXPECT_FALSE(insertion_rt->GetStatus().IsError()); + + // retrieve and check again + auto retrieval(connection->CreateTransaction()); + retrieval->RetrieveById(insertion_rt->GetResultSet().at(0).GetId()); + retrieval->Execute(); + + EXPECT_TRUE(retrieval->GetStatus().IsTerminated()); + EXPECT_FALSE(retrieval->GetStatus().IsError()); + + const auto &same_entity = retrieval->GetResultSet().at(0); + const auto &data_type = same_entity.GetProperties().at(0).GetDataType(); + const auto &value = same_entity.GetProperties().at(0).GetValue(); + + EXPECT_TRUE(data_type.IsList()); + EXPECT_TRUE(data_type.AsList().IsListOfAtomic()); + EXPECT_EQ(data_type.AsList().GetAtomicDataType(), AtomicDataType::BOOLEAN); + + EXPECT_TRUE(value.IsList()); + EXPECT_EQ(value.AsList().size(), 3); + EXPECT_TRUE(value.AsList().at(1).IsBool()); + EXPECT_FALSE(value.AsList().at(1).AsBool()); +} + +} // namespace caosdb::entity diff --git a/test/test_properties.cpp b/test/test_properties.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6137494905b5fe0e5c04aa256a45648f355a8b91 --- /dev/null +++ b/test/test_properties.cpp @@ -0,0 +1,106 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ +#include "caosdb/connection.h" // for Connection, ConnectionManager +#include "caosdb/data_type.h" // for AtomicDataType, AtomicDataTyp... +#include "caosdb/entity.h" // for Entity, Properties, Property +#include "caosdb/transaction.h" // for Transaction, ResultSet, Resul... +#include "caosdb/transaction_status.h" // for TransactionStatus +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver +#include <gtest/gtest_pred_impl.h> // for AssertionResult, EXPECT_EQ +#include <memory> // for unique_ptr, allocator, __shar... + +namespace caosdb::entity { + +class test_properties : public ::testing::Test { +protected: + void SetUp() override { DeleteEntities(); } + + void TearDown() override { DeleteEntities(); } + + static void DeleteEntities() { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + auto query_transaction(connection->CreateTransaction()); + query_transaction->Query("FIND ENTITY WITH id > 99"); + query_transaction->Execute(); + if (query_transaction->GetResultSet().size() > 0) { + auto delete_transaction(connection->CreateTransaction()); + for (const Entity &entity : query_transaction->GetResultSet()) { + delete_transaction->DeleteById(entity.GetId()); + } + delete_transaction->Execute(); + } + } +}; + +TEST_F(test_properties, retrieve_unit) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + auto insertion_prop(connection->CreateTransaction()); + + Entity abstract_property; + abstract_property.SetRole(Role::PROPERTY); + abstract_property.SetName("TestProp"); + abstract_property.SetDataType(AtomicDataType::DOUBLE); + abstract_property.SetUnit("V"); + + insertion_prop->InsertEntity(&abstract_property); + insertion_prop->Execute(); + EXPECT_TRUE(insertion_prop->GetStatus().IsTerminated()); + + auto insertion_rt(connection->CreateTransaction()); + + Property property; + property.SetId(insertion_prop->GetResultSet().at(0).GetId()); + + Entity entity; + entity.SetRole(Role::RECORD_TYPE); + entity.SetName("TestRT"); + entity.AppendProperty(property); + + insertion_rt->InsertEntity(&entity); + insertion_rt->Execute(); + EXPECT_TRUE(insertion_rt->GetStatus().IsTerminated()); + EXPECT_FALSE(insertion_rt->GetStatus().IsError()); + + // retrieve and check again + auto retrieval(connection->CreateTransaction()); + retrieval->RetrieveById(insertion_prop->GetResultSet().at(0).GetId()); + retrieval->RetrieveById(insertion_rt->GetResultSet().at(0).GetId()); + retrieval->ExecuteAsynchronously(); + retrieval->WaitForIt(); + + EXPECT_TRUE(retrieval->GetStatus().IsTerminated()); + EXPECT_FALSE(retrieval->GetStatus().IsError()); + + const auto &same_property = retrieval->GetResultSet().at(0); + EXPECT_EQ(same_property.GetDataType().AsAtomic(), AtomicDataType::DOUBLE); + EXPECT_EQ(same_property.GetUnit(), "V"); + + const auto &same_record_type = retrieval->GetResultSet().at(1); + EXPECT_EQ(same_record_type.GetProperties().at(0).GetDataType().AsAtomic(), + AtomicDataType::DOUBLE); + EXPECT_EQ(same_record_type.GetProperties().at(0).GetUnit(), "V"); +} + +} // namespace caosdb::entity diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index 008093a2720ea7de89d2c03e06f65b92082cf5e1..13ffb647dbbc8eb152b1683309032459611927cb 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -1,8 +1,9 @@ /* * This file is a part of the CaosDB Project. * - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,94 +19,146 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ -#include "caosdb/connection.h" // for Connection, ConnectionManager -#include "caosdb/entity.h" // for Entity, Messages, Message -#include "caosdb/file_transmission/file_reader.h" // for FileReader +#include "caosdb/connection.h" // for Connection, Connec... +#include "caosdb/data_type.h" // for AtomicDataType +#include "caosdb/entity.h" // for Entity, Property +#include "caosdb/file_transmission/file_reader.h" // for path, FileReader #include "caosdb/file_transmission/file_writer.h" // for FileWriter -#include "caosdb/message_code.h" // for ENTITY_DOES_NOT_EXIST, Messag... -#include "caosdb/status_code.h" // for SUCCESS, StatusCode -#include "caosdb/transaction.h" // for Entity, Transaction,... -#include "caosdb/transaction_status.h" // for TransactionStatus, StatusCode -#include <boost/filesystem/operations.hpp> // for remove -#include <boost/filesystem/path.hpp> // for path -#include <boost/filesystem/path_traits.hpp> // for filesystem -#include <gtest/gtest-message.h> // for Message -#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver -#include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ, AssertionResult -#include <iostream> -#include <memory> // for unique_ptr, allocator, __shar... -#include <string> // for string -#include <vector> // for vector +#include "caosdb/message_code.h" // for MessageCode, ENTIT... +#include "caosdb/status_code.h" // for StatusCode, SUCCESS +#include "caosdb/transaction.h" // for Entity, Transaction +#include "caosdb/transaction_status.h" // for TransactionStatus +#include "caosdb/value.h" // for Value +#include <boost/filesystem/operations.hpp> // for remove +#include <boost/filesystem/path.hpp> // for path +#include <boost/filesystem/path_traits.hpp> // for filesystem +#include <cstddef> // for size_t +#include <cstdint> // for int64_t, int32_t +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult +#include <gtest/gtest_pred_impl.h> // for AssertionResult +#include <iostream> // for operator<<, endl +#include <limits> // for numeric_limits +#include <memory> // for unique_ptr, allocator +#include <stdexcept> // for logic_error +#include <string> // for string, operator+ +#include <vector> // for vector, operator!= namespace fs = boost::filesystem; namespace caosdb::transaction { +using caosdb::entity::AtomicDataType; using caosdb::entity::Entity; +using caosdb::entity::Importance; using caosdb::entity::MessageCode; using caosdb::entity::Parent; using caosdb::entity::Property; +using caosdb::entity::Role; +using caosdb::entity::Value; class test_transaction : public ::testing::Test { +public: + // public utility functions + // //////////////////////////////////////////////////////// + + /** + * Generate a vector with useful values for testing. + */ + template <typename T> static auto generateValues() -> std::vector<T> { + std::vector<T> values = { + static_cast<T>(0), + static_cast<T>(1), + static_cast<T>(6.91629132943846e-310), + std::numeric_limits<T>::max(), + std::numeric_limits<T>::min(), + std::numeric_limits<T>::denorm_min(), + std::numeric_limits<T>::lowest(), + std::numeric_limits<T>::epsilon() // 0 for integers, but who cares? + }; + + return values; + } + + template <typename T> static auto getValueAs(const Value & /*value*/) -> T { + throw std::logic_error("Template not implemented for this type."); + } + static void DeleteEntities() { + // delete all entities + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + auto query_transaction(connection->CreateTransaction()); + query_transaction->Query("FIND ENTITY WITH id > 99"); + query_transaction->Execute(); + if (query_transaction->GetResultSet().size() > 0) { + std::cout << "Cleanup: Deleting " + << query_transaction->GetResultSet().size() << " entities." + << std::endl; + auto delete_transaction(connection->CreateTransaction()); + for (const Entity &entity : query_transaction->GetResultSet()) { + delete_transaction->DeleteById(entity.GetId()); + } + delete_transaction->Execute(); + } + } + protected: fs::path test_upload_file_1; fs::path test_download_file_1; + size_t test_file_size_kib = + 20; // We should test at least something over 16kiB + + // Fixture methods ////////////////////////////////////////////////////////// void SetUp() override { + DeleteEntities(); + test_upload_file_1 = fs::path("test_upload_file_1_delete_me.dat"); test_download_file_1 = fs::path("test_download_file_1_delete_me.dat"); + // fill the file that shall be uploaded FileWriter writer(test_upload_file_1); std::string buffer(1024, 'c'); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < test_file_size_kib; i++) { writer.write(buffer); } } void TearDown() override { + // delete files fs::remove(test_upload_file_1); fs::remove(test_download_file_1); - - const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); - auto query_transaction(connection->CreateTransaction()); - query_transaction->Query("FIND ENTITY WITH id > 99"); - query_transaction->Execute(); - if (query_transaction->GetResultSet().size() > 0) { - auto delete_transaction(connection->CreateTransaction()); - for (const Entity &entity : query_transaction->GetResultSet()) { - delete_transaction->DeleteById(entity.GetId()); - } - delete_transaction->Execute(); - } + DeleteEntities(); } }; -TEST_F(test_transaction, DISABLED_retrieve_manufacturer_by_id) { - const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); - - const auto *id = "107"; - const auto *role = "RecordType"; - const auto *name = "Manufacturer"; - const auto *description = "A generic manufacturer of all kinds of products"; - const auto *version = "0bea8f7b17f0130fa5701a6c3849b9f8bfa0651b"; +template <> +auto test_transaction::getValueAs<double>(const Value &value) -> double { + return value.AsDouble(); +} - auto transaction(connection->CreateTransaction()); - transaction->RetrieveById(id); - transaction->Execute(); +template <> +auto test_transaction::getValueAs<int64_t>(const Value &value) -> int64_t { + return value.AsInteger(); +} - const auto &result_set = transaction->GetResultSet(); +template <> +auto test_transaction::getValueAs<int32_t>(const Value &value) -> int32_t { + return static_cast<int32_t>(value.AsInteger()); +} - const auto &entity = result_set.at(0); - EXPECT_EQ(id, entity.GetId()); - EXPECT_EQ(name, entity.GetName()); - EXPECT_EQ(role, entity.GetRole()); - EXPECT_EQ(description, entity.GetDescription()); - EXPECT_EQ(version, entity.GetVersionId()); +template <> +auto test_transaction::getValueAs<bool>(const Value &value) -> bool { + return value.AsBool(); } +/* + * Test the retrieval of a non-existing entity + * + * The transaction returns an error code and the entity has the appropriate + * error message + */ TEST_F(test_transaction, retrieve_non_existing) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); auto transaction(connection->CreateTransaction()); @@ -127,14 +180,20 @@ TEST_F(test_transaction, retrieve_non_existing) { MessageCode::ENTITY_DOES_NOT_EXIST); } +/* + * Testing the insertion of a basic entity + * Transaction should terminate without errors. + * Returned entity should have an id, no errors and a warning since no property + * is set. + */ TEST_F(test_transaction, insert_without_delete) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); auto insert_transaction(connection->CreateTransaction()); Entity entity; - entity.SetRole("RecordType"); + entity.SetRole(Role::RECORD_TYPE); entity.SetName("RT1"); insert_transaction->InsertEntity(&entity); insert_transaction->ExecuteAsynchronously(); @@ -156,14 +215,20 @@ TEST_F(test_transaction, insert_without_delete) { MessageCode::ENTITY_HAS_NO_PROPERTIES); } +/* + * Test deletion of an entity + * Insert an entity first. + */ TEST_F(test_transaction, insert_delete) { + // same as in insert_without_delete + // until marked END SAME const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); auto insert_transaction(connection->CreateTransaction()); Entity entity; - entity.SetRole("RecordType"); + entity.SetRole(Role::RECORD_TYPE); entity.SetName("RT1"); insert_transaction->InsertEntity(&entity); insert_transaction->ExecuteAsynchronously(); @@ -177,12 +242,7 @@ TEST_F(test_transaction, insert_delete) { const auto &new_entity = insert_result_set.at(0); EXPECT_FALSE(new_entity.GetId().empty()); - EXPECT_FALSE(new_entity.HasErrors()); - // Should have a warning since it has no properties - EXPECT_TRUE(new_entity.HasWarnings()); - EXPECT_EQ(new_entity.GetWarnings().size(), 1); - EXPECT_EQ(new_entity.GetWarnings().at(0).GetCode(), - MessageCode::ENTITY_HAS_NO_PROPERTIES); + // END SAME auto delete_transaction(connection->CreateTransaction()); @@ -201,14 +261,20 @@ TEST_F(test_transaction, insert_delete) { EXPECT_FALSE(deleted_entity.HasErrors()); } +/* + * Insert a Record with a parent, retrieve and delete it again + */ TEST_F(test_transaction, insert_delete_with_parent) { + // same as in insert_without_delete + // until marked END SAME const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); auto insert_transaction(connection->CreateTransaction()); + // insert RT Entity rt; - rt.SetRole("RecordType"); + rt.SetRole(Role::RECORD_TYPE); rt.SetName("TestRT"); insert_transaction->InsertEntity(&rt); insert_transaction->ExecuteAsynchronously(); @@ -221,9 +287,11 @@ TEST_F(test_transaction, insert_delete_with_parent) { const auto &insert_result_set = insert_transaction->GetResultSet(); const auto &inserted_rt = insert_result_set.at(0); + // END SAME + // create Record with parent Entity rec; - rec.SetRole("Record"); + rec.SetRole(Role::RECORD); rec.SetName("TestRec"); Parent parent; @@ -231,6 +299,7 @@ TEST_F(test_transaction, insert_delete_with_parent) { parent.SetId(inserted_rt.GetId()); rec.AppendParent(parent); + // insert Record auto rec_transaction(connection->CreateTransaction()); rec_transaction->InsertEntity(&rec); rec_transaction->ExecuteAsynchronously(); @@ -246,6 +315,7 @@ TEST_F(test_transaction, insert_delete_with_parent) { EXPECT_FALSE(inserted_rec.GetId().empty()); + // retrieve inserted Record auto retrieve_transaction(connection->CreateTransaction()); retrieve_transaction->RetrieveById(inserted_rec.GetId()); @@ -259,11 +329,13 @@ TEST_F(test_transaction, insert_delete_with_parent) { const auto &retrieve_result_set = retrieve_transaction->GetResultSet(); const auto &retrieved_rec = retrieve_result_set.at(0); + // compare retrieved record with the one that was inserted EXPECT_EQ(retrieved_rec.GetName(), rec.GetName()); EXPECT_EQ(retrieved_rec.GetParents().size(), 1); EXPECT_EQ(retrieved_rec.GetParents().at(0).GetId(), inserted_rt.GetId()); EXPECT_EQ(retrieved_rec.GetParents().at(0).GetName(), rt.GetName()); + // delete the inserted record again auto rec_deletion(connection->CreateTransaction()); rec_deletion->DeleteById(retrieved_rec.GetId()); @@ -274,6 +346,7 @@ TEST_F(test_transaction, insert_delete_with_parent) { ASSERT_TRUE(rec_delete_status.IsTerminated()); ASSERT_FALSE(rec_delete_status.IsError()); + // delete the record type again auto rt_deletion(connection->CreateTransaction()); rt_deletion->DeleteById(inserted_rt.GetId()); @@ -285,15 +358,18 @@ TEST_F(test_transaction, insert_delete_with_parent) { ASSERT_FALSE(rt_delete_status.IsError()); } +/* + * insert a property, record type with that property and a record with it + */ TEST_F(test_transaction, insert_delete_with_property) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); - // Create and insert property + // Create and insert Text property Entity prop_ent; - prop_ent.SetRole("Property"); + prop_ent.SetRole(Role::PROPERTY); prop_ent.SetName("TestProperty"); - prop_ent.SetDatatype("TEXT"); + prop_ent.SetDataType(AtomicDataType::TEXT); auto prop_insertion(connection->CreateTransaction()); prop_insertion->InsertEntity(&prop_ent); @@ -309,17 +385,44 @@ TEST_F(test_transaction, insert_delete_with_property) { const auto &inserted_prop = prop_result_set.at(0); EXPECT_FALSE(inserted_prop.GetId().empty()); - // create and insert record type with the above property + // Create and insert Double property + Entity prop_double_ent; + prop_double_ent.SetRole(Role::PROPERTY); + prop_double_ent.SetName("TestDoubleProperty"); + prop_double_ent.SetDataType(AtomicDataType::DOUBLE); + + auto prop_double_insertion = connection->CreateTransaction(); + prop_double_insertion->InsertEntity(&prop_double_ent); + prop_double_insertion->ExecuteAsynchronously(); + + prop_insert_status = prop_double_insertion->WaitForIt(); + + ASSERT_TRUE(prop_insert_status.IsTerminated()); + ASSERT_FALSE(prop_insert_status.IsError()); + + const auto &prop_double_result_set = prop_double_insertion->GetResultSet(); + + const auto &inserted_prop_double = prop_double_result_set.at(0); + + EXPECT_FALSE(inserted_prop_double.GetId().empty()); + + // create and insert record type with the above properties Property prop_rt; prop_rt.SetName(prop_ent.GetName()); prop_rt.SetId(inserted_prop.GetId()); - prop_rt.SetImportance("SUGGESTED"); + prop_rt.SetImportance(Importance::SUGGESTED); + + Property prop_double; + prop_double.SetName(prop_double_ent.GetName()); + prop_double.SetId(inserted_prop_double.GetId()); + prop_double.SetImportance(Importance::SUGGESTED); Entity rt; - rt.SetRole("RecordType"); + rt.SetRole(Role::RECORD_TYPE); rt.SetName("TestRT"); rt.SetDescription("Some description"); rt.AppendProperty(prop_rt); + rt.AppendProperty(prop_double); auto rt_insertion(connection->CreateTransaction()); rt_insertion->InsertEntity(&rt); @@ -350,13 +453,19 @@ TEST_F(test_transaction, insert_delete_with_property) { EXPECT_EQ(inserted_rt.GetId(), retrieved_rt.GetId()); EXPECT_EQ(rt.GetName(), retrieved_rt.GetName()); EXPECT_EQ(rt.GetDescription(), retrieved_rt.GetDescription()); - EXPECT_EQ(retrieved_rt.GetProperties().size(), 1); - - const auto &retrieved_prop_rt = retrieved_rt.GetProperties().at(0); - EXPECT_EQ(retrieved_prop_rt.GetName(), prop_ent.GetName()); - EXPECT_EQ(retrieved_prop_rt.GetId(), inserted_prop.GetId()); - EXPECT_EQ(retrieved_prop_rt.GetDatatype(), prop_ent.GetDatatype()); - EXPECT_EQ(retrieved_prop_rt.GetImportance(), prop_rt.GetImportance()); + EXPECT_EQ(retrieved_rt.GetProperties().size(), 2); + + const Property *retrieved_prop_rt = nullptr; + // get by name is not possible yet + if (retrieved_rt.GetProperties().at(0).GetName() == prop_rt.GetName()) { + retrieved_prop_rt = &retrieved_rt.GetProperties().at(0); + } else { + retrieved_prop_rt = &retrieved_rt.GetProperties().at(1); + } + EXPECT_EQ(retrieved_prop_rt->GetName(), prop_rt.GetName()); + EXPECT_EQ(retrieved_prop_rt->GetId(), inserted_prop.GetId()); + EXPECT_EQ(retrieved_prop_rt->GetDataType(), prop_ent.GetDataType()); + EXPECT_EQ(retrieved_prop_rt->GetImportance(), prop_rt.GetImportance()); // create and insert record of the above record type with a property // with a value. @@ -367,13 +476,19 @@ TEST_F(test_transaction, insert_delete_with_property) { Property prop_rec; prop_rec.SetName(prop_ent.GetName()); prop_rec.SetId(inserted_prop.GetId()); - prop_rec.SetValue("Test"); + prop_rec.SetValue(std::string("Test")); + + Property prop_double_rec; + prop_double_rec.SetName(prop_double_ent.GetName()); + prop_double_rec.SetId(inserted_prop_double.GetId()); + prop_double_rec.SetValue(123456789.98700001); Entity rec; rec.SetName("TestRec"); - rec.SetRole("Record"); + rec.SetRole(Role::RECORD); rec.AppendParent(parent); rec.AppendProperty(prop_rec); + rec.AppendProperty(prop_double_rec); auto rec_insertion(connection->CreateTransaction()); rec_insertion->InsertEntity(&rec); @@ -389,7 +504,7 @@ TEST_F(test_transaction, insert_delete_with_property) { const auto &inserted_rec = rec_result_set.at(0); EXPECT_FALSE(inserted_rec.GetId().empty()); - // Retrieve the record and verify paretn and property + // Retrieve the record and verify parent and property auto rec_retrieval(connection->CreateTransaction()); rec_retrieval->RetrieveById(inserted_rec.GetId()); rec_retrieval->ExecuteAsynchronously(); @@ -404,23 +519,45 @@ TEST_F(test_transaction, insert_delete_with_property) { EXPECT_EQ(rec.GetName(), retrieved_rec.GetName()); EXPECT_EQ(inserted_rec.GetId(), retrieved_rec.GetId()); EXPECT_EQ(retrieved_rec.GetParents().size(), 1); - EXPECT_EQ(retrieved_rec.GetProperties().size(), 1); + EXPECT_EQ(retrieved_rec.GetProperties().size(), 2); const auto &retrieved_parent_rec = retrieved_rec.GetParents().at(0); EXPECT_EQ(retrieved_parent_rec.GetName(), rt.GetName()); EXPECT_EQ(retrieved_parent_rec.GetId(), inserted_rt.GetId()); EXPECT_EQ(retrieved_parent_rec.GetDescription(), rt.GetDescription()); - const auto &retrieved_prop_rec = retrieved_rec.GetProperties().at(0); - EXPECT_EQ(retrieved_prop_rec.GetName(), prop_ent.GetName()); - EXPECT_EQ(retrieved_prop_rec.GetId(), inserted_prop.GetId()); - EXPECT_EQ(retrieved_prop_rec.GetDatatype(), prop_ent.GetDatatype()); - EXPECT_EQ(retrieved_prop_rec.GetValue(), prop_rec.GetValue()); + const auto &retrieved_prop_0 = retrieved_rec.GetProperties().at(0); + const auto &retrieved_prop_1 = retrieved_rec.GetProperties().at(1); + + const Property *retrieved_prop_rec = nullptr; + const Property *retrieved_prop_double = nullptr; + if (retrieved_prop_0.GetName() == prop_ent.GetName()) { + retrieved_prop_rec = &retrieved_prop_0; + retrieved_prop_double = &retrieved_prop_1; + } else { + retrieved_prop_rec = &retrieved_prop_1; + retrieved_prop_double = &retrieved_prop_0; + } + + EXPECT_EQ(retrieved_prop_rec->GetName(), prop_ent.GetName()); + EXPECT_EQ(retrieved_prop_rec->GetId(), inserted_prop.GetId()); + EXPECT_EQ(retrieved_prop_rec->GetDataType(), prop_ent.GetDataType()); + EXPECT_EQ(retrieved_prop_rec->GetValue(), prop_rec.GetValue()); + + EXPECT_EQ(retrieved_prop_double->GetName(), prop_double_ent.GetName()); + EXPECT_EQ(retrieved_prop_double->GetId(), inserted_prop_double.GetId()); + EXPECT_EQ(retrieved_prop_double->GetDataType(), + prop_double_ent.GetDataType()); + EXPECT_TRUE(retrieved_prop_double->GetValue().IsDouble()); + EXPECT_EQ(retrieved_prop_double->GetValue(), prop_double_rec.GetValue()); } +/* + * test retrieving multiple entities at once + */ TEST_F(test_transaction, test_multi_retrieve) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); auto transaction(connection->CreateTransaction()); @@ -449,15 +586,18 @@ TEST_F(test_transaction, test_multi_retrieve) { MessageCode::ENTITY_DOES_NOT_EXIST); } +/* + * test insert, then update and then delete a RecordType + */ TEST_F(test_transaction, insert_update_delete) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); // INSERT auto insert_transaction(connection->CreateTransaction()); Entity entity; - entity.SetRole("RecordType"); + entity.SetRole(Role::RECORD_TYPE); entity.SetName("RT1"); insert_transaction->InsertEntity(&entity); insert_transaction->ExecuteAsynchronously(); @@ -514,14 +654,17 @@ TEST_F(test_transaction, insert_update_delete) { EXPECT_FALSE(deleted_entity.HasErrors()); } +/* + * test insert a RecordType and then submit a find and a count query + */ TEST_F(test_transaction, test_query) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); auto insert_transaction(connection->CreateTransaction()); Entity entity; - entity.SetRole("RecordType"); + entity.SetRole(Role::RECORD_TYPE); entity.SetName("RT1"); insert_transaction->InsertEntity(&entity); insert_transaction->ExecuteAsynchronously(); @@ -556,13 +699,137 @@ TEST_F(test_transaction, test_query) { EXPECT_EQ(count_query_trans->GetCountResult(), 1); } +/** + * Test numeric values (template). + */ +template <typename T, typename S> +auto test_numeric_values_impl(AtomicDataType a_type) -> void { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + // Insert entities + auto values_orig = test_transaction::generateValues<T>(); + auto props_orig = std::vector<Entity>(); + size_t i = 0; + for (auto value : values_orig) { + auto insert_transaction(connection->CreateTransaction()); + Entity prop; + prop.SetRole(Role::PROPERTY); + const auto name = std::string("Prop ") + std::to_string(i); + std::cout << "Creating: " << name << std::endl; + prop.SetName(name); + prop.SetDataType(a_type); + std::cout << "Setting value " << value << std::endl; + prop.SetValue(static_cast<S>(value)); + props_orig.push_back(prop); + auto i_stat = insert_transaction->InsertEntity(&prop); + EXPECT_EQ(i_stat, StatusCode::READY); + insert_transaction->ExecuteAsynchronously(); + auto t_stat = insert_transaction->WaitForIt(); + EXPECT_TRUE(t_stat.IsTerminated()); + EXPECT_FALSE(t_stat.IsError()); + ++i; + } + + // Retrieve and verify + i = 0; + for (const auto value : values_orig) { + auto retrieve_transaction(connection->CreateTransaction()); + const auto prop = props_orig[i]; + const auto name = std::string("Prop ") + std::to_string(i); + std::cout << "Retrieving: " << name << std::endl; + const auto query = std::string("FIND ENTITY \"") + name + "\""; + retrieve_transaction->Query(query); + retrieve_transaction->ExecuteAsynchronously(); + const auto t_stat = retrieve_transaction->WaitForIt(); + EXPECT_TRUE(t_stat.IsTerminated()); + EXPECT_FALSE(t_stat.IsError()); + + if (retrieve_transaction->GetResultSet().size() > 0) { + const auto result = retrieve_transaction->GetResultSet().at(0); + EXPECT_EQ(result.GetDataType(), a_type); + const auto &retrieved_value = + test_transaction::getValueAs<T>(result.GetValue()); + // std::cout << "retrieved_value: " << retrieved_value << std::endl; + EXPECT_EQ(retrieved_value, value); + } + ++i; + } +} + +/** + * Test numeric values (wrapper for types). + */ +TEST_F(test_transaction, test_numeric_values) { + test_numeric_values_impl<double, double>(AtomicDataType::DOUBLE); + test_transaction::DeleteEntities(); + test_numeric_values_impl<int32_t, int64_t>(AtomicDataType::INTEGER); + test_transaction::DeleteEntities(); + test_numeric_values_impl<bool, bool>(AtomicDataType::BOOLEAN); +} + +TEST_F(test_transaction, test_integer_out_of_range) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + // Insert entities + std::vector<int64_t> values = {std::numeric_limits<int64_t>::max(), + std::numeric_limits<int64_t>::min()}; + for (auto value : values) { + auto insert_transaction(connection->CreateTransaction()); + Entity prop; + + prop.SetRole(Role::PROPERTY); + const auto name = std::string("Prop_") + std::to_string(value); + + prop.SetName(name); + prop.SetDataType(AtomicDataType::INTEGER); + prop.SetValue(value); + + auto i_stat = insert_transaction->InsertEntity(&prop); + EXPECT_EQ(i_stat, StatusCode::READY); + + insert_transaction->ExecuteAsynchronously(); + auto t_stat = insert_transaction->WaitForIt(); + EXPECT_TRUE(t_stat.IsTerminated()); + EXPECT_TRUE(t_stat.IsError()); + EXPECT_EQ(t_stat.GetCode(), StatusCode::GENERIC_TRANSACTION_ERROR); + EXPECT_EQ(insert_transaction->GetResultSet().size(), 1); + EXPECT_TRUE(insert_transaction->GetResultSet().at(0).HasErrors()); + EXPECT_EQ( + insert_transaction->GetResultSet().at(0).GetErrors().at(0).GetCode(), + MessageCode::INTEGER_VALUE_OUT_OF_RANGE); + } +} + +// /* +// * test miscellaneous queries +// */ +// TEST_F(test_transaction, test_queries_misc) { +// const auto &connection = +// caosdb::connection::ConnectionManager::GetDefaultConnection(); + +// // query empty database +// auto query_transaction(connection->CreateTransaction()); +// query_transaction->Query("FIND Property \"Prop *\""); +// query_transaction->ExecuteAsynchronously(); +// auto t_stat = query_transaction->WaitForIt(); +// std::cout << "status: " << t_stat.GetCode() << " // " +// << t_stat.GetDescription() << std::endl; +// EXPECT_TRUE(t_stat.GetCode() >= 0); +// } + +/* + * insert three recordtypes and the submit multiple queries in different + * combinations + */ TEST_F(test_transaction, test_query_with_retrieve) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); // rt1 Entity rt1; - rt1.SetRole("RecordType"); + rt1.SetRole(Role::RECORD_TYPE); rt1.SetName("TestRT1"); auto insert_rt1_transaction(connection->CreateTransaction()); @@ -582,7 +849,7 @@ TEST_F(test_transaction, test_query_with_retrieve) { // rt2 Entity rt2; - rt2.SetRole("RecordType"); + rt2.SetRole(Role::RECORD_TYPE); rt2.SetName("TestRT2"); auto insert_rt2_transaction(connection->CreateTransaction()); @@ -602,7 +869,7 @@ TEST_F(test_transaction, test_query_with_retrieve) { // rt3 Entity rt3; - rt3.SetRole("RecordType"); + rt3.SetRole(Role::RECORD_TYPE); rt3.SetName("TestRT3"); auto insert_rt3_transaction(connection->CreateTransaction()); @@ -668,12 +935,15 @@ TEST_F(test_transaction, test_query_with_retrieve) { EXPECT_EQ(count_and_retrieve->GetCountResult(), 3); } +/* + * create a file object and upload it + */ TEST_F(test_transaction, test_file_upload) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); Entity file; - file.SetRole("File"); + file.SetRole(Role::FILE); file.SetFilePath("test.txt"); file.SetLocalPath(test_upload_file_1); @@ -702,12 +972,15 @@ TEST_F(test_transaction, test_file_upload) { EXPECT_EQ(count_query->GetCountResult(), 1); } +/* + * create a file object, upload and then download it + */ TEST_F(test_transaction, test_file_up_n_download) { const auto &connection = - caosdb::connection::ConnectionManager::GetDefaultConnection(); + caosdb::connection::ConnectionManager::GetDefaultConnection(); Entity file; - file.SetRole("File"); + file.SetRole(Role::FILE); file.SetFilePath("test.txt"); file.SetLocalPath(test_upload_file_1); @@ -722,8 +995,8 @@ TEST_F(test_transaction, test_file_up_n_download) { ASSERT_FALSE(inserted_file.HasErrors()); auto download_transaction(connection->CreateTransaction()); - download_transaction->RetrieveAndDownloadFilesById( - inserted_file.GetId(), test_download_file_1.string()); + download_transaction->RetrieveAndDownloadFileById( + inserted_file.GetId(), test_download_file_1.string()); download_transaction->ExecuteAsynchronously(); ASSERT_EQ(download_transaction->WaitForIt().GetCode(), StatusCode::SUCCESS); @@ -735,14 +1008,147 @@ TEST_F(test_transaction, test_file_up_n_download) { ASSERT_FALSE(downloaded_file.HasErrors()); EXPECT_EQ(downloaded_file.GetLocalPath().string(), test_download_file_1.string()); + EXPECT_EQ(fs::file_size(test_upload_file_1), + fs::file_size(test_download_file_1)); + // test_download_file_1 - FileReader reader_remote(test_upload_file_1); + FileReader reader_remote(test_download_file_1); std::string buffer_local(1024, 'c'); std::string buffer_remote(1024, 'c'); - for (int i = 0; i < 8; i++) { + std::cout << "Reading kiB chunk"; + for (size_t i = 0; i < test_file_size_kib; i++) { + std::cout << " #" << i + 1 << "/" << test_file_size_kib; reader_remote.read(buffer_remote); EXPECT_EQ(buffer_remote, buffer_local); } + std::cout << std::endl; +} + +/* + * Test a small worklfow + */ +TEST_F(test_transaction, test_full_workflow) { + const auto &connection = + caosdb::connection::ConnectionManager::GetDefaultConnection(); + + // ###### Create Data Model ###### + Entity dataRT; + dataRT.SetRole(Role::RECORD_TYPE); + dataRT.SetName("DataRT"); + + Entity voltage; + voltage.SetRole(Role::PROPERTY); + voltage.SetName("voltage"); + voltage.SetUnit("V"); + voltage.SetDataType(AtomicDataType::DOUBLE); + + Entity notes; + notes.SetRole(Role::PROPERTY); + notes.SetName("notes"); + notes.SetDataType(AtomicDataType::TEXT); + + Entity participants; + participants.SetRole(Role::PROPERTY); + participants.SetName("participants"); + participants.SetDataType(AtomicDataType::INTEGER); + // participants.SetDataType(ListDataType::); + + Entity success; + success.SetRole(Role::PROPERTY); + success.SetName("success"); + success.SetDataType(AtomicDataType::BOOLEAN); + + Entity experiment; + experiment.SetRole(Role::RECORD_TYPE); + experiment.SetName("Experiment"); + // TODO(henrik): creating this extra Property (additionally to the Entity + // above) is VERY clumsy + Property volt_for_rt; + volt_for_rt.SetName(voltage.GetName()); + volt_for_rt.SetImportance(Importance::RECOMMENDED); + experiment.AppendProperty(volt_for_rt); + Property notes_for_rt2; + notes_for_rt2.SetName(notes.GetName()); + notes_for_rt2.SetImportance(Importance::RECOMMENDED); + experiment.AppendProperty(notes_for_rt2); + Property part_for_rt3; + part_for_rt3.SetName(participants.GetName()); + part_for_rt3.SetImportance(Importance::SUGGESTED); + experiment.AppendProperty(part_for_rt3); + Property succ_for_rt; + succ_for_rt.SetName(success.GetName()); + succ_for_rt.SetImportance(Importance::SUGGESTED); + experiment.AppendProperty(succ_for_rt); + + auto insert_transaction(connection->CreateTransaction()); + insert_transaction->InsertEntity(&dataRT); + auto insert_status = insert_transaction->Execute(); + const auto &insert_results = insert_transaction->GetResultSet(); + const auto &inserted_ent = insert_results.at(0); + ASSERT_FALSE(inserted_ent.GetId().empty()); + ASSERT_FALSE(inserted_ent.HasErrors()); + + insert_transaction = connection->CreateTransaction(); + insert_transaction->InsertEntity(&voltage); + insert_status = insert_transaction->Execute(); + insert_transaction = connection->CreateTransaction(); + insert_transaction->InsertEntity(¬es); + insert_status = insert_transaction->Execute(); + insert_transaction = connection->CreateTransaction(); + insert_transaction->InsertEntity(&participants); + insert_status = insert_transaction->Execute(); + insert_transaction = connection->CreateTransaction(); + insert_transaction->InsertEntity(&success); + insert_status = insert_transaction->Execute(); + insert_transaction = connection->CreateTransaction(); + insert_transaction->InsertEntity(&experiment); + insert_status = insert_transaction->Execute(); + + auto retr_transaction(connection->CreateTransaction()); + retr_transaction->Query("FIND Experiment"); + retr_transaction->Execute(); + EXPECT_EQ(retr_transaction->GetResultSet().size(), 1); + + Parent experiment_parent; + experiment_parent.SetName("Experiment"); + + for (int i = 0; i < 8; i++) { + Entity experiment_rec; + experiment_rec.SetRole(Role::RECORD); + experiment_rec.AppendParent(experiment_parent); + volt_for_rt.SetValue(1.6); + experiment_rec.AppendProperty(volt_for_rt); + notes_for_rt2.SetValue("This is important!"); + experiment_rec.AppendProperty(notes_for_rt2); + part_for_rt3.SetValue(static_cast<int64_t>(6)); + experiment_rec.AppendProperty(part_for_rt3); + succ_for_rt.SetValue(true); + experiment_rec.AppendProperty(succ_for_rt); + auto rec_insert_transaction(connection->CreateTransaction()); + rec_insert_transaction->InsertEntity(&experiment_rec); + std::cout << "before ..." << std::endl; + insert_status = rec_insert_transaction->Execute(); + std::cout << "after ..." << std::endl; + } + + retr_transaction = connection->CreateTransaction(); + retr_transaction->Query("FIND Record Experiment"); + retr_transaction->Execute(); + EXPECT_EQ(retr_transaction->GetResultSet().size(), 8); + + auto to_be_updated = retr_transaction->GetResultSet().at(0); + // TODO(henrik) using the index for deletion is very inconvenient + to_be_updated.RemoveProperty(0); + to_be_updated.SetName("changedone"); + + auto update_transaction(connection->CreateTransaction()); + update_transaction->UpdateEntity(&to_be_updated); + update_transaction->Execute(); + + retr_transaction = connection->CreateTransaction(); + retr_transaction->Query("FIND Record Experiment with name=changedone"); + retr_transaction->Execute(); + EXPECT_EQ(retr_transaction->GetResultSet().size(), 1); } } // namespace caosdb::transaction