diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 18cb4da07c0d780d0a335bfde0cc177876329efb..41e6bf3e29e115914f6732e87cb44818ed9c28ae 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ set(test_cases test_properties test_transaction test_ccaosdb + test_issues ) diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp index 64ef436100824845e52b2a39511e6ed1a86a365c..ff01b9ba95d8dcded153aa9f94260a98cf5b3c53 100644 --- a/test/test_ccaosdb.cpp +++ b/test/test_ccaosdb.cpp @@ -46,7 +46,7 @@ protected: // 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 < 8; i++) { + for (int i = 0; i < 20; i++) { writer.write(buffer); } } diff --git a/test/test_issues.cpp b/test/test_issues.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b934237e0571805ac1b95fe474ca64129258fcc --- /dev/null +++ b/test/test_issues.cpp @@ -0,0 +1,171 @@ +/* + * 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}); + + EXPECT_NONFATAL_FAILURE( + { + update_transaction->UpdateEntity(&update_entity); + update_transaction->ExecuteAsynchronously(); + + auto update_status = update_transaction->WaitForIt(); + EXPECT_TRUE(update_status.IsTerminated()); + EXPECT_FALSE(update_status.IsError()); + }, + "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; +} + +} // namespace caosdb::transaction diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index 7625c80f33f96e64351fb24bd360f1a74b0ab3f4..4f9d3871404ac7db6dbf476e54f1f6bae2b33f05 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 @@ -102,6 +103,7 @@ public: 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 ////////////////////////////////////////////////////////// @@ -114,7 +116,7 @@ protected: // 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); } } @@ -1005,14 +1007,20 @@ 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; } /*