Skip to content
Snippets Groups Projects
Select Git revision
  • d287c5693194b8dc8028efc80c7c49359fbe9253
  • main default protected
  • f-sss4grpc
  • dev
  • 108-implement-rpc-call-for-server-side-scripting
  • f-windows-conan-create
  • f-to-string
  • f-update-requirements
  • f-related-projects
  • f-role
  • f-remote-path
  • f-rel-path
  • f-consol-message
  • v0.3.0
  • v0.2.2
  • v0.2.1
  • v0.2.0
  • v0.1.2
  • v0.1.1
  • v0.1
  • v0.0.19
  • v0.0.18
  • v0.0.16
  • v0.0.15
  • v0.0.10
  • v0.0.9
  • v0.0.8
  • v0.0.7
  • v0.0.6
  • v0.0.5
  • v0.0.4
  • v0.0.3
  • v0.0.2
33 results

test_connection.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    test_clinkahead.cpp 38.27 KiB
    /*
     *
     * This file is a part of the LinkAhead Project.
     *
     * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
     * Copyright (C) 2021-2024 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 "linkahead/configuration.h"
    #include "linkahead/status_code.h"  // for StatusCode
    #include "linkahead_test_utility.h" // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
    #include "clinkahead.h"             // for linkahead_utility_get_env_fallback
    #include <cstdint>                  // for int64_t
    #include <cstring>                  // for strcmp
    #include <gtest/gtest.h>
    #include <gtest/gtest-message.h>   // for Message
    #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
    #include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
    #include <string>                  // for allocator, operator+, string,...
    
    class test_clinkahead : public ::testing::Test {
    protected:
      void SetUp() override {
        linkahead::configuration::ConfigurationManager::Clear();
        linkahead::configuration::ConfigurationManager::LoadSingleJSONConfiguration(
          TEST_DATA_DIR + "/test_linkahead_client.json");
      }
    
      void TearDown() override { linkahead::configuration::ConfigurationManager::Clear(); }
    };
    
    TEST_F(test_clinkahead, test_get_env_fallback) {
      const char *const some_var = linkahead_utility_get_env_fallback("SOME_ENV_VAR", "fall-back");
      EXPECT_EQ("fall-back", some_var);
    }
    
    TEST_F(test_clinkahead, test_other_client_error) {
      EXPECT_EQ(linkahead_status_code_OTHER_CLIENT_ERROR(), linkahead::StatusCode::OTHER_CLIENT_ERROR);
    }
    
    TEST_F(test_clinkahead, test_get_default_connection) {
      linkahead_connection_connection out;
    
      linkahead_connection_connection_manager_get_default_connection(&out);
      EXPECT_TRUE(out.wrapped_connection);
    }
    
    TEST_F(test_clinkahead, test_get_connection) {
      linkahead_connection_connection out;
    
      linkahead_connection_connection_manager_get_connection(&out, "local-linkahead-admin");
      EXPECT_TRUE(out.wrapped_connection);
    }
    
    TEST_F(test_clinkahead, test_execute_transaction) {
      linkahead_connection_connection connection;
      linkahead_connection_connection_manager_get_connection(&connection, "local-linkahead-admin");
    
      linkahead_transaction_transaction transaction;
      linkahead_connection_connection_create_transaction(&connection, &transaction);
    
      EXPECT_TRUE(transaction.wrapped_transaction);
    
      int return_code(linkahead_transaction_transaction_retrieve_by_id(&transaction, "some_id"));
      EXPECT_EQ(return_code, linkahead::StatusCode::GO_ON);
    
      return_code = linkahead_transaction_transaction_execute(&transaction);
      EXPECT_EQ(return_code, linkahead::StatusCode::CONNECTION_ERROR);
    
      return_code = linkahead_transaction_delete_transaction(&transaction);
      EXPECT_EQ(return_code, 0);
    
      linkahead_transaction_transaction multi_transaction;
      linkahead_connection_connection_create_transaction(&connection, &multi_transaction);
    
      // We explicitely want to define a C-style array here, so we disable
      // linting
      const char *ids[] = {"id1", "id2", "id3"}; // NOLINT
      return_code = linkahead_transaction_transaction_retrieve_by_ids(&multi_transaction, ids, 3);
      EXPECT_EQ(return_code, linkahead::StatusCode::GO_ON);
    
      return_code = linkahead_transaction_delete_transaction(&multi_transaction);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_multi_retrieve) {
      linkahead_connection_connection connection;
      linkahead_connection_connection_manager_get_connection(&connection, "local-linkahead-admin");
    
      linkahead_transaction_transaction multi_transaction;
      linkahead_connection_connection_create_transaction(&connection, &multi_transaction);
    
      // We explicitely want to define a C-style array here, so we disable
      // linting
      const char *ids[] = {"id1", "id2", "id3"}; // NOLINT
      int return_code(linkahead_transaction_transaction_retrieve_by_ids(&multi_transaction, ids, 3));
      EXPECT_EQ(return_code, linkahead::StatusCode::GO_ON);
    
      return_code = linkahead_transaction_delete_transaction(&multi_transaction);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_query) {
      linkahead_connection_connection connection;
      linkahead_connection_connection_manager_get_connection(&connection, "local-linkahead-admin");
    
      linkahead_transaction_transaction transaction;
      linkahead_connection_connection_create_transaction(&connection, &transaction);
    
      int return_code(linkahead_transaction_transaction_query(&transaction, "FIND ENTITY WITH id=123"));
      EXPECT_EQ(return_code, linkahead::StatusCode::GO_ON);
    
      return_code = linkahead_transaction_delete_transaction(&transaction);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_datatype) {
    
      linkahead_entity_datatype atomic;
      // check that this fails
      int return_code(linkahead_entity_create_atomic_datatype(&atomic, "some type"));
      EXPECT_EQ(return_code, linkahead::StatusCode::ENUM_MAPPING_ERROR);
    
      return_code = linkahead_entity_create_atomic_datatype(&atomic, "INTEGER");
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype reference;
      return_code = linkahead_entity_create_reference_datatype(&reference, "MyType");
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype list_of_atomics;
      return_code = linkahead_entity_create_atomic_list_datatype(&list_of_atomics, "DATETIME");
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype list_of_references;
      return_code = linkahead_entity_create_reference_list_datatype(&list_of_references, "MyType");
      EXPECT_EQ(return_code, 0);
    
      bool is_a(false);
      return_code = linkahead_entity_datatype_is_undefined(&atomic, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_atomic(&atomic, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
      return_code = linkahead_entity_datatype_is_reference(&atomic, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_atomic(&atomic, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_reference(&atomic, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
    
      return_code = linkahead_entity_datatype_is_atomic(&reference, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_reference(&reference, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_atomic(&reference, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_reference(&reference, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
    
      return_code = linkahead_entity_datatype_is_atomic(&list_of_atomics, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_reference(&list_of_atomics, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_atomic(&list_of_atomics, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_reference(&list_of_atomics, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
    
      return_code = linkahead_entity_datatype_is_atomic(&list_of_references, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_reference(&list_of_references, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_atomic(&list_of_references, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_reference(&list_of_references, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
    
      char *name = nullptr; // NOLINT
      return_code = linkahead_entity_datatype_get_datatype_name(&atomic, &name);
      EXPECT_EQ(return_code, 0);
      EXPECT_STREQ(name, "INTEGER");
    
      return_code = linkahead_entity_datatype_get_datatype_name(&reference, &name);
      EXPECT_EQ(return_code, 0);
      EXPECT_STREQ(name, "MyType");
    
      return_code = linkahead_entity_datatype_get_datatype_name(&list_of_atomics, &name);
      EXPECT_EQ(return_code, 0);
      EXPECT_STREQ(name, "DATETIME");
    
      return_code = linkahead_entity_datatype_get_datatype_name(&list_of_references, &name);
      EXPECT_EQ(return_code, 0);
      EXPECT_STREQ(name, "MyType");
    
      return_code = linkahead_entity_delete_datatype(&atomic);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&reference);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&list_of_atomics);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&list_of_references);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_value) {
    
      linkahead_entity_value string_value;
      int return_code(linkahead_entity_create_string_value(&string_value, "value"));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value int_value;
      return_code = linkahead_entity_create_int_value(&int_value, 27);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value bool_value;
      return_code = linkahead_entity_create_bool_value(&bool_value, true);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value double_value;
      return_code = linkahead_entity_create_double_value(&double_value, 2.7);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value string_vector_value;
      const char *string_values[] = {"a", "b", "c"}; // NOLINT
      return_code = linkahead_entity_create_string_vector_value(&string_vector_value, string_values, 3);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value int_vector_value;
      const int64_t int_values[] = {1, 2, 3}; // NOLINT
      return_code = linkahead_entity_create_int_vector_value(&int_vector_value, int_values, 3);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value double_vector_value;
      const double double_values[] = {1.1, 2.2, 3.3}; // NOLINT
      return_code = linkahead_entity_create_double_vector_value(&double_vector_value, double_values, 3);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value bool_vector_value;
      const bool bool_values[] = {true, false, false}; // NOLINT
      return_code = linkahead_entity_create_bool_vector_value(&bool_vector_value, bool_values, 3);
      EXPECT_EQ(return_code, 0);
    
      // One thorough check, afterwards only the ones that should be true
      bool is_a(false);
      return_code = linkahead_entity_value_is_null(&string_value, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_value_is_string(&string_value, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
      return_code = linkahead_entity_value_is_double(&string_value, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_value_is_integer(&string_value, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_value_is_bool(&string_value, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_value_is_vector(&string_value, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
    
      linkahead_entity_value_is_integer(&int_value, &is_a);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_value_is_bool(&bool_value, &is_a);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_value_is_double(&double_value, &is_a);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_value_is_vector(&string_vector_value, &is_a);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_value_is_vector(&int_vector_value, &is_a);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_value_is_vector(&double_vector_value, &is_a);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_value_is_vector(&bool_vector_value, &is_a);
      EXPECT_TRUE(is_a);
    
      char *out_string = nullptr; // NOLINT
      return_code = linkahead_entity_value_get_as_string(&string_value, &out_string);
      EXPECT_EQ(return_code, 0);
      EXPECT_STREQ(out_string, "value");
    
      int64_t out_int(0);
      return_code = linkahead_entity_value_get_as_integer(&int_value, &out_int);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(out_int, 27);
    
      bool out_bool(false);
      return_code = linkahead_entity_value_get_as_bool(&bool_value, &out_bool);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(out_bool);
    
      double out_double(0);
      return_code = linkahead_entity_value_get_as_double(&double_value, &out_double);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(out_double, 2.7);
    
      int list_length(0);
      return_code = linkahead_entity_value_get_as_vector_size(&string_vector_value, &list_length);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(list_length, 3);
      return_code = linkahead_entity_value_get_as_vector_size(&int_vector_value, &list_length);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(list_length, 3);
      return_code = linkahead_entity_value_get_as_vector_size(&double_vector_value, &list_length);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(list_length, 3);
      return_code = linkahead_entity_value_get_as_vector_size(&bool_vector_value, &list_length);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(list_length, 3);
    
      // Only check for one, rest should be covered by this + scalar values
      linkahead_entity_value out_val;
      return_code = linkahead_entity_value_get_as_vector_at(&string_vector_value, &out_val, 0);
      EXPECT_EQ(return_code, 0);
      linkahead_entity_value_get_as_string(&out_val, &out_string);
      EXPECT_STREQ(out_string, "a");
      return_code = linkahead_entity_value_get_as_vector_at(&string_vector_value, &out_val, 1);
      EXPECT_EQ(return_code, 0);
      linkahead_entity_value_get_as_string(&out_val, &out_string);
      EXPECT_STREQ(out_string, "b");
      return_code = linkahead_entity_value_get_as_vector_at(&string_vector_value, &out_val, 2);
      EXPECT_EQ(return_code, 0);
      linkahead_entity_value_get_as_string(&out_val, &out_string);
      EXPECT_STREQ(out_string, "c");
    
      return_code = linkahead_entity_delete_value(&string_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&int_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&bool_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&double_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&string_vector_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&int_vector_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&double_vector_value);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&bool_vector_value);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_entity) {
      linkahead_entity_entity entity;
    
      int return_code(linkahead_entity_create_entity(&entity));
      EXPECT_EQ(return_code, 0);
    
      // cannot be created again without deletion
      // return_code = linkahead_entity_create_entity(&entity);
      // EXPECT_EQ(return_code, linkahead::StatusCode::EXTERN_C_ASSIGNMENT_ERROR);
    
      // deletion and re-creation is ok
      return_code = linkahead_entity_delete_entity(&entity);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_create_entity(&entity);
      EXPECT_EQ(return_code, 0);
    
      // In-depth check for one pair of setter and getter, just compare
      // the strings for the rest
      return_code = linkahead_entity_entity_set_name(&entity, "length");
      EXPECT_EQ(return_code, 0);
      char *out = nullptr; // NOLINT
      return_code = linkahead_entity_entity_get_name(&entity, &out);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(strcmp(out, "length"), 0);
    
      // test call without validation of result
      return_code = linkahead_entity_entity_set_role(&entity, "FILE");
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_entity_set_local_path(
        &entity, (TEST_DATA_DIR + "/test_linkahead_client.json").c_str());
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_entity_get_local_path(&entity, &out);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(strcmp(out, (TEST_DATA_DIR + "/test_linkahead_client.json").c_str()), 0);
    
      // invalid role
      return_code = linkahead_entity_entity_set_role(&entity, "Role does not exist");
      EXPECT_EQ(return_code, linkahead::StatusCode::ENUM_MAPPING_ERROR);
    
      linkahead_entity_entity_set_role(&entity, "PROPERTY");
      linkahead_entity_entity_get_role(&entity, &out);
      EXPECT_EQ(strcmp(out, "PROPERTY"), 0);
    
      linkahead_entity_entity_set_description(&entity, "The length of an object");
      linkahead_entity_entity_get_description(&entity, &out);
      EXPECT_EQ(strcmp(out, "The length of an object"), 0);
    
      linkahead_entity_datatype in_type;
      linkahead_entity_create_atomic_datatype(&in_type, "DOUBLE");
      linkahead_entity_entity_set_datatype(&entity, &in_type);
    
      // verify that this doesn't work ...
      // return_code = linkahead_entity_entity_get_datatype(&entity, &in_type);
      // EXPECT_EQ(return_code, linkahead::StatusCode::EXTERN_C_ASSIGNMENT_ERROR);
      linkahead_entity_datatype out_type;
      // ... but does with a clean property
      return_code = linkahead_entity_entity_get_datatype(&entity, &out_type);
      EXPECT_EQ(return_code, 0);
      bool is_a(false);
      return_code = linkahead_entity_datatype_is_atomic(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
      return_code = linkahead_entity_datatype_is_reference(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_atomic(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_reference(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
    
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(out, "DOUBLE");
    
      linkahead_entity_value in_value;
      return_code = linkahead_entity_create_double_value(&in_value, 5.0);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_entity_set_value(&entity, &in_value);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value out_value;
      return_code = linkahead_entity_entity_get_value(&entity, &out_value);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value_is_double(&out_value, &is_a);
      EXPECT_TRUE(is_a);
      linkahead_entity_value_is_null(&out_value, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_value_is_string(&out_value, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_value_is_bool(&out_value, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_value_is_integer(&out_value, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_value_is_vector(&out_value, &is_a);
      EXPECT_FALSE(is_a);
    
      double out_double(0);
      linkahead_entity_value_get_as_double(&out_value, &out_double);
      EXPECT_EQ(out_double, 5.0);
    
      // clear to re-use
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      linkahead_entity_create_reference_list_datatype(&in_type, "Person");
      linkahead_entity_entity_set_datatype(&entity, &in_type);
    
      // works without clearing since datatype is managed by the owning entity
      linkahead_entity_entity_get_datatype(&entity, &out_type);
      return_code = linkahead_entity_datatype_is_atomic(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_reference(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_atomic(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_FALSE(is_a);
      return_code = linkahead_entity_datatype_is_list_of_reference(&out_type, &is_a);
      EXPECT_EQ(return_code, 0);
      EXPECT_TRUE(is_a);
    
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(out, "Person");
    
      linkahead_entity_entity_set_unit(&entity, "m");
      linkahead_entity_entity_get_unit(&entity, &out);
      EXPECT_EQ(strcmp(out, "m"), 0);
    
      return_code = linkahead_entity_delete_entity(&entity);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&in_value);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_parent) {
      linkahead_entity_parent parent;
    
      int return_code(linkahead_entity_create_parent(&parent));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_parent_set_id(&parent, "some_id");
      linkahead_entity_parent_set_name(&parent, "some_name");
    
      char *out = nullptr; // NOLINT
      linkahead_entity_parent_get_id(&parent, &out);
      EXPECT_EQ(strcmp(out, "some_id"), 0);
    
      linkahead_entity_parent_get_name(&parent, &out);
      EXPECT_EQ(strcmp(out, "some_name"), 0);
    
      return_code = linkahead_entity_delete_parent(&parent);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_property) {
      linkahead_entity_property property;
    
      int return_code(linkahead_entity_create_property(&property));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_property_set_id(&property, "some_id");
      linkahead_entity_property_set_name(&property, "some_name");
    
      linkahead_entity_datatype in_type;
      linkahead_entity_create_atomic_datatype(&in_type, "TEXT");
      linkahead_entity_property_set_datatype(&property, &in_type);
      linkahead_entity_property_set_importance(&property, "FIX");
      linkahead_entity_property_set_unit(&property, "some_unit");
      linkahead_entity_value in_value;
      linkahead_entity_create_string_value(&in_value, "some_value");
      linkahead_entity_property_set_value(&property, &in_value);
    
      char *out = nullptr; // NOLINT
      linkahead_entity_property_get_id(&property, &out);
      EXPECT_EQ(strcmp(out, "some_id"), 0);
    
      linkahead_entity_property_get_name(&property, &out);
      EXPECT_EQ(strcmp(out, "some_name"), 0);
    
      linkahead_entity_datatype out_type;
      return_code = linkahead_entity_property_get_datatype(&property, &out_type);
      EXPECT_EQ(return_code, 0);
      bool is_a(false);
    
      linkahead_entity_datatype_is_atomic(&out_type, &is_a);
      EXPECT_TRUE(is_a);
      linkahead_entity_datatype_is_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_list_of_atomic(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_list_of_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
    
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(out, "TEXT");
    
      linkahead_entity_property_get_importance(&property, &out);
      EXPECT_EQ(strcmp(out, "FIX"), 0);
    
      linkahead_entity_property_get_unit(&property, &out);
      EXPECT_EQ(strcmp(out, "some_unit"), 0);
    
      linkahead_entity_value out_value;
      return_code = linkahead_entity_property_get_value(&property, &out_value);
      EXPECT_EQ(return_code, 0);
      linkahead_entity_value_is_string(&out_value, &is_a);
      EXPECT_TRUE(is_a);
      linkahead_entity_value_get_as_string(&out_value, &out);
      EXPECT_STREQ(out, "some_value");
    
      return_code = linkahead_entity_delete_property(&property);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&in_value);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_string_list_property) {
    
      linkahead_entity_property property;
      int return_code(linkahead_entity_create_property(&property));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype in_type;
      return_code = linkahead_entity_create_atomic_list_datatype(&in_type, "TEXT");
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_property_set_datatype(&property, &in_type);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_value in_value;
      const char *value_list[] = {"val0", "val1", "val2"}; // NOLINT
      return_code = linkahead_entity_create_string_vector_value(&in_value, value_list, 3);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_property_set_value(&property, &in_value);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype out_type;
      return_code = linkahead_entity_property_get_datatype(&property, &out_type);
      EXPECT_EQ(return_code, 0);
    
      bool is_a(false);
      linkahead_entity_datatype_is_atomic(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_list_of_atomic(&out_type, &is_a);
      EXPECT_TRUE(is_a);
      linkahead_entity_datatype_is_list_of_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      char *out = nullptr; // NOLINT
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(out, "TEXT");
    
      linkahead_entity_value out_value;
      linkahead_entity_property_get_value(&property, &out_value);
      linkahead_entity_value_is_vector(&out_value, &is_a);
      EXPECT_TRUE(is_a);
      int length(-1);
      linkahead_entity_value_get_as_vector_size(&out_value, &length);
      EXPECT_EQ(length, 3);
    
      linkahead_entity_value list_elt;
      for (int i = 0; i < length; i++) {
        return_code = linkahead_entity_value_get_as_vector_at(&out_value, &list_elt, i);
        EXPECT_EQ(return_code, 0);
        return_code = linkahead_entity_value_get_as_string(&list_elt, &out);
        EXPECT_EQ(return_code, 0);
        EXPECT_STREQ(value_list[i], out); // NOLINT
      }
    
      return_code = linkahead_entity_delete_property(&property);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&in_value);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_int_list_property) {
    
      linkahead_entity_property property;
      int return_code(linkahead_entity_create_property(&property));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype in_type;
      linkahead_entity_create_atomic_list_datatype(&in_type, "INTEGER");
      return_code = linkahead_entity_property_set_datatype(&property, &in_type);
      EXPECT_EQ(return_code, 0);
    
      const int64_t value_list[] = {1, 2, 3}; // NOLINT
      linkahead_entity_value in_value;
      linkahead_entity_create_int_vector_value(&in_value, value_list, 3);
      return_code = linkahead_entity_property_set_value(&property, &in_value);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype out_type;
      return_code = linkahead_entity_property_get_datatype(&property, &out_type);
      EXPECT_EQ(return_code, 0);
    
      bool is_a(false);
      linkahead_entity_datatype_is_atomic(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_list_of_atomic(&out_type, &is_a);
      EXPECT_TRUE(is_a);
      linkahead_entity_datatype_is_list_of_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      char *out = nullptr; // NOLINT
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(out, "INTEGER");
    
      linkahead_entity_value out_value;
      linkahead_entity_property_get_value(&property, &out_value);
      linkahead_entity_value_is_vector(&out_value, &is_a);
      EXPECT_TRUE(is_a);
      int length(-1);
      linkahead_entity_value_get_as_vector_size(&out_value, &length);
      EXPECT_EQ(length, 3);
    
      int64_t out_int = -1;
      linkahead_entity_value list_elt;
      for (int i = 0; i < length; i++) {
        return_code = linkahead_entity_value_get_as_vector_at(&out_value, &list_elt, i);
        EXPECT_EQ(return_code, 0);
        return_code = linkahead_entity_value_get_as_integer(&list_elt, &out_int);
        EXPECT_EQ(return_code, 0);
        EXPECT_EQ(value_list[i], out_int); // NOLINT
      }
    
      return_code = linkahead_entity_delete_property(&property);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&in_value);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_bool_list_property) {
    
      linkahead_entity_property property;
      int return_code(linkahead_entity_create_property(&property));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype in_type;
      linkahead_entity_create_atomic_list_datatype(&in_type, "BOOLEAN");
      return_code = linkahead_entity_property_set_datatype(&property, &in_type);
      EXPECT_EQ(return_code, 0);
    
      const bool value_list[] = {true, true, false}; // NOLINT
      linkahead_entity_value in_value;
      linkahead_entity_create_bool_vector_value(&in_value, value_list, 3);
      return_code = linkahead_entity_property_set_value(&property, &in_value);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_datatype out_type;
      return_code = linkahead_entity_property_get_datatype(&property, &out_type);
      EXPECT_EQ(return_code, 0);
    
      bool is_a(false);
      linkahead_entity_datatype_is_atomic(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      linkahead_entity_datatype_is_list_of_atomic(&out_type, &is_a);
      EXPECT_TRUE(is_a);
      linkahead_entity_datatype_is_list_of_reference(&out_type, &is_a);
      EXPECT_FALSE(is_a);
      char *out = nullptr; // NOLINT
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(out, "BOOLEAN");
    
      linkahead_entity_value out_value;
      linkahead_entity_property_get_value(&property, &out_value);
      linkahead_entity_value_is_vector(&out_value, &is_a);
      EXPECT_TRUE(is_a);
      int length(-1);
      linkahead_entity_value_get_as_vector_size(&out_value, &length);
      EXPECT_EQ(length, 3);
    
      bool out_bool(false);
      linkahead_entity_value list_elt;
      for (int i = 0; i < length; i++) {
        return_code = linkahead_entity_value_get_as_vector_at(&out_value, &list_elt, i);
        EXPECT_EQ(return_code, 0);
        return_code = linkahead_entity_value_get_as_bool(&list_elt, &out_bool);
        EXPECT_EQ(return_code, 0);
        EXPECT_EQ(value_list[i], out_bool); // NOLINT
      }
    
      return_code = linkahead_entity_delete_property(&property);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&in_value);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_entity_with_parent_and_property) {
      linkahead_entity_parent input_parent;
      int return_code(linkahead_entity_create_parent(&input_parent));
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_parent_set_id(&input_parent, "parent_id");
      linkahead_entity_parent_set_name(&input_parent, "parent_name");
    
      linkahead_entity_property input_property;
      return_code = linkahead_entity_create_property(&input_property);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_property_set_id(&input_property, "property_id");
      linkahead_entity_property_set_name(&input_property, "property_name");
    
      linkahead_entity_datatype in_type;
      linkahead_entity_create_atomic_datatype(&in_type, "TEXT");
      linkahead_entity_value in_value;
      linkahead_entity_create_string_value(&in_value, "property_value");
      linkahead_entity_property_set_datatype(&input_property, &in_type);
      linkahead_entity_property_set_value(&input_property, &in_value);
    
      linkahead_entity_entity entity;
      return_code = linkahead_entity_create_entity(&entity);
      EXPECT_EQ(return_code, 0);
    
      return_code = linkahead_entity_entity_append_parent(&entity, &input_parent);
      EXPECT_EQ(return_code, 0);
    
      return_code = linkahead_entity_entity_append_property(&entity, &input_property);
      EXPECT_EQ(return_code, 0);
    
      int count[] = {0}; // NOLINT
      return_code = linkahead_entity_entity_get_parents_size(&entity, count);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(*count, 1);
    
      return_code = linkahead_entity_entity_get_properties_size(&entity, count);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(*count, 1);
    
      char *in = nullptr;  // NOLINT
      char *out = nullptr; // NOLINT
    
      // cannot assign an already assigned property
      // return_code = linkahead_entity_entity_get_property(&entity, &input_property, 0);
      // EXPECT_EQ(return_code, linkahead::StatusCode::EXTERN_C_ASSIGNMENT_ERROR);
      linkahead_entity_property output_property;
      return_code = linkahead_entity_entity_get_property(&entity, &output_property, 0);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_property_get_id(&input_property, &in);
      linkahead_entity_property_get_id(&output_property, &out);
      EXPECT_EQ(strcmp(in, out), 0);
    
      linkahead_entity_property_get_name(&input_property, &in);
      linkahead_entity_property_get_name(&output_property, &out);
      EXPECT_EQ(strcmp(in, out), 0);
    
      linkahead_entity_datatype out_type;
      linkahead_entity_property_get_datatype(&output_property, &out_type);
    
      bool in_is(false);
      bool out_is(false);
      linkahead_entity_datatype_is_atomic(&in_type, &in_is);
      linkahead_entity_datatype_is_atomic(&out_type, &out_is);
      EXPECT_EQ(in_is, out_is);
      linkahead_entity_datatype_is_reference(&in_type, &in_is);
      linkahead_entity_datatype_is_reference(&out_type, &out_is);
      EXPECT_EQ(in_is, out_is);
      linkahead_entity_datatype_is_list_of_atomic(&in_type, &in_is);
      linkahead_entity_datatype_is_list_of_atomic(&out_type, &out_is);
      EXPECT_EQ(in_is, out_is);
      linkahead_entity_datatype_is_list_of_reference(&in_type, &in_is);
      linkahead_entity_datatype_is_list_of_reference(&out_type, &out_is);
      EXPECT_EQ(in_is, out_is);
    
      linkahead_entity_datatype_get_datatype_name(&in_type, &in);
      linkahead_entity_datatype_get_datatype_name(&out_type, &out);
      EXPECT_STREQ(in, out);
    
      linkahead_entity_value out_value;
      linkahead_entity_property_get_value(&output_property, &out_value);
      linkahead_entity_value_is_string(&in_value, &in_is);
      EXPECT_TRUE(in_is);
      linkahead_entity_value_is_string(&out_value, &out_is);
      EXPECT_TRUE(out_is);
      linkahead_entity_value_get_as_string(&in_value, &in);
      linkahead_entity_value_get_as_string(&out_value, &out);
      EXPECT_STREQ(in, out);
    
      linkahead_entity_parent output_parent;
      return_code = linkahead_entity_entity_get_parent(&entity, &output_parent, 0);
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_parent_get_id(&input_parent, &in);
      linkahead_entity_parent_get_id(&output_parent, &out);
      EXPECT_EQ(strcmp(in, out), 0);
    
      linkahead_entity_parent_get_name(&input_parent, &in);
      linkahead_entity_parent_get_name(&output_parent, &out);
      EXPECT_EQ(strcmp(in, out), 0);
    
      // Delete everything
      return_code = linkahead_entity_delete_parent(&input_parent);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_property(&input_property);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_entity(&entity);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_datatype(&in_type);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_value(&in_value);
      EXPECT_EQ(return_code, 0);
    
      // This tests the `_deletable` flag. The wrapped cpp objects of
      // `output_parent` and `output_property` are owned by the entity, so
      // they have been deleted together with the entity. With a wrong
      // `_deletable` flag, the following would cause segfaults.
      //
      return_code = linkahead_entity_delete_parent(&output_parent);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_delete_property(&output_property);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_remove_property) {
      linkahead_entity_entity entity;
      int return_code(linkahead_entity_create_entity(&entity));
      EXPECT_EQ(return_code, 0);
    
      // Create two properties with names
      linkahead_entity_property in_prop_1;
      return_code = linkahead_entity_create_property(&in_prop_1);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_property_set_name(&in_prop_1, "Property 1");
      EXPECT_EQ(return_code, 0);
    
      linkahead_entity_property in_prop_2;
      return_code = linkahead_entity_create_property(&in_prop_2);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_property_set_name(&in_prop_2, "Property 2");
      EXPECT_EQ(return_code, 0);
    
      // Append them
      return_code = linkahead_entity_entity_append_property(&entity, &in_prop_1);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_entity_append_property(&entity, &in_prop_2);
      EXPECT_EQ(return_code, 0);
    
      // Delete one and see that the number of properties decreases by one
      int count[] = {0}; // NOLINT
      return_code = linkahead_entity_entity_get_properties_size(&entity, count);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(*count, 2);
    
      return_code = linkahead_entity_entity_remove_property(&entity, 0);
      EXPECT_EQ(return_code, 0);
    
      return_code = linkahead_entity_entity_get_properties_size(&entity, count);
      EXPECT_EQ(return_code, 0);
      EXPECT_EQ(*count, 1);
    
      linkahead_entity_property out_prop;
      return_code = linkahead_entity_entity_get_property(&entity, &out_prop, 0);
      EXPECT_EQ(return_code, 0);
    
      char *in = nullptr;  // NOLINT
      char *out = nullptr; // NOLINT
    
      // Deleted the first property, so the second one should remain.
      return_code = linkahead_entity_property_get_name(&in_prop_2, &in);
      EXPECT_EQ(return_code, 0);
      return_code = linkahead_entity_property_get_name(&out_prop, &out);
      EXPECT_EQ(return_code, 0);
    
      EXPECT_EQ(strcmp(in, out), 0);
    
      // Delete everything we have created
      return_code = linkahead_entity_delete_property(&in_prop_2);
      EXPECT_EQ(return_code, 0);
    
      return_code = linkahead_entity_delete_property(&in_prop_1);
      EXPECT_EQ(return_code, 0);
    
      return_code = linkahead_entity_delete_entity(&entity);
      EXPECT_EQ(return_code, 0);
    }
    
    TEST_F(test_clinkahead, test_insert_update_delete) {
      // Only test adding to a transaction.  Excution and results are
      // tested in integration tests.
      linkahead_connection_connection connection;
      linkahead_connection_connection_manager_get_default_connection(&connection);
    
      linkahead_transaction_transaction insert_transaction;
      linkahead_connection_connection_create_transaction(&connection, &insert_transaction);
    
      linkahead_entity_entity entity;
      linkahead_entity_create_entity(&entity);
      linkahead_entity_entity_set_name(&entity, "some_name");
      linkahead_entity_entity_set_local_path(&entity, "some_name");
      linkahead_entity_entity_set_file_path(&entity, "some_name");
    
      auto return_code = linkahead_transaction_transaction_insert_entity(&insert_transaction, &entity);
      EXPECT_EQ(return_code, linkahead::StatusCode::GO_ON);
    
      linkahead_transaction_transaction update_transaction;
      linkahead_connection_connection_create_transaction(&connection, &update_transaction);
    
      return_code = linkahead_transaction_transaction_update_entity(&update_transaction, &entity);
      // No ID, so this should be an error
      EXPECT_EQ(return_code, linkahead::StatusCode::ORIGINAL_ENTITY_MISSING_ID);
    
      linkahead_transaction_transaction delete_transaction;
      linkahead_connection_connection_create_transaction(&connection, &delete_transaction);
    
      return_code = linkahead_transaction_transaction_delete_by_id(&delete_transaction, "some_id");
      // Could add further deletions
      EXPECT_EQ(return_code, linkahead::StatusCode::GO_ON);
    
      linkahead_entity_delete_entity(&entity);
      linkahead_transaction_delete_transaction(&insert_transaction);
      linkahead_transaction_delete_transaction(&update_transaction);
      linkahead_transaction_delete_transaction(&delete_transaction);
    }