Add e2e tests for temporal types (#241)

This commit is contained in:
Kostas Kyrimis 2021-10-02 12:49:07 +02:00 committed by Antonio Andelic
parent 7e81a95b81
commit 18b801a722
8 changed files with 259 additions and 3 deletions

View File

@ -203,7 +203,8 @@ import_external_library(mgclient STATIC
${CMAKE_CURRENT_SOURCE_DIR}/mgclient/include
CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DBUILD_TESTING=OFF)
-DBUILD_TESTING=OFF
-DBUILD_CPP_BINDINGS=ON)
find_package(OpenSSL REQUIRED)
target_link_libraries(mgclient INTERFACE ${OPENSSL_LIBRARIES})

View File

@ -212,7 +212,7 @@ repo_clone_try_double "${primary_urls[rocksdb]}" "${secondary_urls[rocksdb]}" "r
sed -i 's/TARGETS ${ROCKSDB_SHARED_LIB}/TARGETS ${ROCKSDB_SHARED_LIB} OPTIONAL/' rocksdb/CMakeLists.txt
# mgclient
mgclient_tag="v1.2.0" # (2021-01-14)
mgclient_tag="v1.3.0" # (2021-09-23)
repo_clone_try_double "${primary_urls[mgclient]}" "${secondary_urls[mgclient]}" "mgclient" "$mgclient_tag"
sed -i 's/\${CMAKE_INSTALL_LIBDIR}/lib/' mgclient/src/CMakeLists.txt

View File

