0fb8e4116f
* Fix up REPLICA GetInfo and CreateSnapshot Subtle bug where these actions were using the incorrect transactional access while in REPLICA role. This casued timestamp to be incorrectly bumped, breaking REPLICA from doing replication. * Delay DNS resolution Rather than resolve at endpoint creation, we will instread resolve only on Socket connect. This allows k8s deployments to change their IP during pod restarts. * Minor sonarsource fixes --------- Co-authored-by: Andreja <andreja.tonev@memgraph.io> Co-authored-by: DavIvek <david.ivekovic@memgraph.io>
543 lines
20 KiB
C++
543 lines
20 KiB
C++
// Copyright 2024 Memgraph Ltd.
|
|
//
|
|
// Use of this software is governed by the Business Source License
|
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
// License, and you may not use this file except in compliance with the Business Source License.
|
|
//
|
|
// As of the Change Date specified in that file, in accordance with
|
|
// the Business Source License, use of this software will be governed
|
|
// by the Apache License, Version 2.0, included in the file
|
|
// licenses/APL.txt.
|
|
|
|
#include <array>
|
|
#include <bit>
|
|
#include <memory>
|
|
|
|
#include "bolt_common.hpp"
|
|
#include "bolt_testdata.hpp"
|
|
#include "communication/bolt/v1/codes.hpp"
|
|
#include "communication/bolt/v1/encoder/encoder.hpp"
|
|
#include "disk_test_utils.hpp"
|
|
#include "glue/communication.hpp"
|
|
#include "storage/v2/disk/storage.hpp"
|
|
#include "storage/v2/inmemory/storage.hpp"
|
|
#include "storage/v2/storage.hpp"
|
|
#include "utils/temporal.hpp"
|
|
using memgraph::communication::bolt::Value;
|
|
|
|
/**
|
|
* TODO (mferencevic): document
|
|
*/
|
|
|
|
inline constexpr const int SIZE = 131072;
|
|
uint8_t data[SIZE];
|
|
|
|
uint64_t GetBigEndianInt(std::vector<uint8_t> &v, uint8_t len, uint8_t offset = 1) {
|
|
uint64_t ret = 0;
|
|
v.erase(v.begin(), v.begin() + offset);
|
|
for (int i = 0; i < len; ++i) {
|
|
ret <<= 8;
|
|
ret += v[i];
|
|
}
|
|
v.erase(v.begin(), v.begin() + len);
|
|
return ret;
|
|
}
|
|
|
|
void CheckTypeSize(std::vector<uint8_t> &v, int typ, uint64_t size) {
|
|
uint64_t len;
|
|
if ((v[0] & 0xF0) == type_tiny_magic[typ]) {
|
|
len = v[0] & 0x0F;
|
|
v.erase(v.begin(), v.begin() + 1);
|
|
} else if (v[0] == type_8_magic[typ]) {
|
|
len = GetBigEndianInt(v, 1);
|
|
} else if (v[0] == type_16_magic[typ]) {
|
|
len = GetBigEndianInt(v, 2);
|
|
} else if (v[0] == type_32_magic[typ]) {
|
|
len = GetBigEndianInt(v, 4);
|
|
} else {
|
|
FAIL() << "Got wrong marker!";
|
|
}
|
|
ASSERT_EQ(len, size);
|
|
}
|
|
|
|
void CheckInt(std::vector<uint8_t> &output, int64_t value) {
|
|
TestOutputStream output_stream;
|
|
TestBuffer encoder_buffer(output_stream);
|
|
memgraph::communication::bolt::BaseEncoder<TestBuffer> bolt_encoder(encoder_buffer);
|
|
std::vector<uint8_t> &encoded = output_stream.output;
|
|
bolt_encoder.WriteInt(value);
|
|
CheckOutput(output, encoded.data(), encoded.size(), false);
|
|
}
|
|
|
|
void CheckRecordHeader(std::vector<uint8_t> &v, uint64_t size) {
|
|
CheckOutput(v, (const uint8_t *)"\xB1\x71", 2, false);
|
|
CheckTypeSize(v, LIST, size);
|
|
}
|
|
|
|
TestOutputStream output_stream;
|
|
TestBuffer encoder_buffer(output_stream);
|
|
memgraph::communication::bolt::Encoder<TestBuffer> bolt_encoder(encoder_buffer);
|
|
std::vector<uint8_t> &output = output_stream.output;
|
|
|
|
struct BoltEncoder : ::testing::Test {
|
|
// In newer gtest library (1.8.1+) this is changed to SetUpTestSuite
|
|
static void SetUpTestCase() { InitializeData(data, SIZE); }
|
|
};
|
|
|
|
TEST_F(BoltEncoder, NullAndBool) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
vals.push_back(Value());
|
|
vals.push_back(Value(true));
|
|
vals.push_back(Value(false));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, 3);
|
|
CheckOutput(output, (const uint8_t *)"\xC0\xC3\xC2", 3);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, Int) {
|
|
int N = 28;
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
for (int i = 0; i < N; ++i) vals.push_back(Value(int_decoded[i]));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, N);
|
|
for (int i = 0; i < N; ++i) CheckOutput(output, int_encoded[i], int_encoded_len[i], false);
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, Double) {
|
|
int N = 4;
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
for (int i = 0; i < N; ++i) vals.push_back(Value(double_decoded[i]));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, N);
|
|
for (int i = 0; i < N; ++i) CheckOutput(output, double_encoded[i], 9, false);
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, String) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
for (uint64_t i = 0; i < sizes_num; ++i) vals.push_back(Value(std::string((const char *)data, sizes[i])));
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, vals.size());
|
|
for (uint64_t i = 0; i < sizes_num; ++i) {
|
|
CheckTypeSize(output, STRING, sizes[i]);
|
|
CheckOutput(output, data, sizes[i], false);
|
|
}
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, List) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
for (uint64_t i = 0; i < sizes_num; ++i) {
|
|
std::vector<Value> val;
|
|
for (uint64_t j = 0; j < sizes[i]; ++j) val.push_back(Value(std::string((const char *)&data[j], 1)));
|
|
vals.push_back(Value(val));
|
|
}
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, vals.size());
|
|
for (uint64_t i = 0; i < sizes_num; ++i) {
|
|
CheckTypeSize(output, LIST, sizes[i]);
|
|
for (uint64_t j = 0; j < sizes[i]; ++j) {
|
|
CheckTypeSize(output, STRING, 1);
|
|
CheckOutput(output, &data[j], 1, false);
|
|
}
|
|
}
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, Map) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
uint8_t buff[10];
|
|
for (int i = 0; i < sizes_num; ++i) {
|
|
std::map<std::string, Value> val;
|
|
for (int j = 0; j < sizes[i]; ++j) {
|
|
sprintf((char *)buff, "%05X", j);
|
|
std::string tmp((char *)buff, 5);
|
|
val.insert(std::make_pair(tmp, Value(tmp)));
|
|
}
|
|
vals.push_back(Value(val));
|
|
}
|
|
bolt_encoder.MessageRecord(vals);
|
|
CheckRecordHeader(output, vals.size());
|
|
for (int i = 0; i < sizes_num; ++i) {
|
|
CheckTypeSize(output, MAP, sizes[i]);
|
|
for (int j = 0; j < sizes[i]; ++j) {
|
|
sprintf((char *)buff, "%05X", j);
|
|
CheckTypeSize(output, STRING, 5);
|
|
CheckOutput(output, buff, 5, false);
|
|
CheckTypeSize(output, STRING, 5);
|
|
CheckOutput(output, buff, 5, false);
|
|
}
|
|
}
|
|
CheckOutput(output, nullptr, 0);
|
|
}
|
|
|
|
void TestVertexAndEdgeWithDifferentStorages(std::unique_ptr<memgraph::storage::Storage> &&db) {
|
|
output.clear();
|
|
|
|
// create vertex
|
|
auto dba = db->Access(memgraph::replication::ReplicationRole::MAIN);
|
|
auto va1 = dba->CreateVertex();
|
|
auto va2 = dba->CreateVertex();
|
|
auto l1 = dba->NameToLabel("label1");
|
|
auto l2 = dba->NameToLabel("label2");
|
|
ASSERT_TRUE(va1.AddLabel(l1).HasValue());
|
|
ASSERT_TRUE(va1.AddLabel(l2).HasValue());
|
|
auto p1 = dba->NameToProperty("prop1");
|
|
auto p2 = dba->NameToProperty("prop2");
|
|
memgraph::storage::PropertyValue pv1(12), pv2(200);
|
|
ASSERT_TRUE(va1.SetProperty(p1, pv1).HasValue());
|
|
ASSERT_TRUE(va1.SetProperty(p2, pv2).HasValue());
|
|
|
|
// create edge
|
|
auto et = dba->NameToEdgeType("edgetype");
|
|
auto ea = dba->CreateEdge(&va1, &va2, et).GetValue();
|
|
auto p3 = dba->NameToProperty("prop3");
|
|
auto p4 = dba->NameToProperty("prop4");
|
|
memgraph::storage::PropertyValue pv3(42), pv4(1234);
|
|
ASSERT_TRUE(ea.SetProperty(p3, pv3).HasValue());
|
|
ASSERT_TRUE(ea.SetProperty(p4, pv4).HasValue());
|
|
|
|
// check everything
|
|
std::vector<Value> vals;
|
|
vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::VertexAccessor(va1)), *db,
|
|
memgraph::storage::View::NEW));
|
|
vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::VertexAccessor(va2)), *db,
|
|
memgraph::storage::View::NEW));
|
|
vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::EdgeAccessor(ea)), *db,
|
|
memgraph::storage::View::NEW));
|
|
bolt_encoder.MessageRecord(vals);
|
|
|
|
// The vertexedge_encoded testdata has hardcoded zeros for IDs,
|
|
// and Memgraph now encodes IDs so we need to check the output
|
|
// part by part.
|
|
CheckOutput(output, vertexedge_encoded, 5, false);
|
|
CheckInt(output, va1.Gid().AsInt());
|
|
CheckOutput(output, vertexedge_encoded + 6, 34, false);
|
|
CheckInt(output, va2.Gid().AsInt());
|
|
CheckOutput(output, vertexedge_encoded + 41, 4, false);
|
|
CheckInt(output, ea.Gid().AsInt());
|
|
CheckInt(output, va1.Gid().AsInt());
|
|
CheckInt(output, va2.Gid().AsInt());
|
|
CheckOutput(output, vertexedge_encoded + 48, 26);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, VertexAndEdgeInMemoryStorage) {
|
|
std::unique_ptr<memgraph::storage::Storage> db{new memgraph::storage::InMemoryStorage()};
|
|
TestVertexAndEdgeWithDifferentStorages(std::move(db));
|
|
}
|
|
|
|
TEST_F(BoltEncoder, VertexAndEdgeOnDiskStorage) {
|
|
const std::string testSuite = "bolt_encoder";
|
|
memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
|
|
|
|
std::unique_ptr<memgraph::storage::Storage> db{new memgraph::storage::DiskStorage(config)};
|
|
TestVertexAndEdgeWithDifferentStorages(std::move(db));
|
|
|
|
disk_test_utils::RemoveRocksDbDirs(testSuite);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, BoltV1ExampleMessages) {
|
|
// this test checks example messages from: http://boltprotocol.org/v1/
|
|
|
|
output.clear();
|
|
|
|
// record message
|
|
std::vector<Value> rvals;
|
|
for (int i = 1; i < 4; ++i) rvals.push_back(Value(i));
|
|
bolt_encoder.MessageRecord(rvals);
|
|
CheckOutput(output, (const uint8_t *)"\xB1\x71\x93\x01\x02\x03", 6);
|
|
|
|
// success message
|
|
std::string sv1("name"), sv2("age"), sk("fields");
|
|
std::vector<Value> svec;
|
|
svec.push_back(Value(sv1));
|
|
svec.push_back(Value(sv2));
|
|
Value slist(svec);
|
|
std::map<std::string, Value> svals;
|
|
svals.insert(std::make_pair(sk, slist));
|
|
bolt_encoder.MessageSuccess(svals);
|
|
CheckOutput(output,
|
|
(const uint8_t *)"\xB1\x70\xA1\x86\x66\x69\x65\x6C\x64\x73\x92\x84\x6E\x61\x6D\x65\x83\x61\x67\x65", 20);
|
|
|
|
// failure message
|
|
std::string fv1("Neo.ClientError.Statement.SyntaxError"), fv2("Invalid syntax.");
|
|
std::string fk1("code"), fk2("message");
|
|
Value ftv1(fv1), ftv2(fv2);
|
|
std::map<std::string, Value> fvals;
|
|
fvals.insert(std::make_pair(fk1, ftv1));
|
|
fvals.insert(std::make_pair(fk2, ftv2));
|
|
bolt_encoder.MessageFailure(fvals);
|
|
CheckOutput(output,
|
|
(const uint8_t *)
|
|
"\xB1\x7F\xA2\x84\x63\x6F\x64\x65\xD0\x25\x4E\x65\x6F\x2E\x43\x6C\x69\x65\x6E\x74\x45\x72\x72\x6F\x72\x2E\x53\x74\x61\x74\x65\x6D\x65\x6E\x74\x2E\x53\x79\x6E\x74\x61\x78\x45\x72\x72\x6F\x72\x87\x6D\x65\x73\x73\x61\x67\x65\x8F\x49\x6E\x76\x61\x6C\x69\x64\x20\x73\x79\x6E\x74\x61\x78\x2E",
|
|
71);
|
|
|
|
// ignored message
|
|
bolt_encoder.MessageIgnored();
|
|
CheckOutput(output, (const uint8_t *)"\xB0\x7E", 2);
|
|
}
|
|
|
|
// Temporal types testing starts here
|
|
template <typename T>
|
|
constexpr uint8_t Cast(T marker) {
|
|
return static_cast<uint8_t>(marker);
|
|
}
|
|
|
|
TEST_F(BoltEncoder, DateOld) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::Date({1970, 1, 1}));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &date = value.ValueDate();
|
|
const auto days = date.DaysSinceEpoch();
|
|
ASSERT_EQ(days, 0);
|
|
const auto *d_bytes = std::bit_cast<const uint8_t *>(&days);
|
|
// 0x91 denotes the size of vals (it's 0x91 because it's anded -- see
|
|
// WriteTypeSize() in base_encoder.hpp).
|
|
// We reverse the order of d_bytes because after the encoding
|
|
// it has BigEndian orderring.
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 6> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Date),
|
|
d_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, DateRecent) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::Date({2021, 7, 20}));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &date = value.ValueDate();
|
|
const auto days = date.DaysSinceEpoch();
|
|
ASSERT_EQ(days, 18828);
|
|
const auto *d_bytes = std::bit_cast<const uint8_t *>(&days);
|
|
// 0x91 denotes the size of vals (it's 0x91 because it's anded -- see
|
|
// WriteTypeSize() in base_encoder.hpp).
|
|
// We reverse the order of d_bytes because after the encoding
|
|
// it has BigEndian orderring.
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 8> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Date),
|
|
Cast(Marker::Int16),
|
|
d_bytes[1],
|
|
d_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, DurationOneSec) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::Duration(1));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &dur = value.ValueDuration();
|
|
ASSERT_EQ(dur.Days(), 0);
|
|
ASSERT_EQ(dur.SubDaysAsSeconds(), 0);
|
|
const auto nanos = dur.SubSecondsAsNanoseconds();
|
|
ASSERT_EQ(nanos, 1000);
|
|
const auto *d_bytes = std::bit_cast<const uint8_t *>(&nanos);
|
|
// 0x91 denotes the size of vals (it's 0x91 because it's anded -- see
|
|
// WriteTypeSize in base_encoder.hpp).
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 11> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct4),
|
|
Cast(Sig::Duration),
|
|
0x0,
|
|
0x0,
|
|
0x0,
|
|
Cast(Marker::Int16),
|
|
d_bytes[1],
|
|
d_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, DurationMinusOneSec) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::Duration(-1));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &dur = value.ValueDuration();
|
|
ASSERT_EQ(dur.Days(), 0);
|
|
ASSERT_EQ(dur.SubDaysAsSeconds(), 0);
|
|
const auto nanos = dur.SubSecondsAsNanoseconds();
|
|
const auto *d_bytes = std::bit_cast<const uint8_t *>(&nanos);
|
|
ASSERT_EQ(nanos, -1000);
|
|
// 0x91 denotes the size of vals (it's 0x91 because it's anded -- see
|
|
// WriteTypeSize in base_encoder.hpp).
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 11> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct4),
|
|
Cast(Sig::Duration),
|
|
0x0,
|
|
0x0,
|
|
0x0,
|
|
Cast(Marker::Int16),
|
|
d_bytes[1],
|
|
d_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, ArbitraryDuration) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::Duration({15, 1, 2, 3, 5, 0}));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &dur = value.ValueDuration();
|
|
// This is an approximation because of chrono::months to days conversion
|
|
ASSERT_EQ(dur.Days(), 15);
|
|
const auto secs = dur.SubDaysAsSeconds();
|
|
ASSERT_EQ(secs, 3723);
|
|
const auto *sec_bytes = std::bit_cast<const uint8_t *>(&secs);
|
|
const auto nanos = dur.SubSecondsAsNanoseconds();
|
|
ASSERT_EQ(nanos, 5000000);
|
|
const auto *nano_bytes = std::bit_cast<const uint8_t *>(&nanos);
|
|
// 0x91 denotes the size of vals (it's 0x91 because it's anded -- see
|
|
// WriteTypeSize in base_encoder.hpp).
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 15> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct4),
|
|
Cast(Sig::Duration),
|
|
0x0,
|
|
0xF,
|
|
Cast(Marker::Int16),
|
|
sec_bytes[1],
|
|
sec_bytes[0],
|
|
Cast(Marker::Int32),
|
|
nano_bytes[3],
|
|
nano_bytes[2],
|
|
nano_bytes[1],
|
|
nano_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, LocalTimeOneMicro) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::LocalTime(1));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &local_time = value.ValueLocalTime();
|
|
const auto nanos = local_time.NanosecondsSinceEpoch();
|
|
ASSERT_EQ(nanos, 1000);
|
|
const auto *n_bytes = std::bit_cast<const uint8_t *>(&nanos);
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 8> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::LocalTime),
|
|
Cast(Marker::Int16),
|
|
n_bytes[1], n_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, LocalTimeOneThousandMicro) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value = Value(memgraph::utils::LocalTime(1000));
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto &local_time = value.ValueLocalTime();
|
|
const auto nanos = local_time.NanosecondsSinceEpoch();
|
|
ASSERT_EQ(nanos, 1000000);
|
|
const auto *n_bytes = std::bit_cast<const uint8_t *>(&nanos);
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 10> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::LocalTime),
|
|
Cast(Marker::Int32),
|
|
n_bytes[3], n_bytes[2],
|
|
n_bytes[1], n_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|
|
|
|
TEST_F(BoltEncoder, LocalDateTime) {
|
|
output.clear();
|
|
std::vector<Value> vals;
|
|
const auto value =
|
|
Value(memgraph::utils::LocalDateTime(memgraph::utils::Date(1), memgraph::utils::LocalTime({0, 0, 30, 1, 0})));
|
|
const auto &local_date_time = value.ValueLocalDateTime();
|
|
vals.push_back(value);
|
|
ASSERT_EQ(bolt_encoder.MessageRecord(vals), true);
|
|
const auto secs = local_date_time.SecondsSinceEpoch();
|
|
ASSERT_EQ(secs, 30);
|
|
const auto *sec_bytes = std::bit_cast<const uint8_t *>(&secs);
|
|
const auto nanos = local_date_time.SubSecondsAsNanoseconds();
|
|
ASSERT_EQ(nanos, 1000000);
|
|
const auto *nano_bytes = std::bit_cast<const uint8_t *>(&nanos);
|
|
// 0x91 denotes the size of vals (it's 0x91 because it's anded -- see
|
|
// WriteTypeSize in base_encoder.hpp).
|
|
// The rest of the expected results follow logically from LocalTime and Date test cases
|
|
using Marker = memgraph::communication::bolt::Marker;
|
|
using Sig = memgraph::communication::bolt::Signature;
|
|
// clang-format off
|
|
const auto expected = std::array<uint8_t, 11> {
|
|
Cast(Marker::TinyStruct1),
|
|
Cast(Sig::Record),
|
|
0x91,
|
|
Cast(Marker::TinyStruct2),
|
|
Cast(Sig::LocalDateTime),
|
|
// SuperSeconds
|
|
sec_bytes[0],
|
|
// SubSeconds
|
|
Cast(Marker::Int32),
|
|
nano_bytes[3], nano_bytes[2],
|
|
nano_bytes[1], nano_bytes[0] };
|
|
// clang-format on
|
|
CheckOutput(output, expected.data(), expected.size());
|
|
}
|