diff options
| author | Dan Engelbrecht <[email protected]> | 2023-02-09 16:49:51 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-02-09 07:49:51 -0800 |
| commit | 2f872e432d4a77d1c2dd082cb97a0cbfddb3cc97 (patch) | |
| tree | d631da0746b78cad7140784de4e637bcfb4e1cac /zenserver-test/zenserver-test.cpp | |
| parent | Update README.md (diff) | |
| download | zen-2f872e432d4a77d1c2dd082cb97a0cbfddb3cc97.tar.xz zen-2f872e432d4a77d1c2dd082cb97a0cbfddb3cc97.zip | |
oplog upload/download (#214)
- Feature: Zen server endpoint `prj/{project}/oplog/{log}/chunks` to post multiple attachments in one request.
- Feature: Zen server endpoint `prj/{project}/oplog/{log}/save` to save an oplog container. Accepts `CbObject` containing a compressed oplog and attachment references organized in blocks.
- Feature: Zen server endpoint `prj/{project}/oplog/{log}/load` to request an oplog container. Responds with an `CbObject` containing a compressed oplog and attachment references organized in blocks.
- Feature: Zen server endpoint `{project}/oplog/{log}/rpc` to initiate an import to or export from an external location and other operations. Use either JSon or CbPackage as payload.
- CbObject/JSon RPC format for `import` and `export` methods:
- CbObject RPC format for `getchunks` method, returns CbPackage with the found chunks, if all chunks are found the number of attachments matches number of chunks requested.
- Feature: Zen server `{project}/oplog/{log}/{hash}` now accepts `HttpVerb::kPost` as well as `HttpVerb::kGet`.
- Feature: Zen command line tool `oplog-export` to export an oplog to an external target using the zenserver oplog export endpoint.
- Feature: Zen command line tool `oplog-import` to import an oplog from an external source using the zenserver oplog import endpoint.
Diffstat (limited to 'zenserver-test/zenserver-test.cpp')
| -rw-r--r-- | zenserver-test/zenserver-test.cpp | 546 |
1 files changed, 542 insertions, 4 deletions
diff --git a/zenserver-test/zenserver-test.cpp b/zenserver-test/zenserver-test.cpp index 5c7e150ce..d8b135f74 100644 --- a/zenserver-test/zenserver-test.cpp +++ b/zenserver-test/zenserver-test.cpp @@ -15,8 +15,10 @@ #include <zencore/refcount.h> #include <zencore/stream.h> #include <zencore/string.h> +#include <zencore/testutils.h> #include <zencore/thread.h> #include <zencore/timer.h> +#include <zencore/xxhash.h> #include <zenhttp/httpclient.h> #include <zenhttp/httpshared.h> #include <zenhttp/websocket.h> @@ -49,6 +51,7 @@ ZEN_THIRD_PARTY_INCLUDES_END #include <random> #include <span> #include <thread> +#include <typeindex> #include <unordered_map> #if ZEN_PLATFORM_WINDOWS @@ -2323,12 +2326,12 @@ public: ZenServerTestHelper(std::string_view HelperId, int ServerCount) : m_HelperId{HelperId}, m_ServerCount{ServerCount} {} ~ZenServerTestHelper() {} - void SpawnServers() + void SpawnServers(std::string_view AdditionalServerArgs = std::string_view()) { - SpawnServers([](ZenServerInstance&) {}); + SpawnServers([](ZenServerInstance&) {}, AdditionalServerArgs); } - void SpawnServers(auto&& Callback) + void SpawnServers(auto&& Callback, std::string_view AdditionalServerArgs) { ZEN_INFO("{}: spawning {} server instances", m_HelperId, m_ServerCount); @@ -2343,7 +2346,7 @@ public: Callback(*Instance); - Instance->SpawnServer(13337 + i); + Instance->SpawnServer(13337 + i, AdditionalServerArgs); } for (int i = 0; i < m_ServerCount; ++i) @@ -2530,6 +2533,541 @@ TEST_CASE("websocket.basic") IoDispatcher.Stop(); } +std::string +OidAsString(const Oid& Id) +{ + StringBuilder<25> OidStringBuilder; + Id.ToString(OidStringBuilder); + return OidStringBuilder.ToString(); +} + +CbPackage +CreateOplogPackage(const Oid& Id, const std::span<const std::pair<Oid, CompressedBuffer> >& Attachments) +{ + CbPackage Package; + CbObjectWriter Object; + Object << "key"sv << OidAsString(Id); + if (!Attachments.empty()) + { + Object.BeginArray("bulkdata"); + for (const auto& Attachment : Attachments) + { + CbAttachment Attach(Attachment.second, Attachment.second.DecodeRawHash()); + Object.BeginObject(); + Object << "id"sv << Attachment.first; + Object << "type"sv + << "Standard"sv; + Object << "data"sv << Attach; + Object.EndObject(); + + Package.AddAttachment(Attach); + ZEN_DEBUG("Added attachment {}", Attach.GetHash()); + } + Object.EndArray(); + } + Package.SetObject(Object.Save()); + return Package; +}; + +std::vector<std::pair<Oid, CompressedBuffer> > +CreateAttachments(const std::span<const size_t>& Sizes) +{ + std::vector<std::pair<Oid, CompressedBuffer> > Result; + Result.reserve(Sizes.size()); + for (size_t Size : Sizes) + { + std::vector<uint8_t> Data; + Data.resize(Size); + uint16_t* DataPtr = reinterpret_cast<uint16_t*>(Data.data()); + for (size_t Idx = 0; Idx < Size / 2; ++Idx) + { + DataPtr[Idx] = static_cast<uint16_t>(Idx % 0xffffu); + } + if (Size & 1) + { + Data[Size - 1] = static_cast<uint8_t>((Size - 1) & 0xff); + } + CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(Data.data(), Data.size())); + Result.emplace_back(std::pair<Oid, CompressedBuffer>(Oid::NewOid(), Compressed)); + } + return Result; +} + +cpr::Body +AsBody(const IoBuffer& Payload) +{ + return cpr::Body{(const char*)Payload.GetData(), Payload.Size()}; +}; + +enum CbWriterMeta +{ + BeginObject, + EndObject, + BeginArray, + EndArray +}; + +inline CbWriter& +operator<<(CbWriter& Writer, CbWriterMeta Meta) +{ + switch (Meta) + { + case BeginObject: + Writer.BeginObject(); + break; + case EndObject: + Writer.EndObject(); + break; + case BeginArray: + Writer.BeginArray(); + break; + case EndArray: + Writer.EndArray(); + break; + default: + ZEN_ASSERT(false); + } + return Writer; +} + +TEST_CASE("project.remote") +{ + using namespace std::literals; + + ZenServerTestHelper Servers("remote", 3); + Servers.SpawnServers("--debug"); + + std::vector<Oid> OpIds; + OpIds.reserve(24); + for (size_t I = 0; I < 24; ++I) + { + OpIds.emplace_back(Oid::NewOid()); + } + + std::unordered_map<Oid, std::vector<std::pair<Oid, CompressedBuffer> >, Oid::Hasher> Attachments; + { + std::vector<std::size_t> AttachmentSizes({7633, 6825, 5738, 8031, 7225, 566, 3656, 6006, 24, 3466, 1093, 4269, + 2257, 3685, 3489, 7194, 6151, 5482, 6217, 3511, 6738, 5061, 7537, 2759, + 1916, 8210, 2235, 4024, 1582, 5251, 491, 5464, 4607, 8135, 3767, 4045, + 4415, 5007, 8876, 6761, 3359, 8526, 4097, 4855, 8225}); + auto It = AttachmentSizes.begin(); + Attachments[OpIds[0]] = {}; + Attachments[OpIds[1]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[2]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++, *It++}); + Attachments[OpIds[3]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[4]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++}); + Attachments[OpIds[5]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++, *It++}); + Attachments[OpIds[6]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[7]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++, *It++}); + Attachments[OpIds[8]] = CreateAttachments(std::initializer_list<size_t>{}); + Attachments[OpIds[9]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++, *It++}); + Attachments[OpIds[10]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[11]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++}); + Attachments[OpIds[12]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++, *It++}); + Attachments[OpIds[13]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[14]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++}); + Attachments[OpIds[15]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++}); + Attachments[OpIds[16]] = CreateAttachments(std::initializer_list<size_t>{}); + Attachments[OpIds[17]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++}); + Attachments[OpIds[18]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++}); + Attachments[OpIds[19]] = CreateAttachments(std::initializer_list<size_t>{}); + Attachments[OpIds[20]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[21]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + Attachments[OpIds[22]] = CreateAttachments(std::initializer_list<size_t>{*It++, *It++, *It++}); + Attachments[OpIds[23]] = CreateAttachments(std::initializer_list<size_t>{*It++}); + ZEN_ASSERT(It == AttachmentSizes.end()); + } + + auto AddOp = [](const CbObject& Op, std::unordered_map<Oid, uint32_t, Oid::Hasher>& Ops) { + XXH3_128Stream KeyHasher; + Op["key"sv].WriteToStream([&](const void* Data, size_t Size) { KeyHasher.Append(Data, Size); }); + XXH3_128 KeyHash = KeyHasher.GetHash(); + Oid Id; + memcpy(Id.OidBits, &KeyHash, sizeof Id.OidBits); + IoBuffer Buffer = Op.GetBuffer().AsIoBuffer(); + const uint32_t OpCoreHash = uint32_t(XXH3_64bits(Buffer.GetData(), Buffer.GetSize()) & 0xffffFFFF); + Ops.insert({Id, OpCoreHash}); + }; + + auto MakeProject = [](cpr::Session& Session, std::string_view UrlBase, std::string_view ProjectName) { + CbObjectWriter Project; + Project.AddString("id"sv, ProjectName); + Project.AddString("root"sv, ""sv); + Project.AddString("engine"sv, ""sv); + Project.AddString("project"sv, ""sv); + Project.AddString("projectfile"sv, ""sv); + IoBuffer ProjectPayload = Project.Save().GetBuffer().AsIoBuffer(); + std::string ProjectRequest = fmt::format("{}/prj/{}", UrlBase, ProjectName); + Session.SetUrl({ProjectRequest}); + Session.SetBody(cpr::Body{(const char*)ProjectPayload.GetData(), ProjectPayload.GetSize()}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + }; + + auto MakeOplog = [](cpr::Session& Session, std::string_view UrlBase, std::string_view ProjectName, std::string_view OplogName) { + std::string CreateOplogRequest = fmt::format("{}/prj/{}/oplog/{}", UrlBase, ProjectName, OplogName); + Session.SetUrl({CreateOplogRequest}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + }; + + auto MakeOp = [](cpr::Session& Session, + std::string_view UrlBase, + std::string_view ProjectName, + std::string_view OplogName, + const CbPackage& OpPackage) { + std::string CreateOpRequest = fmt::format("{}/prj/{}/oplog/{}/new", UrlBase, ProjectName, OplogName); + Session.SetUrl({CreateOpRequest}); + zen::BinaryWriter MemOut; + legacy::SaveCbPackage(OpPackage, MemOut); + Session.SetBody(cpr::Body{(const char*)MemOut.Data(), MemOut.Size()}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + }; + + cpr::Session Session; + MakeProject(Session, Servers.GetInstance(0).GetBaseUri(), "proj0"); + MakeOplog(Session, Servers.GetInstance(0).GetBaseUri(), "proj0", "oplog0"); + + std::unordered_map<Oid, uint32_t, Oid::Hasher> SourceOps; + for (const Oid& OpId : OpIds) + { + CbPackage OpPackage = CreateOplogPackage(OpId, Attachments[OpId]); + CHECK(OpPackage.GetAttachments().size() == Attachments[OpId].size()); + AddOp(OpPackage.GetObject(), SourceOps); + MakeOp(Session, Servers.GetInstance(0).GetBaseUri(), "proj0", "oplog0", OpPackage); + } + + std::vector<IoHash> AttachmentHashes; + AttachmentHashes.reserve(Attachments.size()); + for (const auto& AttachmentOplog : Attachments) + { + for (const auto& Attachment : AttachmentOplog.second) + { + AttachmentHashes.emplace_back(Attachment.second.DecodeRawHash()); + } + } + + auto MakeCbObjectPayload = [](std::function<void(CbObjectWriter & Writer)> Write) -> IoBuffer { + CbObjectWriter Writer; + Write(Writer); + IoBuffer Result = Writer.Save().GetBuffer().AsIoBuffer(); + Result.MakeOwned(); + return Result; + }; + + auto ValidateAttachments = [&MakeCbObjectPayload, &AttachmentHashes, &Servers, &Session](int ServerIndex, + std::string_view Project, + std::string_view Oplog) { + std::string GetChunksRequest = fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(ServerIndex).GetBaseUri(), Project, Oplog); + Session.SetUrl({GetChunksRequest}); + IoBuffer Payload = MakeCbObjectPayload([&AttachmentHashes](CbObjectWriter& Writer) { + Writer << "method"sv + << "getchunks"sv; + Writer << "chunks"sv << BeginArray; + for (const IoHash& Chunk : AttachmentHashes) + { + Writer << Chunk; + } + Writer << EndArray; // chunks + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}, {"Accept", "application/x-ue-cbpkg"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + CbPackage ResponsePackage = ParsePackageMessage(IoBuffer(IoBuffer::Wrap, Response.text.data(), Response.text.size())); + CHECK(ResponsePackage.GetAttachments().size() == AttachmentHashes.size()); + }; + + auto ValidateOplog = [&SourceOps, &AddOp, &Servers, &Session](int ServerIndex, std::string_view Project, std::string_view Oplog) { + std::unordered_map<Oid, uint32_t, Oid::Hasher> TargetOps; + std::vector<CbObject> ResultingOplog; + + std::string GetOpsRequest = + fmt::format("{}/prj/{}/oplog/{}/entries", Servers.GetInstance(ServerIndex).GetBaseUri(), Project, Oplog); + Session.SetUrl({GetOpsRequest}); + cpr::Response Response = Session.Get(); + CHECK(IsHttpSuccessCode(Response.status_code)); + + IoBuffer Payload(IoBuffer::Wrap, Response.text.data(), Response.text.size()); + CbObject OplogResonse = LoadCompactBinaryObject(Payload); + CbArrayView EntriesArray = OplogResonse["entries"sv].AsArrayView(); + + for (CbFieldView OpEntry : EntriesArray) + { + CbObjectView Core = OpEntry.AsObjectView(); + BinaryWriter Writer; + Core.CopyTo(Writer); + MemoryView OpView = Writer.GetView(); + IoBuffer OpBuffer(IoBuffer::Wrap, OpView.GetData(), OpView.GetSize()); + CbObject Op(SharedBuffer(OpBuffer), CbFieldType::HasFieldType); + AddOp(Op, TargetOps); + } + CHECK(SourceOps == TargetOps); + }; + + SUBCASE("File") + { + ScopedTemporaryDirectory TempDir; + { + std::string SaveOplogRequest = fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(0).GetBaseUri(), "proj0", "oplog0"); + Session.SetUrl({SaveOplogRequest}); + + IoBuffer Payload = MakeCbObjectPayload([&AttachmentHashes, path = TempDir.Path().string()](CbObjectWriter& Writer) { + Writer << "method"sv + << "export"sv; + Writer << "params" << BeginObject; + { + Writer << "maxblocksize"sv << 3072u; + Writer << "maxchunkembedsize"sv << 1296u; + Writer << "force"sv << false; + Writer << "file"sv << BeginObject; + { + Writer << "path"sv << path; + Writer << "name"sv + << "proj0_oplog0"sv; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + { + MakeProject(Session, Servers.GetInstance(1).GetBaseUri(), "proj0_copy"); + MakeOplog(Session, Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy"); + std::string LoadOplogRequest = + fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy"); + Session.SetUrl({LoadOplogRequest}); + + IoBuffer Payload = MakeCbObjectPayload([&AttachmentHashes, path = TempDir.Path().string()](CbObjectWriter& Writer) { + Writer << "method"sv + << "import"sv; + Writer << "params" << BeginObject; + { + Writer << "force"sv << false; + Writer << "file"sv << BeginObject; + { + Writer << "path"sv << path; + Writer << "name"sv + << "proj0_oplog0"sv; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + ValidateAttachments(1, "proj0_copy", "oplog0_copy"); + ValidateOplog(1, "proj0_copy", "oplog0_copy"); + } + + SUBCASE("File disable blocks") + { + ScopedTemporaryDirectory TempDir; + { + std::string SaveOplogRequest = fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(0).GetBaseUri(), "proj0", "oplog0"); + Session.SetUrl({SaveOplogRequest}); + + IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { + Writer << "method"sv + << "export"sv; + Writer << "params" << BeginObject; + { + Writer << "maxblocksize"sv << 3072u; + Writer << "maxchunkembedsize"sv << 1296u; + Writer << "force"sv << false; + Writer << "file"sv << BeginObject; + { + Writer << "path"sv << TempDir.Path().string(); + Writer << "name"sv + << "proj0_oplog0"sv; + Writer << "disableblocks"sv << true; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + { + MakeProject(Session, Servers.GetInstance(1).GetBaseUri(), "proj0_copy"); + MakeOplog(Session, Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy"); + std::string LoadOplogRequest = + fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy"); + Session.SetUrl({LoadOplogRequest}); + IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { + Writer << "method"sv + << "import"sv; + Writer << "params" << BeginObject; + { + Writer << "force"sv << false; + Writer << "file"sv << BeginObject; + { + Writer << "path"sv << TempDir.Path().string(); + Writer << "name"sv + << "proj0_oplog0"sv; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + ValidateAttachments(1, "proj0_copy", "oplog0_copy"); + ValidateOplog(1, "proj0_copy", "oplog0_copy"); + } + + SUBCASE("File force temp blocks") + { + ScopedTemporaryDirectory TempDir; + { + std::string SaveOplogRequest = fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(0).GetBaseUri(), "proj0", "oplog0"); + Session.SetUrl({SaveOplogRequest}); + IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { + Writer << "method"sv + << "export"sv; + Writer << "params" << BeginObject; + { + Writer << "maxblocksize"sv << 3072u; + Writer << "maxchunkembedsize"sv << 1296u; + Writer << "force"sv << false; + Writer << "file"sv << BeginObject; + { + Writer << "path"sv << TempDir.Path().string(); + Writer << "name"sv + << "proj0_oplog0"sv; + Writer << "enabletempblocks"sv << true; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + { + MakeProject(Session, Servers.GetInstance(1).GetBaseUri(), "proj0_copy"); + MakeOplog(Session, Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy"); + std::string LoadOplogRequest = + fmt::format("{}/prj/{}/oplog/{}/rpc", Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy"); + Session.SetUrl({LoadOplogRequest}); + IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { + Writer << "method"sv + << "import"sv; + Writer << "params" << BeginObject; + { + Writer << "force"sv << false; + Writer << "file"sv << BeginObject; + { + Writer << "path"sv << TempDir.Path().string(); + Writer << "name"sv + << "proj0_oplog0"sv; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + ValidateAttachments(1, "proj0_copy", "oplog0_copy"); + ValidateOplog(1, "proj0_copy", "oplog0_copy"); + } + + SUBCASE("Zen") + { + ScopedTemporaryDirectory TempDir; + { + std::string ExportSourceUri = Servers.GetInstance(0).GetBaseUri(); + std::string ExportTargetUri = Servers.GetInstance(1).GetBaseUri(); + MakeProject(Session, ExportTargetUri, "proj0_copy"); + MakeOplog(Session, ExportTargetUri, "proj0_copy", "oplog0_copy"); + + std::string SaveOplogRequest = fmt::format("{}/prj/{}/oplog/{}/rpc", ExportSourceUri, "proj0", "oplog0"); + Session.SetUrl({SaveOplogRequest}); + + IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { + Writer << "method"sv + << "export"sv; + Writer << "params" << BeginObject; + { + Writer << "maxblocksize"sv << 3072u; + Writer << "maxchunkembedsize"sv << 1296u; + Writer << "force"sv << false; + Writer << "zen"sv << BeginObject; + { + Writer << "url"sv << ExportTargetUri.substr(7); + Writer << "project" + << "proj0_copy"; + Writer << "oplog" + << "oplog0_copy"; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + ValidateAttachments(1, "proj0_copy", "oplog0_copy"); + ValidateOplog(1, "proj0_copy", "oplog0_copy"); + + { + std::string ImportSourceUri = Servers.GetInstance(1).GetBaseUri(); + std::string ImportTargetUri = Servers.GetInstance(2).GetBaseUri(); + MakeProject(Session, ImportTargetUri, "proj1"); + MakeOplog(Session, ImportTargetUri, "proj1", "oplog1"); + std::string LoadOplogRequest = fmt::format("{}/prj/{}/oplog/{}/rpc", ImportTargetUri, "proj1", "oplog1"); + Session.SetUrl({LoadOplogRequest}); + + IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { + Writer << "method"sv + << "import"sv; + Writer << "params" << BeginObject; + { + Writer << "force"sv << false; + Writer << "zen"sv << BeginObject; + { + Writer << "url"sv << ImportSourceUri.substr(7); + Writer << "project" + << "proj0_copy"; + Writer << "oplog" + << "oplog0_copy"; + } + Writer << EndObject; // "file" + } + Writer << EndObject; // "params" + }); + Session.SetBody(AsBody(Payload)); + Session.SetHeader(cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + cpr::Response Response = Session.Post(); + CHECK(IsHttpSuccessCode(Response.status_code)); + } + ValidateAttachments(2, "proj1", "oplog1"); + ValidateOplog(2, "proj1", "oplog1"); + } +} + # if 0 TEST_CASE("lifetime.owner") { |