aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver-test/projectstore-tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver-test/projectstore-tests.cpp')
-rw-r--r--src/zenserver-test/projectstore-tests.cpp1090
1 files changed, 530 insertions, 560 deletions
diff --git a/src/zenserver-test/projectstore-tests.cpp b/src/zenserver-test/projectstore-tests.cpp
index cec453511..49d985abb 100644
--- a/src/zenserver-test/projectstore-tests.cpp
+++ b/src/zenserver-test/projectstore-tests.cpp
@@ -41,423 +41,430 @@ TEST_CASE("project.basic")
const uint16_t PortNumber = Instance1.SpawnServerAndWaitUntilReady();
- std::mt19937_64 mt;
-
- zen::StringBuilder<64> BaseUri;
- BaseUri << fmt::format("http://localhost:{}", PortNumber);
+ std::string ServerUri = fmt::format("http://localhost:{}", PortNumber);
std::filesystem::path BinPath = zen::GetRunningExecutablePath();
std::filesystem::path RootPath = BinPath.parent_path().parent_path();
BinPath = BinPath.lexically_relative(RootPath);
- SUBCASE("build store init")
+ auto CreateProjectAndOplog = [&](std::string_view ProjectName, std::string_view OplogName) -> std::string {
+ HttpClient Http{ServerUri};
+
+ zen::CbObjectWriter Body;
+ Body << "id" << ProjectName;
+ Body << "root" << RootPath.c_str();
+ Body << "project"
+ << "/zooom";
+ Body << "engine"
+ << "/zooom";
+ IoBuffer BodyBuf = Body.Save().GetBuffer().AsIoBuffer();
+ auto Response = Http.Post(fmt::format("/prj/{}", ProjectName), BodyBuf);
+ REQUIRE(Response.StatusCode == HttpResponseCode::Created);
+
+ std::string OplogUri = fmt::format("{}/prj/{}/oplog/{}", ServerUri, ProjectName, OplogName);
+ HttpClient OplogHttp{OplogUri};
+ auto OplogResponse = OplogHttp.Post(""sv, IoBuffer{}, ZenContentType::kCbObject);
+ REQUIRE(OplogResponse.StatusCode == HttpResponseCode::Created);
+
+ return OplogUri;
+ };
+
+ // Create a file at a path exceeding Windows MAX_PATH (260 chars) for long filename testing
+ std::filesystem::path LongPathDir = RootPath / "longpathtest";
+ for (int I = 0; I < 5; ++I)
{
- {
- HttpClient Http{BaseUri};
+ LongPathDir /= std::string(50, char('a' + I));
+ }
+ std::filesystem::path LongFilePath = LongPathDir / "testfile.bin";
+ std::filesystem::path LongRelPath = LongFilePath.lexically_relative(RootPath);
- {
- zen::CbObjectWriter Body;
- Body << "id"
- << "test";
- Body << "root" << RootPath.c_str();
- Body << "project"
- << "/zooom";
- Body << "engine"
- << "/zooom";
-
- zen::BinaryWriter MemOut;
- IoBuffer BodyBuf = Body.Save().GetBuffer().AsIoBuffer();
-
- auto Response = Http.Post("/prj/test"sv, BodyBuf);
- CHECK(Response.StatusCode == HttpResponseCode::Created);
- }
+ const uint8_t LongPathFileData[] = {0xDE, 0xAD, 0xBE, 0xEF};
+ CreateDirectories(MakeSafeAbsolutePath(LongPathDir));
+ WriteFile(MakeSafeAbsolutePath(LongFilePath), IoBufferBuilder::MakeCloneFromMemory(LongPathFileData, sizeof(LongPathFileData)));
+ CHECK(LongRelPath.string().length() > 260);
- {
- auto Response = Http.Get("/prj/test"sv);
- REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+ std::string LongClientPath = "/{engine}/client";
+ for (int I = 0; I < 5; ++I)
+ {
+ LongClientPath += '/';
+ LongClientPath.append(50, char('a' + I));
+ }
+ LongClientPath += "/longfile.bin";
+ CHECK(LongClientPath.length() > 260);
- CbObject ResponseObject = Response.AsObject();
+ const std::string_view LongPathChunkId{
+ "00000000"
+ "00000000"
+ "00020000"};
+ auto LongPathFileOid = zen::Oid::FromHexString(LongPathChunkId);
- CHECK(ResponseObject["id"].AsString() == "test"sv);
- CHECK(ResponseObject["root"].AsString() == PathToUtf8(RootPath.c_str()));
- }
+ // --- build store persistence ---
+ // First section also verifies project and oplog creation responses.
+ {
+ HttpClient ServerHttp{ServerUri};
+
+ {
+ zen::CbObjectWriter Body;
+ Body << "id"
+ << "test_persist";
+ Body << "root" << RootPath.c_str();
+ Body << "project"
+ << "/zooom";
+ Body << "engine"
+ << "/zooom";
+ IoBuffer BodyBuf = Body.Save().GetBuffer().AsIoBuffer();
+
+ auto Response = ServerHttp.Post("/prj/test_persist"sv, BodyBuf);
+ CHECK(Response.StatusCode == HttpResponseCode::Created);
}
- BaseUri << "/prj/test/oplog/foobar";
+ {
+ auto Response = ServerHttp.Get("/prj/test_persist"sv);
+ REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+
+ CbObject ResponseObject = Response.AsObject();
+
+ CHECK(ResponseObject["id"].AsString() == "test_persist"sv);
+ CHECK(ResponseObject["root"].AsString() == PathToUtf8(RootPath.c_str()));
+ }
+
+ std::string OplogUri = fmt::format("{}/prj/test_persist/oplog/oplog_persist", ServerUri);
{
- HttpClient Http{BaseUri};
+ HttpClient OplogHttp{OplogUri};
{
- auto Response = Http.Post(""sv, IoBuffer{}, ZenContentType::kCbObject);
+ auto Response = OplogHttp.Post(""sv, IoBuffer{}, ZenContentType::kCbObject);
CHECK(Response.StatusCode == HttpResponseCode::Created);
}
{
- auto Response = Http.Get(""sv);
+ auto Response = OplogHttp.Get(""sv);
REQUIRE(Response.StatusCode == HttpResponseCode::OK);
CbObject ResponseObject = Response.AsObject();
- CHECK(ResponseObject["id"].AsString() == "foobar"sv);
- CHECK(ResponseObject["project"].AsString() == "test"sv);
+ CHECK(ResponseObject["id"].AsString() == "oplog_persist"sv);
+ CHECK(ResponseObject["project"].AsString() == "test_persist"sv);
}
}
- // Create a file at a path exceeding Windows MAX_PATH (260 chars) for long filename testing
- std::filesystem::path LongPathDir = RootPath / "longpathtest";
- for (int I = 0; I < 5; ++I)
- {
- LongPathDir /= std::string(50, char('a' + I));
- }
- std::filesystem::path LongFilePath = LongPathDir / "testfile.bin";
- std::filesystem::path LongRelPath = LongFilePath.lexically_relative(RootPath);
+ uint8_t AttachData[] = {1, 2, 3};
- const uint8_t LongPathFileData[] = {0xDE, 0xAD, 0xBE, 0xEF};
- CreateDirectories(MakeSafeAbsolutePath(LongPathDir));
- WriteFile(MakeSafeAbsolutePath(LongFilePath), IoBufferBuilder::MakeCloneFromMemory(LongPathFileData, sizeof(LongPathFileData)));
- CHECK(LongRelPath.string().length() > 260);
+ zen::CompressedBuffer Attachment = zen::CompressedBuffer::Compress(zen::SharedBuffer::Clone(zen::MemoryView{AttachData, 3}));
+ zen::CbAttachment Attach{Attachment, Attachment.DecodeRawHash()};
- std::string LongClientPath = "/{engine}/client";
- for (int I = 0; I < 5; ++I)
- {
- LongClientPath += '/';
- LongClientPath.append(50, char('a' + I));
- }
- LongClientPath += "/longfile.bin";
- CHECK(LongClientPath.length() > 260);
+ zen::CbObjectWriter OpWriter;
+ OpWriter << "key"
+ << "foo"
+ << "attachment" << Attach;
- const std::string_view LongPathChunkId{
+ const std::string_view ChunkId{
"00000000"
"00000000"
- "00020000"};
- auto LongPathFileOid = zen::Oid::FromHexString(LongPathChunkId);
+ "00010000"};
+ auto FileOid = zen::Oid::FromHexString(ChunkId);
+
+ OpWriter.BeginArray("files");
+ OpWriter.BeginObject();
+ OpWriter << "id" << FileOid;
+ OpWriter << "clientpath"
+ << "/{engine}/client/side/path";
+ OpWriter << "serverpath" << BinPath.c_str();
+ OpWriter.EndObject();
+ OpWriter.BeginObject();
+ OpWriter << "id" << LongPathFileOid;
+ OpWriter << "clientpath" << LongClientPath;
+ OpWriter << "serverpath" << LongRelPath.c_str();
+ OpWriter.EndObject();
+ OpWriter.EndArray();
+
+ zen::CbObject Op = OpWriter.Save();
+
+ zen::CbPackage OpPackage(Op);
+ OpPackage.AddAttachment(Attach);
- SUBCASE("build store persistence")
- {
- uint8_t AttachData[] = {1, 2, 3};
-
- zen::CompressedBuffer Attachment = zen::CompressedBuffer::Compress(zen::SharedBuffer::Clone(zen::MemoryView{AttachData, 3}));
- zen::CbAttachment Attach{Attachment, Attachment.DecodeRawHash()};
-
- zen::CbObjectWriter OpWriter;
- OpWriter << "key"
- << "foo"
- << "attachment" << Attach;
-
- const std::string_view ChunkId{
- "00000000"
- "00000000"
- "00010000"};
- auto FileOid = zen::Oid::FromHexString(ChunkId);
-
- OpWriter.BeginArray("files");
- OpWriter.BeginObject();
- OpWriter << "id" << FileOid;
- OpWriter << "clientpath"
- << "/{engine}/client/side/path";
- OpWriter << "serverpath" << BinPath.c_str();
- OpWriter.EndObject();
- OpWriter.BeginObject();
- OpWriter << "id" << LongPathFileOid;
- OpWriter << "clientpath" << LongClientPath;
- OpWriter << "serverpath" << LongRelPath.c_str();
- OpWriter.EndObject();
- OpWriter.EndArray();
-
- zen::CbObject Op = OpWriter.Save();
-
- zen::CbPackage OpPackage(Op);
- OpPackage.AddAttachment(Attach);
-
- zen::BinaryWriter MemOut;
- legacy::SaveCbPackage(OpPackage, MemOut);
-
- HttpClient Http{BaseUri};
-
- {
- auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView()));
+ zen::BinaryWriter MemOut;
+ legacy::SaveCbPackage(OpPackage, MemOut);
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::Created);
- }
+ HttpClient Http{OplogUri};
- // Read file data
+ {
+ auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView()));
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << ChunkId;
- auto Response = Http.Get(ChunkGetUri);
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::Created);
+ }
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::OK);
- }
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << ChunkId;
+ auto Response = Http.Get(ChunkGetUri);
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << ChunkId << "?offset=1&size=10";
- auto Response = Http.Get(ChunkGetUri);
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::OK);
+ }
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::OK);
- CHECK(Response.ResponsePayload.GetSize() == 10);
- }
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << ChunkId << "?offset=1&size=10";
+ auto Response = Http.Get(ChunkGetUri);
- // Read long-path file data
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << LongPathChunkId;
- auto Response = Http.Get(ChunkGetUri);
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::OK);
+ CHECK(Response.ResponsePayload.GetSize() == 10);
+ }
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::OK);
- CHECK(Response.ResponsePayload.GetSize() == sizeof(LongPathFileData));
- }
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << LongPathChunkId;
+ auto Response = Http.Get(ChunkGetUri);
- ZEN_INFO("+++++++");
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::OK);
+ CHECK(Response.ResponsePayload.GetSize() == sizeof(LongPathFileData));
}
- SUBCASE("snapshot")
- {
- zen::CbObjectWriter OpWriter;
- OpWriter << "key"
- << "foo";
-
- const std::string_view ChunkId{
- "00000000"
- "00000000"
- "00010000"};
- auto FileOid = zen::Oid::FromHexString(ChunkId);
-
- OpWriter.BeginArray("files");
- OpWriter.BeginObject();
- OpWriter << "id" << FileOid;
- OpWriter << "clientpath"
- << "/{engine}/client/side/path";
- OpWriter << "serverpath" << BinPath.c_str();
- OpWriter.EndObject();
- OpWriter.BeginObject();
- OpWriter << "id" << LongPathFileOid;
- OpWriter << "clientpath" << LongClientPath;
- OpWriter << "serverpath" << LongRelPath.c_str();
- OpWriter.EndObject();
- OpWriter.EndArray();
-
- zen::CbObject Op = OpWriter.Save();
-
- zen::CbPackage OpPackage(Op);
-
- zen::BinaryWriter MemOut;
- legacy::SaveCbPackage(OpPackage, MemOut);
-
- HttpClient Http{BaseUri};
+ ZEN_INFO("+++++++");
+ }
- {
- auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView()));
+ // --- snapshot ---
+ {
+ std::string OplogUri = CreateProjectAndOplog("test_snap", "oplog_snap");
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::Created);
- }
+ zen::CbObjectWriter OpWriter;
+ OpWriter << "key"
+ << "foo";
- // Read file data, it is raw and uncompressed
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << ChunkId;
- auto Response = Http.Get(ChunkGetUri);
+ const std::string_view ChunkId{
+ "00000000"
+ "00000000"
+ "00010000"};
+ auto FileOid = zen::Oid::FromHexString(ChunkId);
+
+ OpWriter.BeginArray("files");
+ OpWriter.BeginObject();
+ OpWriter << "id" << FileOid;
+ OpWriter << "clientpath"
+ << "/{engine}/client/side/path";
+ OpWriter << "serverpath" << BinPath.c_str();
+ OpWriter.EndObject();
+ OpWriter.BeginObject();
+ OpWriter << "id" << LongPathFileOid;
+ OpWriter << "clientpath" << LongClientPath;
+ OpWriter << "serverpath" << LongRelPath.c_str();
+ OpWriter.EndObject();
+ OpWriter.EndArray();
+
+ zen::CbObject Op = OpWriter.Save();
+
+ zen::CbPackage OpPackage(Op);
- REQUIRE(Response);
- REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+ zen::BinaryWriter MemOut;
+ legacy::SaveCbPackage(OpPackage, MemOut);
- IoBuffer Data = Response.ResponsePayload;
- IoBuffer ReferenceData = IoBufferBuilder::MakeFromFile(RootPath / BinPath);
- CHECK(ReferenceData.GetSize() == Data.GetSize());
- CHECK(ReferenceData.GetView().EqualBytes(Data.GetView()));
- }
+ HttpClient Http{OplogUri};
- // Read long-path file data, it is raw and uncompressed
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << LongPathChunkId;
- auto Response = Http.Get(ChunkGetUri);
+ {
+ auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView()));
- REQUIRE(Response);
- REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::Created);
+ }
- IoBuffer Data = Response.ResponsePayload;
- MemoryView ExpectedView{LongPathFileData, sizeof(LongPathFileData)};
- CHECK(Data.GetSize() == sizeof(LongPathFileData));
- CHECK(Data.GetView().EqualBytes(ExpectedView));
- }
+ // Read file data, it is raw and uncompressed
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << ChunkId;
+ auto Response = Http.Get(ChunkGetUri);
- {
- IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("method"sv, "snapshot"sv); });
- auto Response = Http.Post("/rpc"sv, Payload);
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::OK);
- }
+ REQUIRE(Response);
+ REQUIRE(Response.StatusCode == HttpResponseCode::OK);
- // Read chunk data, it is now compressed
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << ChunkId;
- auto Response = Http.Get(ChunkGetUri, {{"Accept-Type", "application/x-ue-comp"}});
+ IoBuffer Data = Response.ResponsePayload;
+ IoBuffer ReferenceData = IoBufferBuilder::MakeFromFile(RootPath / BinPath);
+ CHECK(ReferenceData.GetSize() == Data.GetSize());
+ CHECK(ReferenceData.GetView().EqualBytes(Data.GetView()));
+ }
- REQUIRE(Response);
- REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+ // Read long-path file data, it is raw and uncompressed
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << LongPathChunkId;
+ auto Response = Http.Get(ChunkGetUri);
- IoBuffer Data = Response.ResponsePayload;
- IoHash RawHash;
- uint64_t RawSize;
- CompressedBuffer Compressed = CompressedBuffer::FromCompressed(SharedBuffer(Data), RawHash, RawSize);
- REQUIRE(Compressed);
- IoBuffer DataDecompressed = Compressed.Decompress().AsIoBuffer();
- IoBuffer ReferenceData = IoBufferBuilder::MakeFromFile(RootPath / BinPath);
- CHECK(RawSize == ReferenceData.GetSize());
- CHECK(ReferenceData.GetSize() == DataDecompressed.GetSize());
- CHECK(ReferenceData.GetView().EqualBytes(DataDecompressed.GetView()));
- }
+ REQUIRE(Response);
+ REQUIRE(Response.StatusCode == HttpResponseCode::OK);
- // Read compressed long-path file data after snapshot
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << LongPathChunkId;
- auto Response = Http.Get(ChunkGetUri, {{"Accept-Type", "application/x-ue-comp"}});
+ IoBuffer Data = Response.ResponsePayload;
+ MemoryView ExpectedView{LongPathFileData, sizeof(LongPathFileData)};
+ CHECK(Data.GetSize() == sizeof(LongPathFileData));
+ CHECK(Data.GetView().EqualBytes(ExpectedView));
+ }
- REQUIRE(Response);
- REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+ {
+ IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("method"sv, "snapshot"sv); });
+ auto Response = Http.Post("/rpc"sv, Payload);
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::OK);
+ }
- IoBuffer Data = Response.ResponsePayload;
- IoHash RawHash;
- uint64_t RawSize;
- CompressedBuffer Compressed = CompressedBuffer::FromCompressed(SharedBuffer(Data), RawHash, RawSize);
- REQUIRE(Compressed);
- IoBuffer DataDecompressed = Compressed.Decompress().AsIoBuffer();
- MemoryView ExpectedView{LongPathFileData, sizeof(LongPathFileData)};
- CHECK(RawSize == sizeof(LongPathFileData));
- CHECK(DataDecompressed.GetSize() == sizeof(LongPathFileData));
- CHECK(DataDecompressed.GetView().EqualBytes(ExpectedView));
- }
+ // Read chunk data, it is now compressed
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << ChunkId;
+ auto Response = Http.Get(ChunkGetUri, {{"Accept-Type", "application/x-ue-comp"}});
+
+ REQUIRE(Response);
+ REQUIRE(Response.StatusCode == HttpResponseCode::OK);
- ZEN_INFO("+++++++");
+ IoBuffer Data = Response.ResponsePayload;
+ IoHash RawHash;
+ uint64_t RawSize;
+ CompressedBuffer Compressed = CompressedBuffer::FromCompressed(SharedBuffer(Data), RawHash, RawSize);
+ REQUIRE(Compressed);
+ IoBuffer DataDecompressed = Compressed.Decompress().AsIoBuffer();
+ IoBuffer ReferenceData = IoBufferBuilder::MakeFromFile(RootPath / BinPath);
+ CHECK(RawSize == ReferenceData.GetSize());
+ CHECK(ReferenceData.GetSize() == DataDecompressed.GetSize());
+ CHECK(ReferenceData.GetView().EqualBytes(DataDecompressed.GetView()));
}
- SUBCASE("snapshot zero byte file")
+ // Read compressed long-path file data after snapshot
{
- // A zero-byte file referenced in an oplog entry must survive a
- // snapshot: the file is read, compressed, stored in CidStore, and
- // the oplog is rewritten with a BinaryAttachment reference. After
- // the snapshot the chunk must be retrievable and decompress to an
- // empty payload.
-
- std::filesystem::path EmptyFileRelPath = std::filesystem::path("zerobyte_snapshot_test") / "empty.bin";
- std::filesystem::path EmptyFileAbsPath = RootPath / EmptyFileRelPath;
- CreateDirectories(MakeSafeAbsolutePath(EmptyFileAbsPath.parent_path()));
- // Create a zero-byte file on disk.
- WriteFile(MakeSafeAbsolutePath(EmptyFileAbsPath), IoBuffer{});
- REQUIRE(IsFile(MakeSafeAbsolutePath(EmptyFileAbsPath)));
-
- const std::string_view EmptyChunkId{
- "00000000"
- "00000000"
- "00030000"};
- auto EmptyFileOid = zen::Oid::FromHexString(EmptyChunkId);
-
- zen::CbObjectWriter OpWriter;
- OpWriter << "key"
- << "zero_byte_test";
- OpWriter.BeginArray("files");
- OpWriter.BeginObject();
- OpWriter << "id" << EmptyFileOid;
- OpWriter << "clientpath"
- << "/{engine}/empty_file";
- OpWriter << "serverpath" << EmptyFileRelPath.c_str();
- OpWriter.EndObject();
- OpWriter.EndArray();
-
- zen::CbObject Op = OpWriter.Save();
- zen::CbPackage OpPackage(Op);
-
- zen::BinaryWriter MemOut;
- legacy::SaveCbPackage(OpPackage, MemOut);
-
- HttpClient Http{BaseUri};
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << LongPathChunkId;
+ auto Response = Http.Get(ChunkGetUri, {{"Accept-Type", "application/x-ue-comp"}});
- {
- auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView()));
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::Created);
- }
+ REQUIRE(Response);
+ REQUIRE(Response.StatusCode == HttpResponseCode::OK);
- // Read file data before snapshot - raw and uncompressed, 0 bytes.
- // http.sys converts a 200 OK with empty body to 204 No Content, so
- // accept either status code.
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << EmptyChunkId;
- auto Response = Http.Get(ChunkGetUri);
+ IoBuffer Data = Response.ResponsePayload;
+ IoHash RawHash;
+ uint64_t RawSize;
+ CompressedBuffer Compressed = CompressedBuffer::FromCompressed(SharedBuffer(Data), RawHash, RawSize);
+ REQUIRE(Compressed);
+ IoBuffer DataDecompressed = Compressed.Decompress().AsIoBuffer();
+ MemoryView ExpectedView{LongPathFileData, sizeof(LongPathFileData)};
+ CHECK(RawSize == sizeof(LongPathFileData));
+ CHECK(DataDecompressed.GetSize() == sizeof(LongPathFileData));
+ CHECK(DataDecompressed.GetView().EqualBytes(ExpectedView));
+ }
- REQUIRE(Response);
- CHECK((Response.StatusCode == HttpResponseCode::OK || Response.StatusCode == HttpResponseCode::NoContent));
- CHECK(Response.ResponsePayload.GetSize() == 0);
- }
+ ZEN_INFO("+++++++");
+ }
- // Trigger snapshot.
- {
- IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("method"sv, "snapshot"sv); });
- auto Response = Http.Post("/rpc"sv, Payload);
- REQUIRE(Response);
- CHECK(Response.StatusCode == HttpResponseCode::OK);
- }
+ // --- snapshot zero byte file ---
+ {
+ std::string OplogUri = CreateProjectAndOplog("test_zero", "oplog_zero");
- // Read chunk after snapshot - compressed, decompresses to 0 bytes.
- {
- zen::StringBuilder<128> ChunkGetUri;
- ChunkGetUri << "/" << EmptyChunkId;
- auto Response = Http.Get(ChunkGetUri, {{"Accept-Type", "application/x-ue-comp"}});
+ std::filesystem::path EmptyFileRelPath = std::filesystem::path("zerobyte_snapshot_test") / "empty.bin";
+ std::filesystem::path EmptyFileAbsPath = RootPath / EmptyFileRelPath;
+ CreateDirectories(MakeSafeAbsolutePath(EmptyFileAbsPath.parent_path()));
+ WriteFile(MakeSafeAbsolutePath(EmptyFileAbsPath), IoBuffer{});
+ REQUIRE(IsFile(MakeSafeAbsolutePath(EmptyFileAbsPath)));
- REQUIRE(Response);
- REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+ const std::string_view EmptyChunkId{
+ "00000000"
+ "00000000"
+ "00030000"};
+ auto EmptyFileOid = zen::Oid::FromHexString(EmptyChunkId);
+
+ zen::CbObjectWriter OpWriter;
+ OpWriter << "key"
+ << "zero_byte_test";
+ OpWriter.BeginArray("files");
+ OpWriter.BeginObject();
+ OpWriter << "id" << EmptyFileOid;
+ OpWriter << "clientpath"
+ << "/{engine}/empty_file";
+ OpWriter << "serverpath" << EmptyFileRelPath.c_str();
+ OpWriter.EndObject();
+ OpWriter.EndArray();
+
+ zen::CbObject Op = OpWriter.Save();
+ zen::CbPackage OpPackage(Op);
- IoBuffer Data = Response.ResponsePayload;
- IoHash RawHash;
- uint64_t RawSize;
- CompressedBuffer Compressed = CompressedBuffer::FromCompressed(SharedBuffer(Data), RawHash, RawSize);
- REQUIRE(Compressed);
- CHECK(RawSize == 0);
- IoBuffer DataDecompressed = Compressed.Decompress().AsIoBuffer();
- CHECK(DataDecompressed.GetSize() == 0);
- }
+ zen::BinaryWriter MemOut;
+ legacy::SaveCbPackage(OpPackage, MemOut);
- // Cleanup
- {
- std::error_code Ec;
- DeleteDirectories(MakeSafeAbsolutePath(RootPath / "zerobyte_snapshot_test"), Ec);
- }
+ HttpClient Http{OplogUri};
- ZEN_INFO("+++++++");
+ {
+ auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView()));
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::Created);
}
- SUBCASE("test chunk not found error")
+ // Read file data before snapshot - raw and uncompressed, 0 bytes.
+ // http.sys converts a 200 OK with empty body to 204 No Content, so
+ // accept either status code.
{
- HttpClient Http{BaseUri};
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << EmptyChunkId;
+ auto Response = Http.Get(ChunkGetUri);
- for (size_t I = 0; I < 65; I++)
- {
- zen::StringBuilder<128> PostUri;
- PostUri << "/f77c781846caead318084604/info";
- auto Response = Http.Get(PostUri);
+ REQUIRE(Response);
+ CHECK((Response.StatusCode == HttpResponseCode::OK || Response.StatusCode == HttpResponseCode::NoContent));
+ CHECK(Response.ResponsePayload.GetSize() == 0);
+ }
- REQUIRE(!Response.Error);
- CHECK(Response.StatusCode == HttpResponseCode::NotFound);
- }
+ // Trigger snapshot.
+ {
+ IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("method"sv, "snapshot"sv); });
+ auto Response = Http.Post("/rpc"sv, Payload);
+ REQUIRE(Response);
+ CHECK(Response.StatusCode == HttpResponseCode::OK);
+ }
+
+ // Read chunk after snapshot - compressed, decompresses to 0 bytes.
+ {
+ zen::StringBuilder<128> ChunkGetUri;
+ ChunkGetUri << "/" << EmptyChunkId;
+ auto Response = Http.Get(ChunkGetUri, {{"Accept-Type", "application/x-ue-comp"}});
+
+ REQUIRE(Response);
+ REQUIRE(Response.StatusCode == HttpResponseCode::OK);
+
+ IoBuffer Data = Response.ResponsePayload;
+ IoHash RawHash;
+ uint64_t RawSize;
+ CompressedBuffer Compressed = CompressedBuffer::FromCompressed(SharedBuffer(Data), RawHash, RawSize);
+ REQUIRE(Compressed);
+ CHECK(RawSize == 0);
+ IoBuffer DataDecompressed = Compressed.Decompress().AsIoBuffer();
+ CHECK(DataDecompressed.GetSize() == 0);
}
- // Cleanup long-path test directory
{
std::error_code Ec;
- DeleteDirectories(MakeSafeAbsolutePath(RootPath / "longpathtest"), Ec);
+ DeleteDirectories(MakeSafeAbsolutePath(RootPath / "zerobyte_snapshot_test"), Ec);
+ }
+
+ ZEN_INFO("+++++++");
+ }
+
+ // --- test chunk not found error ---
+ {
+ std::string OplogUri = CreateProjectAndOplog("test_notfound", "oplog_notfound");
+ HttpClient Http{OplogUri};
+
+ for (size_t I = 0; I < 65; I++)
+ {
+ zen::StringBuilder<128> PostUri;
+ PostUri << "/f77c781846caead318084604/info";
+ auto Response = Http.Get(PostUri);
+
+ REQUIRE(!Response.Error);
+ CHECK(Response.StatusCode == HttpResponseCode::NotFound);
}
}
+
+ // Cleanup long-path test directory
+ {
+ std::error_code Ec;
+ DeleteDirectories(MakeSafeAbsolutePath(RootPath / "longpathtest"), Ec);
+ }
}
CbPackage
@@ -753,86 +760,102 @@ TEST_CASE("project.remote")
}
};
- SUBCASE("File")
+ // --- Zen ---
+ // NOTE: Zen export must run before file-based exports from the same source
+ // oplog. A prior file export leaves server-side state that causes a
+ // subsequent zen-protocol export from the same oplog to abort.
{
+ INFO("Zen");
ScopedTemporaryDirectory TempDir;
{
- IoBuffer Payload = MakeCbObjectPayload([&AttachmentHashes, path = TempDir.Path().string()](CbObjectWriter& Writer) {
+ std::string ExportSourceUri = Servers.GetInstance(0).GetBaseUri();
+ std::string ExportTargetUri = Servers.GetInstance(1).GetBaseUri();
+ MakeProject(ExportTargetUri, "proj0_zen");
+ MakeOplog(ExportTargetUri, "proj0_zen", "oplog0_zen");
+
+ IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
Writer << "method"sv
<< "export"sv;
Writer << "params" << BeginObject;
{
Writer << "maxblocksize"sv << 3072u;
Writer << "maxchunkembedsize"sv << 1296u;
- Writer << "chunkfilesizelimit"sv << 5u * 1024u;
Writer << "maxchunksperblock"sv << 16u;
+ Writer << "chunkfilesizelimit"sv << 5u * 1024u;
Writer << "force"sv << false;
- Writer << "file"sv << BeginObject;
+ Writer << "zen"sv << BeginObject;
{
- Writer << "path"sv << path;
- Writer << "name"sv
- << "proj0_oplog0"sv;
+ Writer << "url"sv << ExportTargetUri.substr(7);
+ Writer << "project"
+ << "proj0_zen";
+ Writer << "oplog"
+ << "oplog0_zen";
}
- Writer << EndObject; // "file"
+ Writer << EndObject; // "zen"
}
Writer << EndObject; // "params"
});
- HttpClient Http{Servers.GetInstance(0).GetBaseUri()};
-
+ HttpClient Http{Servers.GetInstance(0).GetBaseUri()};
HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0", "oplog0"), Payload);
HttpWaitForCompletion(Servers.GetInstance(0), Response);
}
+ ValidateAttachments(1, "proj0_zen", "oplog0_zen");
+ ValidateOplog(1, "proj0_zen", "oplog0_zen");
+
{
- MakeProject(Servers.GetInstance(1).GetBaseUri(), "proj0_copy");
- MakeOplog(Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy");
+ std::string ImportSourceUri = Servers.GetInstance(1).GetBaseUri();
+ std::string ImportTargetUri = Servers.GetInstance(2).GetBaseUri();
+ MakeProject(ImportTargetUri, "proj1");
+ MakeOplog(ImportTargetUri, "proj1", "oplog1");
- IoBuffer Payload = MakeCbObjectPayload([&AttachmentHashes, path = TempDir.Path().string()](CbObjectWriter& Writer) {
+ IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
Writer << "method"sv
<< "import"sv;
Writer << "params" << BeginObject;
{
Writer << "force"sv << false;
- Writer << "file"sv << BeginObject;
+ Writer << "zen"sv << BeginObject;
{
- Writer << "path"sv << path;
- Writer << "name"sv
- << "proj0_oplog0"sv;
+ Writer << "url"sv << ImportSourceUri.substr(7);
+ Writer << "project"
+ << "proj0_zen";
+ Writer << "oplog"
+ << "oplog0_zen";
}
- Writer << EndObject; // "file"
+ Writer << EndObject; // "zen"
}
Writer << EndObject; // "params"
});
- HttpClient Http{Servers.GetInstance(1).GetBaseUri()};
-
- HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0_copy", "oplog0_copy"), Payload);
- HttpWaitForCompletion(Servers.GetInstance(1), Response);
+ HttpClient Http{Servers.GetInstance(2).GetBaseUri()};
+ HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj1", "oplog1"), Payload);
+ HttpWaitForCompletion(Servers.GetInstance(2), Response);
}
- ValidateAttachments(1, "proj0_copy", "oplog0_copy");
- ValidateOplog(1, "proj0_copy", "oplog0_copy");
+ ValidateAttachments(2, "proj1", "oplog1");
+ ValidateOplog(2, "proj1", "oplog1");
}
- SUBCASE("File disable blocks")
+ // --- File ---
{
+ INFO("File");
ScopedTemporaryDirectory TempDir;
{
- IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
+ 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 << "maxchunksperblock"sv << 16u;
Writer << "chunkfilesizelimit"sv << 5u * 1024u;
- Writer << "force"sv << false;
+ Writer << "maxchunksperblock"sv << 16u;
+ Writer << "force"sv << true;
Writer << "file"sv << BeginObject;
{
- Writer << "path"sv << TempDir.Path().string();
+ Writer << "path"sv << path;
Writer << "name"sv
<< "proj0_oplog0"sv;
- Writer << "disableblocks"sv << true;
}
Writer << EndObject; // "file"
}
@@ -845,9 +868,10 @@ TEST_CASE("project.remote")
HttpWaitForCompletion(Servers.GetInstance(0), Response);
}
{
- MakeProject(Servers.GetInstance(1).GetBaseUri(), "proj0_copy");
- MakeOplog(Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy");
- IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
+ MakeProject(Servers.GetInstance(1).GetBaseUri(), "proj0_file");
+ MakeOplog(Servers.GetInstance(1).GetBaseUri(), "proj0_file", "oplog0_file");
+
+ IoBuffer Payload = MakeCbObjectPayload([&AttachmentHashes, path = TempDir.Path().string()](CbObjectWriter& Writer) {
Writer << "method"sv
<< "import"sv;
Writer << "params" << BeginObject;
@@ -855,7 +879,7 @@ TEST_CASE("project.remote")
Writer << "force"sv << false;
Writer << "file"sv << BeginObject;
{
- Writer << "path"sv << TempDir.Path().string();
+ Writer << "path"sv << path;
Writer << "name"sv
<< "proj0_oplog0"sv;
}
@@ -866,15 +890,16 @@ TEST_CASE("project.remote")
HttpClient Http{Servers.GetInstance(1).GetBaseUri()};
- HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0_copy", "oplog0_copy"), Payload);
+ HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0_file", "oplog0_file"), Payload);
HttpWaitForCompletion(Servers.GetInstance(1), Response);
}
- ValidateAttachments(1, "proj0_copy", "oplog0_copy");
- ValidateOplog(1, "proj0_copy", "oplog0_copy");
+ ValidateAttachments(1, "proj0_file", "oplog0_file");
+ ValidateOplog(1, "proj0_file", "oplog0_file");
}
- SUBCASE("File force temp blocks")
+ // --- File disable blocks ---
{
+ INFO("File disable blocks");
ScopedTemporaryDirectory TempDir;
{
IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
@@ -886,26 +911,27 @@ TEST_CASE("project.remote")
Writer << "maxchunkembedsize"sv << 1296u;
Writer << "maxchunksperblock"sv << 16u;
Writer << "chunkfilesizelimit"sv << 5u * 1024u;
- Writer << "force"sv << false;
+ Writer << "force"sv << true;
Writer << "file"sv << BeginObject;
{
Writer << "path"sv << TempDir.Path().string();
Writer << "name"sv
<< "proj0_oplog0"sv;
- Writer << "enabletempblocks"sv << true;
+ Writer << "disableblocks"sv << true;
}
Writer << EndObject; // "file"
}
Writer << EndObject; // "params"
});
- HttpClient Http{Servers.GetInstance(0).GetBaseUri()};
+ HttpClient Http{Servers.GetInstance(0).GetBaseUri()};
+
HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0", "oplog0"), Payload);
HttpWaitForCompletion(Servers.GetInstance(0), Response);
}
{
- MakeProject(Servers.GetInstance(1).GetBaseUri(), "proj0_copy");
- MakeOplog(Servers.GetInstance(1).GetBaseUri(), "proj0_copy", "oplog0_copy");
+ MakeProject(Servers.GetInstance(1).GetBaseUri(), "proj0_noblock");
+ MakeOplog(Servers.GetInstance(1).GetBaseUri(), "proj0_noblock", "oplog0_noblock");
IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
Writer << "method"sv
<< "import"sv;
@@ -923,23 +949,20 @@ TEST_CASE("project.remote")
Writer << EndObject; // "params"
});
- HttpClient Http{Servers.GetInstance(1).GetBaseUri()};
- HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0_copy", "oplog0_copy"), Payload);
+ HttpClient Http{Servers.GetInstance(1).GetBaseUri()};
+
+ HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0_noblock", "oplog0_noblock"), Payload);
HttpWaitForCompletion(Servers.GetInstance(1), Response);
}
- ValidateAttachments(1, "proj0_copy", "oplog0_copy");
- ValidateOplog(1, "proj0_copy", "oplog0_copy");
+ ValidateAttachments(1, "proj0_noblock", "oplog0_noblock");
+ ValidateOplog(1, "proj0_noblock", "oplog0_noblock");
}
- SUBCASE("Zen")
+ // --- File force temp blocks ---
{
+ INFO("File force temp blocks");
ScopedTemporaryDirectory TempDir;
{
- std::string ExportSourceUri = Servers.GetInstance(0).GetBaseUri();
- std::string ExportTargetUri = Servers.GetInstance(1).GetBaseUri();
- MakeProject(ExportTargetUri, "proj0_copy");
- MakeOplog(ExportTargetUri, "proj0_copy", "oplog0_copy");
-
IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
Writer << "method"sv
<< "export"sv;
@@ -949,14 +972,13 @@ TEST_CASE("project.remote")
Writer << "maxchunkembedsize"sv << 1296u;
Writer << "maxchunksperblock"sv << 16u;
Writer << "chunkfilesizelimit"sv << 5u * 1024u;
- Writer << "force"sv << false;
- Writer << "zen"sv << BeginObject;
+ Writer << "force"sv << true;
+ Writer << "file"sv << BeginObject;
{
- Writer << "url"sv << ExportTargetUri.substr(7);
- Writer << "project"
- << "proj0_copy";
- Writer << "oplog"
- << "oplog0_copy";
+ Writer << "path"sv << TempDir.Path().string();
+ Writer << "name"sv
+ << "proj0_oplog0"sv;
+ Writer << "enabletempblocks"sv << true;
}
Writer << EndObject; // "file"
}
@@ -967,40 +989,32 @@ TEST_CASE("project.remote")
HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0", "oplog0"), Payload);
HttpWaitForCompletion(Servers.GetInstance(0), Response);
}
- 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(ImportTargetUri, "proj1");
- MakeOplog(ImportTargetUri, "proj1", "oplog1");
-
+ MakeProject(Servers.GetInstance(1).GetBaseUri(), "proj0_tmpblock");
+ MakeOplog(Servers.GetInstance(1).GetBaseUri(), "proj0_tmpblock", "oplog0_tmpblock");
IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) {
Writer << "method"sv
<< "import"sv;
Writer << "params" << BeginObject;
{
Writer << "force"sv << false;
- Writer << "zen"sv << BeginObject;
+ Writer << "file"sv << BeginObject;
{
- Writer << "url"sv << ImportSourceUri.substr(7);
- Writer << "project"
- << "proj0_copy";
- Writer << "oplog"
- << "oplog0_copy";
+ Writer << "path"sv << TempDir.Path().string();
+ Writer << "name"sv
+ << "proj0_oplog0"sv;
}
Writer << EndObject; // "file"
}
Writer << EndObject; // "params"
});
- HttpClient Http{Servers.GetInstance(2).GetBaseUri()};
- HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj1", "oplog1"), Payload);
- HttpWaitForCompletion(Servers.GetInstance(2), Response);
+ HttpClient Http{Servers.GetInstance(1).GetBaseUri()};
+ HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", "proj0_tmpblock", "oplog0_tmpblock"), Payload);
+ HttpWaitForCompletion(Servers.GetInstance(1), Response);
}
- ValidateAttachments(2, "proj1", "oplog1");
- ValidateOplog(2, "proj1", "oplog1");
+ ValidateAttachments(1, "proj0_tmpblock", "oplog0_tmpblock");
+ ValidateOplog(1, "proj0_tmpblock", "oplog0_tmpblock");
}
}
@@ -1379,7 +1393,7 @@ TEST_CASE("project.file.data.transitions")
return Package;
};
- SUBCASE("path-referenced file is retrievable")
+ // --- path-referenced file is retrievable ---
{
MakeProject("proj_path"sv);
MakeOplog("proj_path"sv, "oplog"sv);
@@ -1397,7 +1411,7 @@ TEST_CASE("project.file.data.transitions")
}
}
- SUBCASE("hash-referenced file is retrievable")
+ // --- hash-referenced file is retrievable ---
{
MakeProject("proj_hash"sv);
MakeOplog("proj_hash"sv, "oplog"sv);
@@ -1416,34 +1430,35 @@ TEST_CASE("project.file.data.transitions")
}
}
- SUBCASE("hash-referenced to path-referenced transition with different content")
+ struct TransitionVariant
{
- MakeProject("proj_hash_to_path_diff"sv);
- MakeOplog("proj_hash_to_path_diff"sv, "oplog"sv);
+ std::string_view Suffix;
+ bool SameOpKey;
+ bool RunGc;
+ };
- Oid FirstOpKey = Oid::NewOid();
- Oid SecondOpKey;
- bool RunGcAfterTransition = false;
+ static constexpr TransitionVariant Variants[] = {
+ {"_nk", false, false},
+ {"_sk", true, false},
+ {"_nk_gc", false, true},
+ {"_sk_gc", true, true},
+ };
- SUBCASE("new op key") { SecondOpKey = Oid::NewOid(); }
- SUBCASE("same op key") { SecondOpKey = FirstOpKey; }
- SUBCASE("new op key with gc")
- {
- SecondOpKey = Oid::NewOid();
- RunGcAfterTransition = true;
- }
- SUBCASE("same op key with gc")
- {
- SecondOpKey = FirstOpKey;
- RunGcAfterTransition = true;
- }
+ // --- hash-referenced to path-referenced transition with different content ---
+ for (const TransitionVariant& V : Variants)
+ {
+ std::string ProjName = fmt::format("proj_h2pd{}", V.Suffix);
+ MakeProject(ProjName);
+ MakeOplog(ProjName, "oplog"sv);
+
+ Oid FirstOpKey = Oid::NewOid();
+ Oid SecondOpKey = V.SameOpKey ? FirstOpKey : Oid::NewOid();
- // First op: file with CAS hash (content differs from the on-disk file)
{
CbPackage Op = BuildHashReferencedFileOp(FirstOpKey, CompressedBlob);
- PostOplogEntry("proj_hash_to_path_diff"sv, "oplog"sv, Op);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
- HttpClient::Response Response = GetChunk("proj_hash_to_path_diff"sv);
+ HttpClient::Response Response = GetChunk(ProjName);
CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk first op"));
if (Response.IsSuccess())
{
@@ -1453,19 +1468,17 @@ TEST_CASE("project.file.data.transitions")
}
}
- // Second op: same FileId transitions to serverpath (different data)
{
CbPackage Op = BuildPathReferencedFileOp(SecondOpKey);
- PostOplogEntry("proj_hash_to_path_diff"sv, "oplog"sv, Op);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
}
- if (RunGcAfterTransition)
+ if (V.RunGc)
{
TriggerGcAndWait();
}
- // Must serve the on-disk file content, not the old CAS blob
- HttpClient::Response Response = GetChunk("proj_hash_to_path_diff"sv);
+ HttpClient::Response Response = GetChunk(ProjName);
CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk after transition"));
if (Response.IsSuccess())
{
@@ -1475,95 +1488,68 @@ TEST_CASE("project.file.data.transitions")
}
}
- SUBCASE("hash-referenced to path-referenced transition with identical content")
+ // --- hash-referenced to path-referenced transition with identical content ---
{
- // Compress the same on-disk file content as a CAS blob so both references yield identical data
CompressedBuffer MatchingBlob = CompressedBuffer::Compress(SharedBuffer::Clone(FileBlob.GetView()));
- MakeProject("proj_hash_to_path_same"sv);
- MakeOplog("proj_hash_to_path_same"sv, "oplog"sv);
+ for (const TransitionVariant& V : Variants)
+ {
+ std::string ProjName = fmt::format("proj_h2ps{}", V.Suffix);
+ MakeProject(ProjName);
+ MakeOplog(ProjName, "oplog"sv);
- Oid FirstOpKey = Oid::NewOid();
- Oid SecondOpKey;
- bool RunGcAfterTransition = false;
+ Oid FirstOpKey = Oid::NewOid();
+ Oid SecondOpKey = V.SameOpKey ? FirstOpKey : Oid::NewOid();
- SUBCASE("new op key") { SecondOpKey = Oid::NewOid(); }
- SUBCASE("same op key") { SecondOpKey = FirstOpKey; }
- SUBCASE("new op key with gc")
- {
- SecondOpKey = Oid::NewOid();
- RunGcAfterTransition = true;
- }
- SUBCASE("same op key with gc")
- {
- SecondOpKey = FirstOpKey;
- RunGcAfterTransition = true;
- }
+ {
+ CbPackage Op = BuildHashReferencedFileOp(FirstOpKey, MatchingBlob);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
- // First op: file with CAS hash (content matches the on-disk file)
- {
- CbPackage Op = BuildHashReferencedFileOp(FirstOpKey, MatchingBlob);
- PostOplogEntry("proj_hash_to_path_same"sv, "oplog"sv, Op);
+ HttpClient::Response Response = GetChunk(ProjName);
+ CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk first op"));
+ if (Response.IsSuccess())
+ {
+ IoBuffer Payload = GetDecompressedPayload(Response);
+ CHECK(Payload.GetView().EqualBytes(FileBlob.GetView()));
+ }
+ }
- HttpClient::Response Response = GetChunk("proj_hash_to_path_same"sv);
- CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk first op"));
+ {
+ CbPackage Op = BuildPathReferencedFileOp(SecondOpKey);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
+ }
+
+ if (V.RunGc)
+ {
+ TriggerGcAndWait();
+ }
+
+ HttpClient::Response Response = GetChunk(ProjName);
+ CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk after transition"));
if (Response.IsSuccess())
{
IoBuffer Payload = GetDecompressedPayload(Response);
+ CHECK_EQ(Payload.GetSize(), FileBlob.GetSize());
CHECK(Payload.GetView().EqualBytes(FileBlob.GetView()));
}
}
-
- // Second op: same FileId transitions to serverpath (same data)
- {
- CbPackage Op = BuildPathReferencedFileOp(SecondOpKey);
- PostOplogEntry("proj_hash_to_path_same"sv, "oplog"sv, Op);
- }
-
- if (RunGcAfterTransition)
- {
- TriggerGcAndWait();
- }
-
- // Must still resolve successfully after the transition
- HttpClient::Response Response = GetChunk("proj_hash_to_path_same"sv);
- CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk after transition"));
- if (Response.IsSuccess())
- {
- IoBuffer Payload = GetDecompressedPayload(Response);
- CHECK_EQ(Payload.GetSize(), FileBlob.GetSize());
- CHECK(Payload.GetView().EqualBytes(FileBlob.GetView()));
- }
}
- SUBCASE("path-referenced to hash-referenced transition with different content")
+ // --- path-referenced to hash-referenced transition with different content ---
+ for (const TransitionVariant& V : Variants)
{
- MakeProject("proj_path_to_hash_diff"sv);
- MakeOplog("proj_path_to_hash_diff"sv, "oplog"sv);
-
- Oid FirstOpKey = Oid::NewOid();
- Oid SecondOpKey;
- bool RunGcAfterTransition = false;
+ std::string ProjName = fmt::format("proj_p2hd{}", V.Suffix);
+ MakeProject(ProjName);
+ MakeOplog(ProjName, "oplog"sv);
- SUBCASE("new op key") { SecondOpKey = Oid::NewOid(); }
- SUBCASE("same op key") { SecondOpKey = FirstOpKey; }
- SUBCASE("new op key with gc")
- {
- SecondOpKey = Oid::NewOid();
- RunGcAfterTransition = true;
- }
- SUBCASE("same op key with gc")
- {
- SecondOpKey = FirstOpKey;
- RunGcAfterTransition = true;
- }
+ Oid FirstOpKey = Oid::NewOid();
+ Oid SecondOpKey = V.SameOpKey ? FirstOpKey : Oid::NewOid();
- // First op: file with serverpath
{
CbPackage Op = BuildPathReferencedFileOp(FirstOpKey);
- PostOplogEntry("proj_path_to_hash_diff"sv, "oplog"sv, Op);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
- HttpClient::Response Response = GetChunk("proj_path_to_hash_diff"sv);
+ HttpClient::Response Response = GetChunk(ProjName);
CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk first op"));
if (Response.IsSuccess())
{
@@ -1572,19 +1558,17 @@ TEST_CASE("project.file.data.transitions")
}
}
- // Second op: same FileId transitions to CAS hash (different data)
{
CbPackage Op = BuildHashReferencedFileOp(SecondOpKey, CompressedBlob);
- PostOplogEntry("proj_path_to_hash_diff"sv, "oplog"sv, Op);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
}
- if (RunGcAfterTransition)
+ if (V.RunGc)
{
TriggerGcAndWait();
}
- // Must serve the CAS blob content, not the old on-disk file
- HttpClient::Response Response = GetChunk("proj_path_to_hash_diff"sv);
+ HttpClient::Response Response = GetChunk(ProjName);
CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk after transition"));
if (Response.IsSuccess())
{
@@ -1595,65 +1579,51 @@ TEST_CASE("project.file.data.transitions")
}
}
- SUBCASE("path-referenced to hash-referenced transition with identical content")
+ // --- path-referenced to hash-referenced transition with identical content ---
{
- // Compress the same on-disk file content as a CAS blob so both references yield identical data
CompressedBuffer MatchingBlob = CompressedBuffer::Compress(SharedBuffer::Clone(FileBlob.GetView()));
- MakeProject("proj_path_to_hash_same"sv);
- MakeOplog("proj_path_to_hash_same"sv, "oplog"sv);
+ for (const TransitionVariant& V : Variants)
+ {
+ std::string ProjName = fmt::format("proj_p2hs{}", V.Suffix);
+ MakeProject(ProjName);
+ MakeOplog(ProjName, "oplog"sv);
- Oid FirstOpKey = Oid::NewOid();
- Oid SecondOpKey;
- bool RunGcAfterTransition = false;
+ Oid FirstOpKey = Oid::NewOid();
+ Oid SecondOpKey = V.SameOpKey ? FirstOpKey : Oid::NewOid();
- SUBCASE("new op key") { SecondOpKey = Oid::NewOid(); }
- SUBCASE("same op key") { SecondOpKey = FirstOpKey; }
- SUBCASE("new op key with gc")
- {
- SecondOpKey = Oid::NewOid();
- RunGcAfterTransition = true;
- }
- SUBCASE("same op key with gc")
- {
- SecondOpKey = FirstOpKey;
- RunGcAfterTransition = true;
- }
+ {
+ CbPackage Op = BuildPathReferencedFileOp(FirstOpKey);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
- // First op: file with serverpath
- {
- CbPackage Op = BuildPathReferencedFileOp(FirstOpKey);
- PostOplogEntry("proj_path_to_hash_same"sv, "oplog"sv, Op);
+ HttpClient::Response Response = GetChunk(ProjName);
+ CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk first op"));
+ if (Response.IsSuccess())
+ {
+ IoBuffer Payload = GetDecompressedPayload(Response);
+ CHECK(Payload.GetView().EqualBytes(FileBlob.GetView()));
+ }
+ }
- HttpClient::Response Response = GetChunk("proj_path_to_hash_same"sv);
- CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk first op"));
+ {
+ CbPackage Op = BuildHashReferencedFileOp(SecondOpKey, MatchingBlob);
+ PostOplogEntry(ProjName, "oplog"sv, Op);
+ }
+
+ if (V.RunGc)
+ {
+ TriggerGcAndWait();
+ }
+
+ HttpClient::Response Response = GetChunk(ProjName);
+ CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk after transition"));
if (Response.IsSuccess())
{
IoBuffer Payload = GetDecompressedPayload(Response);
+ CHECK_EQ(Payload.GetSize(), FileBlob.GetSize());
CHECK(Payload.GetView().EqualBytes(FileBlob.GetView()));
}
}
-
- // Second op: same FileId transitions to CAS hash (same data)
- {
- CbPackage Op = BuildHashReferencedFileOp(SecondOpKey, MatchingBlob);
- PostOplogEntry("proj_path_to_hash_same"sv, "oplog"sv, Op);
- }
-
- if (RunGcAfterTransition)
- {
- TriggerGcAndWait();
- }
-
- // Must still resolve successfully after the transition
- HttpClient::Response Response = GetChunk("proj_path_to_hash_same"sv);
- CHECK_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("GetChunk after transition"));
- if (Response.IsSuccess())
- {
- IoBuffer Payload = GetDecompressedPayload(Response);
- CHECK_EQ(Payload.GetSize(), FileBlob.GetSize());
- CHECK(Payload.GetView().EqualBytes(FileBlob.GetView()));
- }
}
}