Newer
Older
/*
* This file is a part of the CaosDB Project.
*
* 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
* 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, Property
#include "caosdb/file_descriptor.h" // for FileDescriptor
#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 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 <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult
#include <gtest/gtest_pred_impl.h> // for AssertionResult
#include <initializer_list> // for initializer_list
#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!=
using caosdb::entity::Property;
// public utility functions
// ////////////////////////////////////////////////////////
/**
* Generate a vector with useful values for testing.
*/
static auto generateDatetimeValues() -> std::vector<std::string> {
std::vector<std::string> values = {
// TODO(tf) (these work, but would need conversion here in the tests due
// to the time zone information)
//"2022-12-24T18:15:00.999999+0200",
//"2022-12-24T18:15:00.999999UTC",
//"2022-12-24T18:15:00",
//"2022-12-24T18:15:00.999999",
//"",
/**
* 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?
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."
auto delete_transaction(connection->CreateTransaction());
for (const Entity &entity : query_transaction->GetResultSet()) {
delete_transaction->DeleteById(entity.GetId());
}
delete_transaction->Execute();
}
}
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 //////////////////////////////////////////////////////////
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 < test_file_size_kib; i++) {
fs::remove(test_upload_file_1);
fs::remove(test_download_file_1);
template <> auto test_transaction::getValueAs<double>(const Value &value) -> double {
template <> auto test_transaction::getValueAs<int64_t>(const Value &value) -> int64_t {
template <> auto test_transaction::getValueAs<int32_t>(const Value &value) -> int32_t {
return static_cast<int32_t>(value.GetAsInt64());
template <> auto test_transaction::getValueAs<bool>(const Value &value) -> bool {
/*
* 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();
auto transaction(connection->CreateTransaction());
const auto *id = "non-existing-id";
transaction->RetrieveById(id);
EXPECT_EQ(status.GetCode(), TransactionStatus::TRANSACTION_ERROR().GetCode());
ASSERT_EQ(status.GetCode(), StatusCode::GENERIC_TRANSACTION_ERROR);
EXPECT_EQ(id, entity.GetId());
EXPECT_TRUE(entity.HasErrors());
EXPECT_EQ(entity.GetErrors().at(0).GetCode(), 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();
auto insert_transaction(connection->CreateTransaction());
Entity entity;
entity.SetName("RT1");
insert_transaction->InsertEntity(&entity);
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();
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().at(0).GetCode(), MessageCode::ENTITY_HAS_NO_PROPERTIES);
/*
* Test deletion of an entity
* Insert an entity first.
*/
// same as in insert_without_delete
// until marked END SAME
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
auto insert_transaction(connection->CreateTransaction());
insert_transaction->InsertEntity(&entity);
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 delete_transaction(connection->CreateTransaction());
delete_transaction->DeleteById(new_entity.GetId());
delete_transaction->ExecuteAsynchronously();
auto delete_status = delete_transaction->WaitForIt();
ASSERT_TRUE(delete_status.IsTerminated());
ASSERT_FALSE(delete_status.IsError());
const auto &delete_result_set = delete_transaction->GetResultSet();
EXPECT_EQ(deleted_entity.GetId(), new_entity.GetId());
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();
auto insert_transaction(connection->CreateTransaction());
rt.SetName("TestRT");
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();
rec.SetName("TestRec");
Parent parent;
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());
ASSERT_FALSE(rec_insert_status.IsError());
const auto &rec_result_set = rec_transaction->GetResultSet();
EXPECT_FALSE(inserted_rec.GetId().empty());
auto retrieve_transaction(connection->CreateTransaction());
retrieve_transaction->RetrieveById(inserted_rec.GetId());
retrieve_transaction->ExecuteAsynchronously();
auto rec_retrieve_status = retrieve_transaction->WaitForIt();
ASSERT_TRUE(rec_retrieve_status.IsTerminated());
ASSERT_FALSE(rec_retrieve_status.IsError());
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());
auto rec_deletion(connection->CreateTransaction());
rec_deletion->DeleteById(retrieved_rec.GetId());
rec_deletion->ExecuteAsynchronously();
auto rec_delete_status = rec_deletion->WaitForIt();
ASSERT_TRUE(rec_delete_status.IsTerminated());
ASSERT_FALSE(rec_delete_status.IsError());
auto rt_deletion(connection->CreateTransaction());
rt_deletion->DeleteById(inserted_rt.GetId());
rt_deletion->ExecuteAsynchronously();
auto rt_delete_status = rt_deletion->WaitForIt();
ASSERT_TRUE(rt_delete_status.IsTerminated());
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();
// Create and insert Text property
prop_ent.SetName("TestProperty");
auto prop_insertion(connection->CreateTransaction());
prop_insertion->InsertEntity(&prop_ent);
prop_insertion->ExecuteAsynchronously();
auto prop_insert_status = prop_insertion->WaitForIt();
ASSERT_TRUE(prop_insert_status.IsTerminated());
ASSERT_FALSE(prop_insert_status.IsError());
const auto &prop_result_set = prop_insertion->GetResultSet();
EXPECT_FALSE(inserted_prop.GetId().empty());
// 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());
Property prop_double;
prop_double.SetName(prop_double_ent.GetName());
prop_double.SetId(inserted_prop_double.GetId());
prop_double.SetImportance(Importance::SUGGESTED);
rt.AppendProperty(prop_double);
auto rt_insertion(connection->CreateTransaction());
rt_insertion->InsertEntity(&rt);
rt_insertion->ExecuteAsynchronously();
auto rt_insert_status = rt_insertion->WaitForIt();
ASSERT_TRUE(rt_insert_status.IsTerminated());
ASSERT_FALSE(rt_insert_status.IsError());
EXPECT_FALSE(inserted_rt.GetId().empty());
// retrieve inserted rt for testing
auto rt_retrieval(connection->CreateTransaction());
rt_retrieval->RetrieveById(inserted_rt.GetId());
rt_retrieval->ExecuteAsynchronously();
auto rt_retrieve_status = rt_retrieval->WaitForIt();
ASSERT_TRUE(rt_retrieve_status.IsTerminated());
ASSERT_FALSE(rt_retrieve_status.IsError());
const auto &rt_retrieve_results = rt_retrieval->GetResultSet();
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(), 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.
Parent parent;
parent.SetName(rt.GetName());
parent.SetId(inserted_rt.GetId());
Property prop_rec;
prop_rec.SetName(prop_ent.GetName());
prop_rec.SetId(inserted_prop.GetId());
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.AppendParent(parent);
rec.AppendProperty(prop_rec);
rec.AppendProperty(prop_double_rec);
auto rec_insertion(connection->CreateTransaction());
rec_insertion->InsertEntity(&rec);
rec_insertion->ExecuteAsynchronously();
auto rec_insert_status = rec_insertion->WaitForIt();
ASSERT_TRUE(rec_insert_status.IsTerminated());
ASSERT_FALSE(rec_insert_status.IsError());
EXPECT_FALSE(inserted_rec.GetId().empty());
// Retrieve the record and verify parent and property
auto rec_retrieval(connection->CreateTransaction());
rec_retrieval->RetrieveById(inserted_rec.GetId());
rec_retrieval->ExecuteAsynchronously();
auto rec_retrieve_status = rec_retrieval->WaitForIt();
ASSERT_TRUE(rec_retrieve_status.IsTerminated());
ASSERT_FALSE(rec_retrieve_status.IsError());
const auto &rec_retrieve_results = rec_retrieval->GetResultSet();
EXPECT_EQ(rec.GetName(), retrieved_rec.GetName());
EXPECT_EQ(inserted_rec.GetId(), retrieved_rec.GetId());
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_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
*/
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
auto transaction(connection->CreateTransaction());
const std::vector<std::string> ids = {"20", "21", "22"};
transaction->RetrieveById(ids.begin(), ids.end());
transaction->ExecuteAsynchronously();
auto status = transaction->WaitForIt();
ASSERT_TRUE(status.IsTerminated());
// Should have an error since entity 22 doesn't exist
const auto &result_set = transaction->GetResultSet();
EXPECT_EQ(result_set.size(), 3);
EXPECT_EQ(result_set.at(1).GetId(), "21");
EXPECT_EQ(result_set.at(1).GetName(), "unit");
EXPECT_EQ(result_set.at(2).GetId(), "22");
EXPECT_TRUE(result_set.at(2).HasErrors());
EXPECT_EQ(result_set.at(2).GetErrors().at(0).GetCode(), 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();
// INSERT
auto insert_transaction(connection->CreateTransaction());
Entity entity;
entity.SetName("RT1");
insert_transaction->InsertEntity(&entity);
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();
EXPECT_FALSE(new_entity.GetId().empty());
EXPECT_FALSE(new_entity.HasErrors());
// RETRIEVE
auto retrieve_transaction(connection->CreateTransaction());
retrieve_transaction->RetrieveById(new_entity.GetId());
retrieve_transaction->Execute();
// UPDATE
auto update_transaction(connection->CreateTransaction());
auto update_entity(retrieve_transaction->GetResultSet().at(0));
auto update_status = update_transaction->WaitForIt();
ASSERT_TRUE(update_status.IsTerminated());
ASSERT_FALSE(update_status.IsError());
EXPECT_EQ(update_transaction->GetResultSet().size(), 1);
const auto &updated_entity = update_transaction->GetResultSet().at(0);
EXPECT_EQ(updated_entity.GetId(), new_entity.GetId());
EXPECT_FALSE(updated_entity.HasErrors());
// DELETE
auto delete_transaction(connection->CreateTransaction());
delete_transaction->DeleteById(new_entity.GetId());
delete_transaction->ExecuteAsynchronously();
auto delete_status = delete_transaction->WaitForIt();
ASSERT_TRUE(delete_status.IsTerminated());
ASSERT_FALSE(delete_status.IsError());
const auto &delete_result_set = delete_transaction->GetResultSet();
EXPECT_EQ(deleted_entity.GetId(), new_entity.GetId());
EXPECT_FALSE(deleted_entity.HasErrors());
}
/*
* test insert a RecordType and then submit a find and a count query
*/
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
auto insert_transaction(connection->CreateTransaction());
Entity entity;
entity.SetName("RT1");
insert_transaction->InsertEntity(&entity);
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();
EXPECT_FALSE(new_entity.GetId().empty());
EXPECT_FALSE(new_entity.HasErrors());
auto query_transaction(connection->CreateTransaction());
query_transaction->Query("FIND ENTITY WITH id = " + new_entity.GetId());
query_transaction->Execute();
EXPECT_EQ(query_transaction->GetResultSet().at(0).GetId(), new_entity.GetId());
// No count query, so no count result should be present
EXPECT_TRUE((query_transaction->GetCountResult() < 0));
auto count_query_trans(connection->CreateTransaction());
std::cout << "Creating count query ..." << std::endl;
count_query_trans->Query("COUNT ENTITY WITH id = " + new_entity.GetId());
std::cout << "Executing count query ..." << std::endl;
count_query_trans->Execute();
// No result set in a count 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);
std::cout << "Creating: " << name << std::endl;
prop.SetName(name);
std::cout << "Setting value " << value << std::endl;
auto i_stat = insert_transaction->InsertEntity(&prop);
EXPECT_EQ(i_stat, StatusCode::GO_ON);
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];
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_numeric_values_impl<int32_t, int64_t>(AtomicDataType::INTEGER);
test_transaction::DeleteEntities();
test_numeric_values_impl<bool, bool>(AtomicDataType::BOOLEAN);
}
/**
* Test date time values.
*/
TEST_F(test_transaction, test_datetime_values) {
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
// Insert entities
auto values_orig = test_transaction::generateDatetimeValues();
auto props_orig = std::vector<Entity>();
size_t i = 0;
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(AtomicDataType::DATETIME);
std::cout << "Setting value " << value << std::endl;
prop.SetValue(value);
props_orig.push_back(prop);
auto i_stat = insert_transaction->InsertEntity(&prop);
EXPECT_EQ(i_stat, StatusCode::GO_ON);
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;
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(), AtomicDataType::DATETIME);
const auto &retrieved_value = result.GetValue().GetAsString();
// std::cout << "retrieved_value: " << retrieved_value << std::endl;
EXPECT_EQ(retrieved_value, value);
}
++i;
}
}
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::GO_ON);
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);
/*
* Insert three record types in a single transaction.
*/
TEST_F(test_transaction, test_multi_insert_transactions) {
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
auto insert_transaction(connection->CreateTransaction());
for (std::string name : {"RT1", "RT2", "RT3"}) {
Entity entity;
entity.SetRole(Role::RECORD_TYPE);
entity.SetName(name);
auto stat = insert_transaction->InsertEntity(&entity);
EXPECT_EQ(stat, StatusCode::GO_ON);
}
insert_transaction->Execute();
auto insert_status = insert_transaction->GetStatus();
ASSERT_TRUE(insert_status.IsTerminated());
ASSERT_FALSE(insert_status.IsError());
// test end results
auto retrieve_transaction(connection->CreateTransaction());
for (const Entity &entity : insert_transaction->GetResultSet()) {
retrieve_transaction->RetrieveById(entity.GetId());
}
retrieve_transaction->Execute();
auto retrieve_status = retrieve_transaction->GetStatus();
ASSERT_TRUE(retrieve_status.IsTerminated());
ASSERT_FALSE(retrieve_status.IsError());
}
/*
* Update three record types in a single transaction.
*/
TEST_F(test_transaction, test_multi_update_transactions) {
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
auto insert_transaction(connection->CreateTransaction());
for (std::string name : {"RT1", "RT2", "RT3"}) {
Entity entity;
entity.SetRole(Role::RECORD_TYPE);
entity.SetName(name);
insert_transaction->InsertEntity(&entity);
}
insert_transaction->Execute();
auto retrieve_transaction(connection->CreateTransaction());
for (const Entity &entity : insert_transaction->GetResultSet()) {
retrieve_transaction->RetrieveById(entity.GetId());
}
retrieve_transaction->Execute();
auto update_transaction(connection->CreateTransaction());
for (Entity entity : retrieve_transaction->GetResultSet()) {
entity.SetDescription("NewDescription");
ASSERT_EQ(update_transaction->UpdateEntity(&entity), StatusCode::GO_ON);
}
update_transaction->Execute();
auto update_status = update_transaction->GetStatus();
ASSERT_TRUE(update_status.IsTerminated());
ASSERT_FALSE(update_status.IsError());
// test end results
auto final_retrieve_transaction(connection->CreateTransaction());
for (const Entity &entity : update_transaction->GetResultSet()) {
final_retrieve_transaction->RetrieveById(entity.GetId());
}
final_retrieve_transaction->Execute();
auto final_retrieve_status = final_retrieve_transaction->GetStatus();
ASSERT_TRUE(final_retrieve_status.IsTerminated());
ASSERT_FALSE(final_retrieve_status.IsError());
for (const Entity &entity : final_retrieve_transaction->GetResultSet()) {
ASSERT_EQ(entity.GetDescription(), "NewDescription");
}
}
/*
*
* Setup: Insert A, B, while A references B
*
* Complex transaction: Insert C, update A (remove reference to B, add reference
* to C), delete B.
*/
TEST_F(test_transaction, test_multi_write_transactions) {
const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
Entity entity_a;
Entity entity_b;
Entity entity_c;
Property reference_b;
Property reference_c;
// SETUP
entity_a.SetRole(Role::RECORD_TYPE);
entity_a.SetName("RTA");
entity_b.SetRole(Role::RECORD_TYPE);
entity_b.SetName("RTB");
reference_b.SetName("RTB");
entity_a.AppendProperty(reference_b);
auto insert_transaction(connection->CreateTransaction());
insert_transaction->InsertEntity(&entity_a);
insert_transaction->InsertEntity(&entity_b);
insert_transaction->ExecuteAsynchronously();
insert_transaction->WaitForIt();
ASSERT_TRUE(insert_transaction->GetStatus().IsTerminated());
ASSERT_FALSE(insert_transaction->GetStatus().IsError());
auto entity_a_id = insert_transaction->GetResultSet().at(0).GetId(); // entity_a
auto entity_b_id = insert_transaction->GetResultSet().at(1).GetId(); // entity_b
auto retrieve_transaction(connection->CreateTransaction());
retrieve_transaction->RetrieveById(entity_a_id);
retrieve_transaction->Execute();
// COMPLEX TRANSACTION
entity_c.SetRole(Role::RECORD_TYPE);
entity_c.SetName("RTC");
reference_c.SetName("RTC");
Entity entity_a_update(retrieve_transaction->GetResultSet().at(0));
entity_a_update.RemoveProperty(0);
entity_a_update.AppendProperty(reference_c);
auto complex_transaction(connection->CreateTransaction());
ASSERT_EQ(complex_transaction->InsertEntity(&entity_c), StatusCode::GO_ON);
ASSERT_EQ(complex_transaction->DeleteById(entity_b_id), StatusCode::GO_ON);
ASSERT_EQ(complex_transaction->UpdateEntity(&entity_a_update), StatusCode::GO_ON);
complex_transaction->ExecuteAsynchronously();
complex_transaction->WaitForIt();
EXPECT_TRUE(complex_transaction->GetStatus().IsTerminated());
EXPECT_FALSE(complex_transaction->GetStatus().IsError());
retrieve_transaction = connection->CreateTransaction();
ASSERT_EQ(retrieve_transaction->RetrieveById(entity_a_update.GetId()), StatusCode::GO_ON);
retrieve_transaction->ExecuteAsynchronously();
retrieve_transaction->WaitForIt();