/* * 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/configuration.h" // for InsecureConnectionConfig... #include "caosdb/connection.h" // for Connection #include "caosdb/entity.h" // for Entity #include "caosdb/entity/v1alpha1/main.pb.h" // for Entity #include "caosdb/exceptions.h" // for ConnectionError #include "caosdb/status_code.h" #include "caosdb/transaction.h" // for Transaction #include "caosdb/transaction_handler.h" // for MultiTransactionResponse #include "caosdb/transaction_status.h" // for ConnectionError #include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE #include <gtest/gtest-message.h> // for Message #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestPa... #include <gtest/gtest_pred_impl.h> // for Test, TestInfo, TEST #include <memory> // for allocator, unique_ptr #include <stdexcept> // for out_of_range #include <string> // for string, basic_string #include <utility> // for move #include <vector> // for vector namespace caosdb::transaction { using caosdb::configuration::InsecureConnectionConfiguration; using caosdb::connection::Connection; using caosdb::entity::Entity; using caosdb::exceptions::ConnectionError; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using caosdb::entity::Role; using caosdb::entity::v1alpha1::RetrieveResponse; TEST(test_transaction, create_transaction) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); ASSERT_EQ(StatusCode::GO_ON, transaction->RetrieveById("100")); EXPECT_THROW_MESSAGE(transaction->Execute(), ConnectionError, "The attempt to execute this transaction was not successful because the " "connection to the server could not be established. " "Original message: failed to connect to all addresses"); } TEST(test_transaction, test_multi_result_set) { std::vector<std::unique_ptr<Entity>> entities; for (int i = 0; i < 5; i++) { entities.push_back(std::make_unique<Entity>()); entities[i]->SetName("E" + std::to_string(i)); } MultiResultSet result_set(std::move(entities)); EXPECT_EQ(result_set.size(), 5); EXPECT_EQ(result_set.mutable_at(3)->GetName(), "E3"); EXPECT_EQ(result_set.at(4).GetName(), "E4"); EXPECT_EQ(result_set.at(4).GetName(), "E4"); EXPECT_THROW(auto &e = result_set.at(15), std::out_of_range); int counter = 0; for (const auto &entity : result_set) { EXPECT_EQ(entity.GetName(), "E" + std::to_string(counter++)); } EXPECT_EQ(counter, 5); } TEST(test_transaction, test_unavailable) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); transaction->RetrieveById("100"); transaction->ExecuteAsynchronously(); EXPECT_EQ(transaction->GetRequestCount(), 1); auto status = transaction->WaitForIt(); EXPECT_EQ(status.GetCode(), StatusCode::CONNECTION_ERROR); } TEST(test_transaction, test_retrieve_by_ids) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); std::vector<std::string> ids = {"100", "101", "102"}; transaction->RetrieveById(ids.begin(), ids.end()); EXPECT_EQ(transaction->GetRequestCount(), 3); } TEST(test_transaction, test_multi_result_set_empty) { std::vector<std::unique_ptr<Entity>> empty; MultiResultSet rs(std::move(empty)); EXPECT_EQ(rs.size(), 0); } TEST(test_transaction, test_multi_result_iterator) { std::vector<std::unique_ptr<Entity>> one_elem; RetrieveResponse response; response.mutable_entity_response()->mutable_entity()->set_id("100"); one_elem.push_back(std::make_unique<Entity>(response.release_entity_response())); MultiResultSet rs(std::move(one_elem)); EXPECT_EQ(rs.size(), 1); for (const Entity &entity : rs) { EXPECT_EQ(entity.GetId(), "100"); } } TEST(test_transaction, test_multi_result_set_one) { std::vector<std::unique_ptr<Entity>> one_elem; RetrieveResponse response; response.mutable_entity_response()->mutable_entity()->set_id("100"); one_elem.push_back(std::make_unique<Entity>(response.release_entity_response())); MultiResultSet rs(std::move(one_elem)); EXPECT_EQ(rs.size(), 1); EXPECT_EQ(rs.at(0).GetId(), "100"); } TEST(test_transaction, test_multi_result_set_three) { std::vector<std::unique_ptr<Entity>> three_elem; MultiTransactionResponse response; response.add_responses() ->mutable_retrieve_response() ->mutable_entity_response() ->mutable_entity() ->set_id("100"); auto *entity_with_error = response.add_responses()->mutable_retrieve_response()->mutable_entity_response(); entity_with_error->mutable_entity()->set_id("101"); entity_with_error->add_errors()->set_code(1); response.add_responses() ->mutable_retrieve_response() ->mutable_entity_response() ->mutable_entity() ->set_id("102"); auto *responses = response.mutable_responses(); std::vector<std::unique_ptr<Entity>> entities; for (auto sub_response : *responses) { three_elem.push_back(std::make_unique<Entity>( sub_response.mutable_retrieve_response()->release_entity_response())); } MultiResultSet rs(std::move(three_elem)); EXPECT_EQ(rs.size(), 3); EXPECT_TRUE(rs.at(1).HasErrors()); } TEST(test_transaction, test_update_entity) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); caosdb::entity::Entity update_entity; update_entity.SetName("New Name"); auto error = transaction->UpdateEntity(&update_entity); EXPECT_EQ(error, StatusCode::ORIGINAL_ENTITY_MISSING_ID); } TEST(test_transaction, test_multi_deletion) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::INITIAL); for (int i = 0; i < 3; i++) { auto status = transaction->DeleteById("asdf"); EXPECT_EQ(status, StatusCode::GO_ON); } } TEST(test_transaction, test_retrieve_and_download) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::INITIAL); transaction->RetrieveAndDownloadFilesById("asdf", "local_path"); EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::GO_ON); EXPECT_EQ(transaction->ExecuteAsynchronously(), StatusCode::EXECUTING); EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::CONNECTION_ERROR); } TEST(test_transaction, test_insert_with_file) { const auto *host = "localhost"; auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); Entity entity; entity.SetRole(Role::FILE); entity.SetLocalPath(TEST_DATA_DIR + "/test.json"); EXPECT_TRUE(transaction->GetUploadFiles().empty()); transaction->InsertEntity(&entity); EXPECT_EQ(transaction->GetUploadFiles().size(), 1); transaction->ExecuteAsynchronously(); EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::FILE_UPLOAD_ERROR); } } // namespace caosdb::transaction