diff options
| author | Zousar Shaker <[email protected]> | 2026-04-01 03:39:18 -0600 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-04-01 11:39:18 +0200 |
| commit | 99753a221753ba8d04b1f8b57454b8ba1f9a99ae (patch) | |
| tree | e415a7b57f7e4fabbaeaf08a85c301ffde0fd64f /src/zenserver-test | |
| parent | 5.8.1 (diff) | |
| download | zen-99753a221753ba8d04b1f8b57454b8ba1f9a99ae.tar.xz zen-99753a221753ba8d04b1f8b57454b8ba1f9a99ae.zip | |
Zs/oplog export zero size attachment fix (#911)
* Unit test coverage for zero byte file handling in oplogs
* Unit test fixes for the zero length file case
* Fixes for zero length file attachments
* Additional fix for zero length file attachments
Diffstat (limited to 'src/zenserver-test')
| -rw-r--r-- | src/zenserver-test/projectstore-tests.cpp | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/zenserver-test/projectstore-tests.cpp b/src/zenserver-test/projectstore-tests.cpp index d72b897c8..cec453511 100644 --- a/src/zenserver-test/projectstore-tests.cpp +++ b/src/zenserver-test/projectstore-tests.cpp @@ -341,6 +341,102 @@ TEST_CASE("project.basic") ZEN_INFO("+++++++"); } + SUBCASE("snapshot zero byte file") + { + // 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}; + + { + auto Response = Http.Post("/new", IoBufferBuilder::MakeFromMemory(MemOut.GetView())); + REQUIRE(Response); + CHECK(Response.StatusCode == HttpResponseCode::Created); + } + + // 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); + + REQUIRE(Response); + CHECK((Response.StatusCode == HttpResponseCode::OK || Response.StatusCode == HttpResponseCode::NoContent)); + CHECK(Response.ResponsePayload.GetSize() == 0); + } + + // 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 + { + std::error_code Ec; + DeleteDirectories(MakeSafeAbsolutePath(RootPath / "zerobyte_snapshot_test"), Ec); + } + + ZEN_INFO("+++++++"); + } + SUBCASE("test chunk not found error") { HttpClient Http{BaseUri}; |