@ -469,7 +469,12 @@ std::pair<DateParameters, LocalTimeParameters> ParseLocalDateTimeParameters(std:
LocalDateTime::LocalDateTime(const int64_t microseconds) {
auto chrono_microseconds = std::chrono::microseconds(microseconds);
date = Date(chrono_microseconds.count());
constexpr int64_t one_day_in_microseconds = std::chrono::microseconds{std::chrono::days{1}}.count();
if (microseconds < 0 && (microseconds % one_day_in_microseconds != 0)) {
date = Date(microseconds - one_day_in_microseconds);
} else {
date = Date(microseconds);
}
chrono_microseconds -= std::chrono::microseconds{date.MicrosecondsSinceEpoch()};
local_time = LocalTime(chrono_microseconds.count());
}

View File

@ -11,6 +11,7 @@ add_subdirectory(memory)
add_subdirectory(triggers)
add_subdirectory(isolation_levels)
add_subdirectory(streams)
add_subdirectory(temporal_types)
add_subdirectory(write_procedures)
copy_e2e_python_files(pytest_runner pytest_runner.sh "")

View File

@ -0,0 +1,3 @@
add_executable(memgraph__e2e__temporal_roundtrip roundtrip.cpp)
target_link_libraries(memgraph__e2e__temporal_roundtrip PUBLIC mgclient mg-utils gflags)

View File

@ -0,0 +1,219 @@
#include <variant>
#include <gflags/gflags.h>
#include "fmt/format.h"
#include "mgclient.hpp"
#include "utils/logging.hpp"
#include "utils/temporal.hpp"
DEFINE_uint64(bolt_port, 0, "Bolt port arguments");
void MaybeExecuteQuery(mg::Client &client, const std::string &query, const std::string_view name) {
auto executed = client.Execute(query);
MG_ASSERT(executed, "Failed to execute {} query", name);
client.DiscardAll();
}
auto MaybeExecuteMatch(mg::Client &client, const std::string_view group) {
auto executed = client.Execute(fmt::format("MATCH (u:{}) return u", group));
MG_ASSERT(executed, "Failed to execute query");
const auto result = client.FetchAll();
MG_ASSERT(result, "Failed to fetch results");
MG_ASSERT(result->size() == 1, "Failed to fetch correct result size");
return result;
}
auto GetItFromNodeProperty(const mg::ConstMap &props, const std::string_view property) {
const auto it = props.find(property);
MG_ASSERT(it != props.end(), "Failed to find property {}", property);
return it;
}
void RoundtripDuration(mg::Client &client, const std::string_view group, const std::string_view property,
const std::string_view dur_str, const utils::Duration &expected) {
const auto query = fmt::format("CREATE (:{} {{{}: DURATION({})}})", group, property, dur_str);
MaybeExecuteQuery(client, query, "Duration");
const auto result = MaybeExecuteMatch(client, group);
const auto node = (*result)[0][0].ValueNode();
const auto props = node.properties();
const auto it = GetItFromNodeProperty(props, property);
const auto dur = (*it).second.ValueDuration();
MG_ASSERT(dur.months() == 0, "Received incorrect months in the duration");
MG_ASSERT(dur.days() == expected.Days(), "Received incorrect days in the duration");
MG_ASSERT(dur.seconds() == expected.SubDaysAsSeconds(), "Received incorrect seconds in the duration");
MG_ASSERT(dur.nanoseconds() == expected.SubSecondsAsNanoseconds(), "Received incorrect nanoseconds in the duration");
}
void RoundtripDate(mg::Client &client, const std::string_view group, const std::string_view property,
const std::string_view date_str, const utils::Date &expected) {
const auto query = fmt::format("CREATE (:{} {{{}: DATE({})}})", group, property, date_str);
MaybeExecuteQuery(client, query, "Date");
const auto result = MaybeExecuteMatch(client, group);
const auto node = (*result)[0][0].ValueNode();
const auto props = node.properties();
const auto it = GetItFromNodeProperty(props, property);
const auto date = (*it).second.ValueDate();
MG_ASSERT(date.days() == expected.DaysSinceEpoch(), "Received incorrect days in the date roundtrip");
}
struct LocalTimeParams {
int64_t hours{0};
int64_t minutes{0};
int64_t seconds{0};
int64_t subseconds{0};
};
void RoundtripLocalTime(mg::Client &client, const std::string_view group, const std::string_view property,
const std::string_view lt_str, const utils::LocalTime &expected) {
const auto query = fmt::format("CREATE (:{} {{{}: LOCALTIME({})}})", group, property, lt_str);
MaybeExecuteQuery(client, query, "LocalTime");
const auto result = MaybeExecuteMatch(client, group);
const auto node = (*result)[0][0].ValueNode();
const auto props = node.properties();
const auto it = GetItFromNodeProperty(props, property);
const auto lt = (*it).second.ValueLocalTime();
MG_ASSERT(lt.nanoseconds() == expected.NanosecondsSinceEpoch(),
"Received incorrect nanoseconds in the LocalTime roundtrip");
}
void RoundtripLocalDateTime(mg::Client &client, const std::string_view group, const std::string_view property,
const std::string_view ldt_str, const utils::LocalDateTime &expected) {
const auto query = fmt::format("CREATE (:{} {{{}: LOCALDATETIME({})}})", group, property, ldt_str);
MaybeExecuteQuery(client, query, "LocalDateTime");
const auto result = MaybeExecuteMatch(client, group);
const auto node = (*result)[0][0].ValueNode();
const auto props = node.properties();
const auto it = GetItFromNodeProperty(props, property);
const auto ldt = (*it).second.ValueLocalDateTime();
MG_ASSERT(ldt.seconds() == expected.SecondsSinceEpoch(), "Received incorrect seconds in the LocalDateTime roundtrip");
MG_ASSERT(ldt.nanoseconds() == expected.SubSecondsAsNanoseconds(),
"Received incorrect nanoseconds in the LocalDateTime roundtrip");
}
void TestDate(mg::Client &client) {
auto date_query = [](auto year, auto month, auto day) {
return fmt::format("\"{:0>2}-{:0>2}-{:0>2}\"", year, month, day);
};
auto date_query_map = [](auto year, auto month, auto day) {
return fmt::format("{{year:{}, month:{}, day:{}}}", year, month, day);
};
RoundtripDate(client, "Person1", "dob", date_query(1960, 1, 12), utils::Date({1960, 1, 12}));
RoundtripDate(client, "Person2", "dob", date_query(1970, 1, 1), utils::Date({1970, 1, 1}));
RoundtripDate(client, "Person3", "dob", date_query(1971, 2, 2), utils::Date({1971, 2, 2}));
RoundtripDate(client, "Person4", "dob", date_query(2021, 12, 9), utils::Date({2021, 12, 9}));
RoundtripDate(client, "PersonMap1", "dob", date_query_map(1970, 1, 1), utils::Date({1970, 1, 1}));
RoundtripDate(client, "PersonMap2", "dob", date_query_map(1971, 6, 5), utils::Date({1971, 6, 5}));
RoundtripDate(client, "PersonMap3", "dob", date_query_map(2000, 8, 9), utils::Date({2000, 8, 9}));
RoundtripDate(client, "PersonMap4", "dob", date_query_map(2021, 12, 9), utils::Date({2021, 12, 9}));
}
void TestLocalTime(mg::Client &client) {
auto lt = [](auto h, auto m, auto s, auto ss) { return fmt::format("{:2>2}:{:0>2}:{:0>2}.{:0>6}", h, m, s, ss); };
auto lt_query = [](auto lt_as_str) { return fmt::format("\"{}\"", lt_as_str); };
auto lt_query_map = [](int h = 0, int m = 0, int s = 0, int ml = 0, int mi = 0) {
return fmt::format("{{hour:{}, minute:{}, second:{}, millisecond:{}, microsecond:{}}}", h, m, s, ml, mi);
};
const auto parse = [](const std::string_view query) {
return utils::LocalTime(utils::ParseLocalTimeParameters(fmt::format("{}", query)).first);
};
const auto str1 = lt(1, 3, 3, 33);
RoundtripLocalTime(client, "LT1", "time", lt_query(str1), parse(str1));
const auto str2 = lt(13, 4, 44, 1002);
RoundtripLocalTime(client, "LT2", "time", lt_query(str2), parse(str2));
const auto str3 = lt(18, 22, 21, 68010);
RoundtripLocalTime(client, "LT3", "time", lt_query(str3), parse(str3));
const auto str4 = lt(1, 3, 3, 33);
RoundtripLocalTime(client, "LT4", "time", lt_query(str4), parse(str4));
const auto str5 = lt_query_map(10, 4, 22, 33, 99);
RoundtripLocalTime(client, "LT5", "time", str5, utils::LocalTime({10, 4, 22, 33, 99}));
const auto str6 = lt_query_map(0, 0, 21, 12, 88);
RoundtripLocalTime(client, "LT6", "time", str6, utils::LocalTime({0, 0, 21, 12, 88}));
const auto str7 = lt_query_map(8, 4, 22, 33, 99);
RoundtripLocalTime(client, "LT7", "time", str7, utils::LocalTime({8, 4, 22, 33, 99}));
const auto str8 = lt_query_map(23, 1, 0, 0, 0);
RoundtripLocalTime(client, "LT8", "time", str8, utils::LocalTime({23, 1, 0, 0, 0}));
}
void TestLocalDateTime(mg::Client &client) {
auto ldt = [](auto y, auto mo, auto d, auto h, auto m, auto s) {
return fmt::format("{:0>2}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}", y, mo, d, h, m, s);
};
auto parse = [](const std::string_view str) {
const auto [dt, lt] = utils::ParseLocalDateTimeParameters(str);
return utils::LocalDateTime(dt, lt);
};
auto ldt_query = [](const std::string_view str) { return fmt::format("\"{}\"", str); };
const auto str = ldt(1200, 8, 9, 12, 33, 1);
RoundtripLocalDateTime(client, "LDT1", "time", ldt_query(str), parse(str));
const auto str1 = ldt(1961, 6, 3, 11, 22, 10);
RoundtripLocalDateTime(client, "LDT2", "time", ldt_query(str1), parse(str1));
const auto str2 = ldt(1971, 1, 1, 15, 16, 2);
RoundtripLocalDateTime(client, "LDT3", "time", ldt_query(str2), parse(str2));
const auto str3 = ldt(2000, 1, 1, 2, 33, 1);
RoundtripLocalDateTime(client, "LDT4", "time", ldt_query(str3), parse(str3));
const auto str4 = ldt(2021, 9, 21, 16, 57, 1);
RoundtripLocalDateTime(client, "LDT5", "time", ldt_query(str4), parse(str4));
RoundtripLocalDateTime(client, "Map_LDT1", "time", "{year:1960, month:10, day:1}",
utils::LocalDateTime({1960, 10, 1}, {}));
RoundtripLocalDateTime(client, "Map_LDT2", "time", "{year:1960, month:10, day:1, hour:1, minute:2, second:33}",
utils::LocalDateTime({1960, 10, 1}, {1, 2, 33}));
RoundtripLocalDateTime(client, "Map_LDT3", "time", "{hour:10}", utils::LocalDateTime({}, {.hours = 10}));
RoundtripLocalDateTime(client, "Map_LDT4", "time", "{day:2}", utils::LocalDateTime({.days = 2}, {}));
}
void TestDuration(mg::Client &client) {
const auto dur = [](auto d, auto h, auto m, auto s, auto ss) {
return fmt::format("\"P{}DT{}H{}M{}.{}S\"", d, h, m, s, ss);
};
RoundtripDuration(client, "Runner1", "time", dur(3, 5, 6, 2, 1), utils::Duration({3, 5, 6, 2.1}));
RoundtripDuration(client, "Runner2", "time", dur(8, 9, 8, 4, 12), utils::Duration({8, 9, 8, 4.12}));
RoundtripDuration(client, "Runner3", "time", dur(10, 10, 12, 44, 44), utils::Duration({10, 10, 12, 44.44}));
RoundtripDuration(client, "Runner4", "time", dur(23, 11, 13, 59, 100000), utils::Duration({23, 11, 13, 59, 100}));
RoundtripDuration(client, "Runner5", "time", dur(0, 110, 14, 88, 400000), utils::Duration({0, 110, 14, 88, 400}));
// fractions
RoundtripDuration(client, "Runner6", "time", "\"P4.5D\"", utils::Duration(utils::DurationParameters{.days = 4.5}));
RoundtripDuration(client, "Runner7", "time", "\"PT9.3H\"", utils::Duration(utils::DurationParameters{.hours = 9.3}));
RoundtripDuration(client, "Runner8", "time", "\"PT4.2M\"",
utils::Duration(utils::DurationParameters{.minutes = 4.2}));
RoundtripDuration(client, "Runner9", "time", "\"PT8.4S\"",
utils::Duration(utils::DurationParameters{.seconds = 8.4}));
RoundtripDuration(client, "RunnerMap1", "time",
"{day:0, hour:4, minute:1, second:44, millisecond:44, microsecond:22}",
utils::Duration(utils::DurationParameters{0, 4, 1, 44, 44, 22}));
RoundtripDuration(client, "RunnerMap2", "time", "{day:15}", utils::Duration(utils::DurationParameters{15}));
RoundtripDuration(client, "RunnerMap3", "time", "{hour:2.5}",
utils::Duration(utils::DurationParameters{.hours = 2.5}));
RoundtripDuration(client, "RunnerMap4", "time", "{minute:10.5, second:44}",
utils::Duration(utils::DurationParameters{.minutes = 10.5, .seconds = 44}));
RoundtripDuration(client, "NegRunnerMap1", "time",
"{day:0, hour:-1, minute:-2, second:-20, millisecond:-4, microsecond:-15}",
utils::Duration(utils::DurationParameters{0, -1, -2, -20, -4, -15}));
RoundtripDuration(client, "NegRunnerMap2", "time", "{day:-15}", utils::Duration(utils::DurationParameters{-15}));
RoundtripDuration(client, "NegRunnerMap3", "time", "{hour:-2.5}",
utils::Duration(utils::DurationParameters{.hours = -2.5}));
}
int main(int argc, char **argv) {
gflags::SetUsageMessage("Memgraph E2E temporal types roundtrip");
gflags::ParseCommandLineFlags(&argc, &argv, true);
MG_ASSERT(FLAGS_bolt_port != 0);
logging::RedirectToStderr();
mg::Client::Init();
auto client = mg::Client::Connect({.port = static_cast<uint16_t>(FLAGS_bolt_port)});
MG_ASSERT(client, "Failed to connect with memgraph");
TestDate(*client);
TestLocalTime(*client);
TestLocalDateTime(*client);
TestDuration(*client);
return 0;
}

View File

@ -0,0 +1,14 @@
bolt_port: &bolt_port "7687"
template_cluster: &template_cluster
cluster:
main:
args: ["--bolt_port", *bolt_port, "--log-level=TRACE"]
log_file: "temporal-types-e2e.log"
setup_queries: []
validation_queries: []
workloads:
- name: "Temporal"
binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip"
args: ["--bolt_port", *bolt_port]
<<: *template_cluster

View File

@ -124,6 +124,9 @@ TEST(TemporalTest, LocalDateTimeMicrosecondsSinceEpochConversion) {
check_microseconds(utils::DateParameters{2020, 11, 22}, utils::LocalTimeParameters{0, 0, 0, 0, 0});
check_microseconds(utils::DateParameters{1900, 2, 22}, utils::LocalTimeParameters{0, 0, 0, 0, 0});
check_microseconds(utils::DateParameters{0, 1, 1}, utils::LocalTimeParameters{0, 0, 0, 0, 0});
check_microseconds(utils::DateParameters{1961, 1, 1}, utils::LocalTimeParameters{15, 44, 12});
check_microseconds(utils::DateParameters{1969, 12, 31}, utils::LocalTimeParameters{23, 59, 59});
{
utils::LocalDateTime local_date_time(utils::DateParameters{1970, 1, 1}, utils::LocalTimeParameters{0, 0, 0, 0, 0});
ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(), 0);
@ -146,6 +149,13 @@ TEST(TemporalTest, LocalDateTimeMicrosecondsSinceEpochConversion) {
utils::LocalDateTime local_date_time(utils::DateParameters{2021, 1, 1}, utils::LocalTimeParameters{0, 0, 0, 0, 0});
ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0);
}
{
// Assert ordering for dates prior the unix epoch.
// If this test fails, our storage indexes will be incorrect.
utils::LocalDateTime ldt({1969, 12, 31}, {0, 0, 0});
utils::LocalDateTime ldt2({1969, 12, 31}, {23, 59, 59});
ASSERT_LT(ldt.MicrosecondsSinceEpoch(), ldt2.MicrosecondsSinceEpoch());
}
}
TEST(TemporalTest, DurationConversion) {
@ -527,6 +537,9 @@ TEST(TemporalTest, LocalDateTimeAdditionSubtraction) {
ASSERT_EQ(one_day_after_unix_epoch, utils::LocalDateTime({1970, 1, 2}, {.hours = 12}));
ASSERT_EQ(one_day_after_unix_epoch_symmetrical, one_day_after_unix_epoch);
const auto one_day_before_unix_epoch = utils::LocalDateTime({1969, 12, 31}, {23, 59, 59});
ASSERT_EQ(one_day_before_unix_epoch + utils::Duration({.seconds = 1}), utils::LocalDateTime({1970, 1, 1}, {}));
one_day_after_unix_epoch = unix_epoch + utils::Duration({.days = 1});
ASSERT_EQ(one_day_after_unix_epoch, utils::LocalDateTime({1970, 1, 2}, {.hours = 12}));