diff --git a/src/FrostFS.SDK.Client/AssemblyInfo.cs b/src/FrostFS.SDK.Client/AssemblyInfo.cs index 4d87f8c..4fa6cd9 100644 --- a/src/FrostFS.SDK.Client/AssemblyInfo.cs +++ b/src/FrostFS.SDK.Client/AssemblyInfo.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; "e15ab287e6239c98d5dfa91615bd77485d523a3a3f65a4e5028454cedd5ac4d9eca6da18b81985"+ "ac6905d33cc64b5a2587050c16f67b71ef8889dbd3c90ef7cc0b06bbbe09886601d195f5db179a"+ "3c2a25b1")] -[assembly: AssemblyFileVersion("1.0.6.0")] +[assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyProduct("FrostFS.SDK.Client")] [assembly: AssemblyTitle("FrostFS.SDK.Client")] -[assembly: AssemblyVersion("1.0.6")] +[assembly: AssemblyVersion("1.0.7")] diff --git a/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj b/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj index 037a2f4..88e20c6 100644 --- a/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj +++ b/src/FrostFS.SDK.Client/FrostFS.SDK.Client.csproj @@ -6,7 +6,7 @@ enable AllEnabledByDefault FrostFS.SDK.Client - 1.0.6 + 1.0.7 C# SDK for FrostFS gRPC native protocol diff --git a/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs b/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs index e4ab8a2..b3fa5e3 100644 --- a/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs +++ b/src/FrostFS.SDK.Client/Mappers/MetaHeader.cs @@ -16,8 +16,8 @@ public static class MetaHeaderMapper return new RequestMetaHeader { Version = metaHeader.Version.ToMessage(), - Epoch = (uint)metaHeader.Epoch, - Ttl = (uint)metaHeader.Ttl + Epoch = metaHeader.Epoch, + Ttl = metaHeader.Ttl }; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs index 00ad4a3..4fa2edd 100644 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs +++ b/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs @@ -11,7 +11,7 @@ public static class PolicyMapper { return new Replica { - Count = (uint)replica.Count, + Count = replica.Count, Selector = replica.Selector, EcDataCount = replica.EcDataCount, EcParityCount = replica.EcParityCount @@ -25,7 +25,7 @@ public static class PolicyMapper throw new ArgumentNullException(nameof(replica)); } - return new FrostFsReplica((int)replica.Count, replica.Selector) + return new FrostFsReplica(replica.Count, replica.Selector) { EcDataCount = replica.EcDataCount, EcParityCount = replica.EcParityCount diff --git a/src/FrostFS.SDK.Client/Mappers/Version.cs b/src/FrostFS.SDK.Client/Mappers/Version.cs index a66df6e..af8738d 100644 --- a/src/FrostFS.SDK.Client/Mappers/Version.cs +++ b/src/FrostFS.SDK.Client/Mappers/Version.cs @@ -18,7 +18,7 @@ public static class VersionMapper throw new System.ArgumentNullException(nameof(model)); } - var key = model.Major << 16 + model.Minor; + var key = (int)model.Major << 16 + (int)model.Minor; if (!_cacheMessages.ContainsKey(key)) { @@ -28,8 +28,8 @@ public static class VersionMapper _spinlock.Enter(ref lockTaken); var message = new Version { - Major = (uint)model.Major, - Minor = (uint)model.Minor + Major = model.Major, + Minor = model.Minor }; _cacheMessages.Add(key, message); @@ -64,7 +64,7 @@ public static class VersionMapper try { _spinlock.Enter(ref lockTaken); - var model = new FrostFsVersion((int)message.Major, (int)message.Minor); + var model = new FrostFsVersion(message.Major, message.Minor); _cacheModels.Add(key, model); return model; diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs index 5ace048..82e2240 100644 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs +++ b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsReplica.cs @@ -4,12 +4,12 @@ namespace FrostFS.SDK; public struct FrostFsReplica : IEquatable { - public int Count { get; set; } + public uint Count { get; set; } public string Selector { get; set; } public uint EcDataCount { get; set; } public uint EcParityCount { get; set; } - public FrostFsReplica(int count, string? selector = null) + public FrostFsReplica(uint count, string? selector = null) { selector ??= string.Empty; @@ -31,12 +31,12 @@ public struct FrostFsReplica : IEquatable public readonly uint CountNodes() { - return Count != 0 ? (uint)Count : EcDataCount + EcParityCount; + return Count != 0 ? Count : EcDataCount + EcParityCount; } public override readonly int GetHashCode() { - return (Count + Selector.GetHashCode()) ^ (int)EcDataCount ^ (int)EcParityCount; + return Count.GetHashCode() ^ Selector.GetHashCode() ^ (int)EcDataCount ^ (int)EcParityCount; } public static bool operator ==(FrostFsReplica left, FrostFsReplica right) diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs index 81e642d..9ed9522 100644 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs +++ b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsVersion.cs @@ -3,12 +3,12 @@ using FrostFS.SDK.Client.Mappers.GRPC; namespace FrostFS.SDK; -public class FrostFsVersion(int major, int minor) +public class FrostFsVersion(uint major, uint minor) { private Version? version; - public int Major { get; set; } = major; - public int Minor { get; set; } = minor; + public uint Major { get; set; } = major; + public uint Minor { get; set; } = minor; internal Version VersionID { diff --git a/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs b/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs index 36dad09..547b2b5 100644 --- a/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs +++ b/src/FrostFS.SDK.Client/Models/Response/MetaHeader.cs @@ -1,10 +1,10 @@ namespace FrostFS.SDK; -public class MetaHeader(FrostFsVersion version, int epoch, int ttl) +public class MetaHeader(FrostFsVersion version, ulong epoch, uint ttl) { public FrostFsVersion Version { get; set; } = version; - public int Epoch { get; set; } = epoch; - public int Ttl { get; set; } = ttl; + public ulong Epoch { get; set; } = epoch; + public uint Ttl { get; set; } = ttl; public static MetaHeader Default() { diff --git a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs index a8b748f..b3c1f50 100644 --- a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs @@ -295,86 +295,88 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl internal async Task PatchObjectAsync(PrmObjectPatch args, CallContext ctx) { var chunkSize = args.MaxChunkLength; - Stream payload = args.Payload ?? throw new ArgumentNullException(nameof(args), "Stream parameter is null"); - + var call = client.Patch(null, ctx.GetDeadline(), ctx.CancellationToken); - byte[]? chunkBuffer = null; - try + var address = new Address { - chunkBuffer = ArrayPool.Shared.Rent(chunkSize); + ObjectId = args.Address.ObjectId, + ContainerId = args.Address.ContainerId + }; - bool isFirstChunk = true; - ulong currentPos = args.Range.Offset; - - var address = new Address + if (args.Payload != null && args.Payload.Length > 0) + { + byte[]? chunkBuffer = null; + try { - ObjectId = args.Address.ObjectId, - ContainerId = args.Address.ContainerId - }; + chunkBuffer = ArrayPool.Shared.Rent(chunkSize); - while (true) - { - var bytesCount = await payload.ReadAsync(chunkBuffer, 0, chunkSize, ctx.CancellationToken).ConfigureAwait(false); + bool isFirstChunk = true; + ulong currentPos = args.Range.Offset; - if (bytesCount == 0) + while (true) { - break; - } + var bytesCount = await args.Payload.ReadAsync(chunkBuffer, 0, chunkSize, ctx.CancellationToken).ConfigureAwait(false); - var request = new PatchRequest() - { - Body = new() + if (bytesCount == 0) { - Address = address, - Patch = new PatchRequest.Types.Body.Types.Patch + break; + } + + PatchRequest request; + + if (isFirstChunk) + { + request = await CreateFirstRequest(args, ctx, address).ConfigureAwait(false); + + request.Body.Patch = new PatchRequest.Types.Body.Types.Patch { Chunk = UnsafeByteOperations.UnsafeWrap(chunkBuffer.AsMemory(0, bytesCount)), SourceRange = new Range { Offset = currentPos, Length = (ulong)bytesCount } - } + }; + + isFirstChunk = false; } - }; - - if (isFirstChunk) - { - var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); - - var protoToken = sessionToken.CreateObjectTokenContext( - address, - ObjectSessionContext.Types.Verb.Patch, - ClientContext.Key); - - request.AddMetaHeader(args.XHeaders, protoToken); - - if (args.NewAttributes != null && args.NewAttributes.Length > 0) + else { - foreach (var attr in args.NewAttributes) + request = new PatchRequest() { - request.Body.NewAttributes.Add(attr.ToMessage()); - request.Body.ReplaceAttributes = args.ReplaceAttributes; - } + Body = new() + { + Address = address, + Patch = new PatchRequest.Types.Body.Types.Patch + { + Chunk = UnsafeByteOperations.UnsafeWrap(chunkBuffer.AsMemory(0, bytesCount)), + SourceRange = new Range { Offset = currentPos, Length = (ulong)bytesCount } + } + } + }; + + request.AddMetaHeader(args.XHeaders); } - isFirstChunk = false; + request.Sign(ClientContext.Key); + + await call.RequestStream.WriteAsync(request).ConfigureAwait(false); + + currentPos += (ulong)bytesCount; } - else + } + finally + { + if (chunkBuffer != null) { - request.AddMetaHeader(args.XHeaders); + ArrayPool.Shared.Return(chunkBuffer); } - - request.Sign(ClientContext.Key); - - await call.RequestStream.WriteAsync(request).ConfigureAwait(false); - - currentPos += (ulong)bytesCount; } } - finally + else if (args.NewAttributes != null && args.NewAttributes.Length > 0) { - if (chunkBuffer != null) - { - ArrayPool.Shared.Return(chunkBuffer); - } + PatchRequest request = await CreateFirstRequest(args, ctx, address).ConfigureAwait(false); + + request.Sign(ClientContext.Key); + + await call.RequestStream.WriteAsync(request).ConfigureAwait(false); } await call.RequestStream.CompleteAsync().ConfigureAwait(false); @@ -383,9 +385,36 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl Verifier.CheckResponse(response); return response.Body.ObjectId.ToModel(); + + async Task CreateFirstRequest(PrmObjectPatch args, CallContext ctx, Address address) + { + var body = new PatchRequest.Types.Body() { Address = address }; + + if (args.NewAttributes != null) + { + body.ReplaceAttributes = args.ReplaceAttributes; + + foreach (var attr in args.NewAttributes!) + { + body.NewAttributes.Add(attr.ToMessage()); + } + } + + var request = new PatchRequest() { Body = body }; + + var sessionToken = args.SessionToken ?? await GetDefaultSession(args, ctx).ConfigureAwait(false); + + var protoToken = sessionToken.CreateObjectTokenContext( + address, + ObjectSessionContext.Types.Verb.Patch, + ClientContext.Key); + + request.AddMetaHeader(args.XHeaders, protoToken); + return request; + } } - internal async Task PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx) + internal async Task PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx) { if (args.Payload == null) throw new ArgumentException(nameof(args.Payload)); @@ -431,9 +460,9 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl var objectsCount = fullLength > 0 ? (int)(fullLength / (ulong)partSize) + restPart : 0; progressInfo ??= new UploadProgressInfo(Guid.NewGuid(), objectsCount); - + var remain = fullLength; - + byte[]? buffer = null; bool isRentBuffer = false; @@ -443,7 +472,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl { if (args.CustomBuffer.Length < chunkSize) throw new ArgumentException($"Buffer size is too small. At least {chunkSize} required"); - + buffer = args.CustomBuffer; } else @@ -457,7 +486,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl while (remain > 0) { - var bytesToWrite = Math.Min((ulong)partSize, remain); + var bytesToWrite = Math.Min((ulong)partSize, remain); var isLastPart = remain <= (ulong)partSize; // When the last part of the object is uploaded, all metadata for the object must be added @@ -502,7 +531,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl var part = new ObjectPartInfo(offset, uploaded, objectId); offset += uploaded; progressInfo.AddPart(part); - + remain -= bytesToWrite; if (isLastPart) @@ -582,16 +611,16 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl var restPart = (fullLength % (ulong)partSize) > 0 ? 1 : 0; var objectsCount = fullLength > 0 ? (int)(fullLength / (ulong)partSize) + restPart : 0; + progressInfo ??= new UploadProgressInfo(Guid.NewGuid(), objectsCount); + // if the object fits one part, it can be loaded as non-complex object, but if it is not upload resuming - if (objectsCount == 1 && progressInfo != null && progressInfo.GetLast().Length == 0) + if (objectsCount == 1 && progressInfo.GetLast().Length == 0) { args.PutObjectContext.MaxObjectSizeCache = partSize; args.PutObjectContext.FullLength = fullLength; var singlePartResult = await PutMultipartStreamObjectAsync(args, default).ConfigureAwait(false); return singlePartResult.ObjectId; } - - progressInfo ??= new UploadProgressInfo(Guid.NewGuid(), objectsCount); var remain = fullLength; @@ -659,8 +688,10 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl offset += size; if (i < objectsCount) + { continue; - + } + // Once all parts of the object are uploaded, they must be linked into a single entity var linkObject = new FrostFsLinkObject(args.Header.ContainerId, progressInfo.SplitId, parentHeader!, [.. progressInfo.GetParts().Select(p => p.ObjectId)]); diff --git a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs index cff5cc1..64bc8e9 100644 --- a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs +++ b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs @@ -1,7 +1,7 @@ using System.Reflection; [assembly: AssemblyCompany("TrueCloudLab")] -[assembly: AssemblyFileVersion("1.0.6.0")] +[assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyProduct("FrostFS.SDK.Cryptography")] [assembly: AssemblyTitle("FrostFS.SDK.Cryptography")] -[assembly: AssemblyVersion("1.0.6.0")] +[assembly: AssemblyVersion("1.0.7.0")] diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj index 20e0a83..faeabb1 100644 --- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj +++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj @@ -5,7 +5,7 @@ 12.0 enable FrostFS.SDK.Cryptography - 1.0.6 + 1.0.7 Cryptography tools for C# SDK diff --git a/src/FrostFS.SDK.Protos/AssemblyInfo.cs b/src/FrostFS.SDK.Protos/AssemblyInfo.cs index f16be42..d7fab05 100644 --- a/src/FrostFS.SDK.Protos/AssemblyInfo.cs +++ b/src/FrostFS.SDK.Protos/AssemblyInfo.cs @@ -1,7 +1,7 @@ using System.Reflection; [assembly: AssemblyCompany("TrueCloudLab")] -[assembly: AssemblyFileVersion("1.0.6.0")] +[assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyProduct("FrostFS.SDK.Protos")] [assembly: AssemblyTitle("FrostFS.SDK.Protos")] -[assembly: AssemblyVersion("1.0.6.0")] +[assembly: AssemblyVersion("1.0.7.0")] diff --git a/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj b/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj index b6bc876..222acd1 100644 --- a/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj +++ b/src/FrostFS.SDK.Protos/FrostFS.SDK.Protos.csproj @@ -5,7 +5,7 @@ 12.0 enable FrostFS.SDK.Protos - 1.0.6 + 1.0.7 Protobuf client for C# SDK diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs index 04a8b2c..f1032db 100644 --- a/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs @@ -77,7 +77,7 @@ public class ContainerTests : SmokeTestsBase Assert.Empty(container.PlacementPolicy.Value.Filters); Assert.Single(container.PlacementPolicy.Value.Replicas); - Assert.Equal(3, container.PlacementPolicy.Value.Replicas[0].Count); + Assert.Equal(3u, container.PlacementPolicy.Value.Replicas[0].Count); Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcParityCount); Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcDataCount); Assert.Equal("", container.PlacementPolicy.Value.Replicas[0].Selector); @@ -191,7 +191,7 @@ public class ContainerTests : SmokeTestsBase Assert.Empty(subFilter.Filters); Assert.Single(container.PlacementPolicy.Value.Replicas); - Assert.Equal(1, container.PlacementPolicy.Value.Replicas[0].Count); + Assert.Equal(1u, container.PlacementPolicy.Value.Replicas[0].Count); Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcParityCount); Assert.Equal(0u, container.PlacementPolicy.Value.Replicas[0].EcDataCount); Assert.Equal("", container.PlacementPolicy.Value.Replicas[0].Selector); diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs b/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs index 664d522..9d2fd63 100644 --- a/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs +++ b/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs @@ -29,8 +29,8 @@ public class InterceptorTests() : SmokeTestsBase Assert.True(callbackInvoked); Assert.True(interceptorInvoked); - Assert.Equal(2, result.Version.Major); - Assert.Equal(13, result.Version.Minor); + Assert.Equal(2u, result.Version.Major); + Assert.Equal(13u, result.Version.Minor); Assert.Equal(NodeState.Online, result.State); Assert.Equal(33, result.PublicKey.Length); Assert.NotNull(result.Addresses); diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs index a3d25af..229531a 100644 --- a/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs @@ -28,8 +28,8 @@ public class SmokeClientTests : SmokeTestsBase var result = await client.GetNodeInfoAsync(default); - Assert.Equal(2, result.Version.Major); - Assert.Equal(13, result.Version.Minor); + Assert.Equal(2u, result.Version.Major); + Assert.Equal(13u, result.Version.Minor); Assert.Equal(NodeState.Online, result.State); Assert.Equal(33, result.PublicKey.Length); Assert.Single(result.Addresses); @@ -103,8 +103,8 @@ public class SmokeClientTests : SmokeTestsBase Assert.True(callbackInvoked); Assert.True(interceptorInvoked); - Assert.Equal(2, result.Version.Major); - Assert.Equal(13, result.Version.Minor); + Assert.Equal(2u, result.Version.Major); + Assert.Equal(13u, result.Version.Minor); Assert.Equal(NodeState.Online, result.State); Assert.Equal(33, result.PublicKey.Length); Assert.NotNull(result.Addresses); diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs index a0f501f..473b076 100644 --- a/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs @@ -26,7 +26,7 @@ public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase [InlineData(false, 2, 3)] [InlineData(true, 2, 1)] [InlineData(false, 2, 1)] - public async void FullScenario(bool unique, uint backupFactor, int replicas) + public async void FullScenario(bool unique, uint backupFactor, uint replicas) { var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); _testOutputHelper.WriteLine("client created"); @@ -121,9 +121,16 @@ public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase await ValidateFilters(client, containerId, objectId, null, (ulong)bytes.Length); _testOutputHelper.WriteLine($"\tfilters validated"); - if (type != clientCut) + if (type != clientCut && bytes.Length > 1024 + 64 && bytes.Length < 20 * 1024 * 1024) { - await ValidatePatch(client, containerId, bytes, objectId); + // patch payload only + await ValidatePatch(client, containerId, bytes, true, objectId, [], false); + + // patch attributes only + await ValidatePatch(client, containerId, bytes, false, objectId, [new("a1", "v1"), new("a2", "v2")], false); + + // patch payload and attributes + await ValidatePatch(client, containerId, bytes, true, objectId, [new("a3", "v3"), new("a4", "v4")], true); _testOutputHelper.WriteLine($"\tpatch validated"); } @@ -199,51 +206,73 @@ public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase _testOutputHelper.WriteLine($"\t\trange {range.Offset};{range.Length} validated"); } - private static async Task ValidatePatch(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId) + private static async Task ValidatePatch( + IFrostFSClient client, + FrostFsContainerId containerId, + byte[] bytes, + bool patchPayload, + FrostFsObjectId objectId, + FrostFsAttributePair[] attributes, + bool replaceAttributes) { - if (bytes.Length < 1024 + 64 || bytes.Length > 5900) - return; - - var patch = new byte[1024]; - for (int i = 0; i < patch.Length; i++) + byte[]? patch = null; + FrostFsRange range = new(); + if (patchPayload) { - patch[i] = 32; - } + patch = new byte[1024]; + for (int i = 0; i < patch.Length; i++) + { + patch[i] = 32; + } - var range = new FrostFsRange(64, (ulong)patch.Length); + range = new FrostFsRange(64, (ulong)patch.Length); + } var patchParams = new PrmObjectPatch( new FrostFsAddress(containerId, objectId), - payload: new MemoryStream(patch), + payload: new MemoryStream(patch ?? []), maxChunkLength: 1024, - range: range); + range: range, + replaceAttributes: replaceAttributes, + newAttributes: attributes); var newIbjId = await client.PatchObjectAsync(patchParams, default); var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, newIbjId), default); - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) + if (patchPayload) { - ms.Write(chunk.Value.Span); + var downloadedBytes = new byte[@object.Header.PayloadLength]; + MemoryStream ms = new(downloadedBytes); + + ReadOnlyMemory? chunk; + while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) + { + ms.Write(chunk.Value.Span); + } + + for (int i = 0; i < (int)range.Offset; i++) + Assert.Equal(downloadedBytes[i], bytes[i]); + + var rangeEnd = range.Offset + range.Length; + + for (int i = (int)range.Offset; i < (int)rangeEnd; i++) + Assert.Equal(downloadedBytes[i], patch[i - (int)range.Offset]); + + for (int i = (int)rangeEnd; i < bytes.Length; i++) + Assert.Equal(downloadedBytes[i], bytes[i]); } - for (int i = 0; i < (int)range.Offset; i++) - Assert.Equal(downloadedBytes[i], bytes[i]); - - var rangeEnd = range.Offset + range.Length; - - for (int i = (int)range.Offset; i < (int)rangeEnd; i++) - Assert.Equal(downloadedBytes[i], patch[i - (int)range.Offset]); - - for (int i = (int)rangeEnd; i < bytes.Length; i++) - Assert.Equal(downloadedBytes[i], bytes[i]); + if (attributes != null && attributes.Length > 0) + { + foreach (var newAttr in attributes) + { + var i = @object!.Header!.Attributes!.Count(p => p.Key == newAttr.Key && p.Value == newAttr.Value); + Assert.Equal(1, i); + } + } } - private async Task ValidateFilters(IFrostFSClient client, FrostFsContainerId containerId, FrostFsObjectId objectId, SplitId? splitId, ulong length) { var ecdsaKey = keyString.LoadWif(); @@ -318,7 +347,7 @@ public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase Assert.NotNull(objHeader); Assert.Equal(containerId.GetValue(), objHeader.ContainerId.GetValue()); - + Assert.Equal(expected.HeaderInfo!.OwnerId!.Value, objHeader.OwnerId!.Value); Assert.Equal(expected.HeaderInfo.Version!.Major, objHeader.Version!.Major); Assert.Equal(expected.HeaderInfo.Version!.Minor, objHeader.Version!.Minor); @@ -338,7 +367,6 @@ public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase Assert.Null(objHeader.Split); } - private static async Task CreateObjectServerCut(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) { var header = new FrostFsObjectHeader( diff --git a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs index e713b4a..ce1d06c 100644 --- a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs +++ b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs @@ -14,7 +14,7 @@ public class ContainerTest : ContainerTestsBase [Theory] [InlineData(1, "test", 0, 0)] - public void ReplicaToMessagelTest(int count, string selector, uint ecDataCount, uint ecParityCount) + public void ReplicaToMessagelTest(uint count, string selector, uint ecDataCount, uint ecParityCount) { FrostFsReplica replica = new() { @@ -26,7 +26,7 @@ public class ContainerTest : ContainerTestsBase Replica message = replica.ToMessage(); - Assert.Equal((uint)count, message.Count); + Assert.Equal(count, message.Count); Assert.Equal(selector, message.Selector); Assert.Equal(ecDataCount, message.EcDataCount); Assert.Equal(ecParityCount, message.EcParityCount); diff --git a/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs b/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs index 9957c63..46f7a3d 100644 --- a/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs +++ b/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs @@ -69,13 +69,13 @@ public class PlacementPolicyTests : NetworkTestsBase var rep0 = result.Replicas[0]; Assert.Equal(2u, rep0.EcDataCount); Assert.Equal(3u, rep0.EcParityCount); - Assert.Equal(4, rep0.Count); + Assert.Equal(4u, rep0.Count); Assert.Equal("selector1", rep0.Selector); var rep1 = result.Replicas[1]; Assert.Equal(5u, rep1.EcDataCount); Assert.Equal(6u, rep1.EcParityCount); - Assert.Equal(7, rep1.Count); + Assert.Equal(7u, rep1.Count); Assert.Equal("selector2", rep1.Selector); var f0 = result.Filters[0]; @@ -126,7 +126,7 @@ public class PlacementPolicyTests : NetworkTestsBase FrostFsReplica model = replica.ToModel(); - Assert.Equal(count, (uint)model.Count); + Assert.Equal(count, model.Count); Assert.Equal(selector, model.Selector); Assert.Equal(ecDataCount, model.EcDataCount); Assert.Equal(ecParityCount, model.EcParityCount); @@ -140,7 +140,7 @@ public class PlacementPolicyTests : NetworkTestsBase [InlineData(1, " ", 2, 3)] [InlineData(10, "!", 0, 0)] [InlineData(1, "123", 0, 0)] - public void ReplicaToMessagelTest(int count, string selector, uint ecDataCount, uint ecParityCount) + public void ReplicaToMessagelTest(uint count, string selector, uint ecDataCount, uint ecParityCount) { FrostFsReplica replica = new () { @@ -152,7 +152,7 @@ public class PlacementPolicyTests : NetworkTestsBase Replica message = replica.ToMessage(); - Assert.Equal((uint)count, message.Count); + Assert.Equal(count, message.Count); Assert.Equal(selector, message.Selector); Assert.Equal(ecDataCount, message.EcDataCount); Assert.Equal(ecParityCount, message.EcParityCount); @@ -235,11 +235,11 @@ public class PlacementPolicyTests : NetworkTestsBase Assert.Single(message.Filters); - var subfilter = message.Filters[0]; - Assert.Equal(name, subfilter.Name); - Assert.Equal(key, subfilter.Key); - Assert.Equal(operation, (int)subfilter.Op); - Assert.Equal(value, subfilter.Value); + var grpcFilter = message.Filters[0]; + Assert.Equal(name, grpcFilter.Name); + Assert.Equal(key, grpcFilter.Key); + Assert.Equal(operation, (int)grpcFilter.Op); + Assert.Equal(value, grpcFilter.Value); } [Fact] @@ -265,11 +265,11 @@ public class PlacementPolicyTests : NetworkTestsBase for (int i = 0; i < 3; i++) { - var subfilter = message.Filters[i]; - Assert.Equal(names[i], subfilter.Name); - Assert.Equal(keys[i], subfilter.Key); - Assert.Equal(operations[i], (int)subfilter.Op); - Assert.Equal(values[i], subfilter.Value); + var grpcFilter = message.Filters[i]; + Assert.Equal(names[i], grpcFilter.Name); + Assert.Equal(keys[i], grpcFilter.Key); + Assert.Equal(operations[i], (int)grpcFilter.Op); + Assert.Equal(values[i], grpcFilter.Value); } } diff --git a/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs b/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs index fdf7ffe..9833ae1 100644 --- a/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs +++ b/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs @@ -248,12 +248,12 @@ public class FilterDto Key ?? string.Empty, (int)Op, Value ?? string.Empty, - Filters != null ? Filters.Select(f => f.Filter).ToArray() : []); + Filters != null ? [.. Filters.Select(f => f.Filter)] : []); } public class ReplicaDto { - public int Count { get; set; } + public uint Count { get; set; } public string? Selector { get; set; } }