From eebba7665b77a059a593ba98a403242d111c2cbc Mon Sep 17 00:00:00 2001 From: Pavel Gross Date: Wed, 23 Apr 2025 00:30:34 +0300 Subject: [PATCH] [#67] Add unit tests Signed-off-by: Pavel Gross --- .../Models/Object/UploadInfo.cs | 2 - .../Models/Response/FrostFsResponseStatus.cs | 1 + src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs | 9 +- src/FrostFS.SDK.Tests/Unit/ContainerTest.cs | 22 ++ src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs | 29 ++ .../Unit/NetmapSnapshotTests.cs | 101 +++++ .../Unit/NetworkSettingsTests.cs | 68 ++++ src/FrostFS.SDK.Tests/Unit/NetworkTest.cs | 225 ----------- src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs | 69 ++++ src/FrostFS.SDK.Tests/Unit/ObjectTest.cs | 356 +++++++++++++++++- .../Unit/ObjectToolsTests.cs | 99 +++++ .../Unit/PlacementPolicyTests.cs | 307 +++++++++++++++ src/FrostFS.SDK.Tests/Unit/SignatureTests.cs | 39 ++ 13 files changed, 1094 insertions(+), 233 deletions(-) create mode 100644 src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs create mode 100644 src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs create mode 100644 src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs delete mode 100644 src/FrostFS.SDK.Tests/Unit/NetworkTest.cs create mode 100644 src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs create mode 100644 src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs create mode 100644 src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs create mode 100644 src/FrostFS.SDK.Tests/Unit/SignatureTests.cs diff --git a/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs b/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs index 748fdfe..ca96c7d 100644 --- a/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs +++ b/src/FrostFS.SDK.Client/Models/Object/UploadInfo.cs @@ -1,5 +1,3 @@ -using FrostFS.SDK; - namespace FrostFS.SDK.Client; public readonly struct ObjectPartInfo(long offset, int length, FrostFsObjectId objectId) : System.IEquatable diff --git a/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs b/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs index 9e1a846..7b003e7 100644 --- a/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs +++ b/src/FrostFS.SDK.Client/Models/Response/FrostFsResponseStatus.cs @@ -3,6 +3,7 @@ namespace FrostFS.SDK; public class FrostFsResponseStatus(FrostFsStatusCode code, string? message = null, string? details = null) { public FrostFsStatusCode Code { get; set; } = code; + public string Message { get; set; } = message ?? string.Empty; public string Details { get; set; } = details ?? string.Empty; diff --git a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs index f33b9a1..772f92e 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs @@ -41,6 +41,8 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) public Collection RangeHashResponses { get; } = []; + public Action? Callback; + public override Mock GetMock() { var mock = new Mock(); @@ -165,9 +167,14 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) It.IsAny())) .Returns((PutSingleRequest r, Metadata m, DateTime? dt, CancellationToken ct) => { + Callback?.Invoke(); Verifier.CheckRequest(r); - PutSingleRequests.Add(r); + var req = r.Clone(); + + // Clone method does not clone the payload but keeps a reference + req.Body.Object.Payload = ByteString.CopyFrom(r.Body.Object.Payload.ToByteArray()); + PutSingleRequests.Add(req); return new AsyncUnaryCall( Task.FromResult(putSingleResponse), diff --git a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs index cf4bdfa..e713b4a 100644 --- a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs +++ b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using FrostFS.Netmap; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Mappers.GRPC; using FrostFS.SDK.Cryptography; @@ -10,6 +11,27 @@ namespace FrostFS.SDK.Tests.Unit; [SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] public class ContainerTest : ContainerTestsBase { + [Theory] + [InlineData(1, "test", 0, 0)] + + public void ReplicaToMessagelTest(int count, string selector, uint ecDataCount, uint ecParityCount) + { + FrostFsReplica replica = new() + { + Count = count, + Selector = selector, + EcDataCount = ecDataCount, + EcParityCount = ecParityCount + }; + + Replica message = replica.ToMessage(); + + Assert.Equal((uint)count, message.Count); + Assert.Equal(selector, message.Selector); + Assert.Equal(ecDataCount, message.EcDataCount); + Assert.Equal(ecParityCount, message.EcParityCount); + } + [Fact] public async void CreateContainerTest() { diff --git a/src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs b/src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs new file mode 100644 index 0000000..3a71d78 --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/MetaheaderTests.cs @@ -0,0 +1,29 @@ +using FrostFS.SDK.Client; +using FrostFS.SDK.Client.Mappers.GRPC; + +namespace FrostFS.SDK.Tests.Unit; + +public class MetaheaderTests +{ + [Theory] + [InlineData(2, 13, 1, 1)] + [InlineData(200, 0, 1000000, 8)] + public void MetaheaderTest(int major, int minor, int epoch, int ttl) + { + MetaHeader metaHeader = new MetaHeader( + new FrostFsVersion( + major: 2, + minor: 13 + ), + epoch: 0, + ttl: 2 + ); + + var result = metaHeader.ToMessage(); + + Assert.Equal((ulong)metaHeader.Epoch, result.Epoch); + Assert.Equal((ulong)metaHeader.Ttl, result.Ttl); + Assert.Equal((ulong)metaHeader.Version.Major, result.Version.Major); + Assert.Equal((ulong)metaHeader.Version.Minor, result.Version.Minor); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs b/src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs new file mode 100644 index 0000000..233e12e --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/NetmapSnapshotTests.cs @@ -0,0 +1,101 @@ +using FrostFS.Netmap; +using FrostFS.SDK.Client; + +using Google.Protobuf; + +namespace FrostFS.SDK.Tests.Unit; + +public class NetmapSnapshotTests : NetworkTestsBase +{ + [Theory] + [InlineData(false)] + [InlineData(true)] + + public async void NetmapSnapshotTest(bool useContext) + { + var body = new NetmapSnapshotResponse.Types.Body + { + Netmap = new Netmap.Netmap { Epoch = 99 } + }; + + var nodeInfo1 = new NodeInfo + { + State = NodeInfo.Types.State.Online, + PublicKey = ByteString.CopyFrom([1, 2, 3]) + }; + + nodeInfo1.Addresses.Add("address1"); + nodeInfo1.Addresses.Add("address2"); + nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); + nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); + + var nodeInfo2 = new NodeInfo + { + State = NodeInfo.Types.State.Offline, + PublicKey = ByteString.CopyFrom([3, 4, 5]) + }; + + nodeInfo2.Addresses.Add("address3"); + nodeInfo2.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key3", Value = "value3" }); + + body.Netmap.Nodes.Add(nodeInfo1); + body.Netmap.Nodes.Add(nodeInfo2); + + Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body }; + + var ctx = useContext + ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) + : default; + + var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); + + var result = await GetClient(DefaultSettings).GetNetmapSnapshotAsync(ctx); + + var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); + + Assert.NotNull(result); + + Assert.Equal(99u, result.Epoch); + Assert.Equal(2, result.NodeInfoCollection.Count); + + var node1 = result.NodeInfoCollection[0]; + Assert.Equal(NodeState.Online, node1.State); + Assert.Equal(2, node1.Addresses.Count); + Assert.Equal("address1", node1.Addresses.ElementAt(0)); + Assert.Equal("address2", node1.Addresses.ElementAt(1)); + + Assert.Equal(2, node1.Attributes.Count); + + Assert.Equal("key1", node1.Attributes.ElementAt(0).Key); + Assert.Equal("value1", node1.Attributes.ElementAt(0).Value); + Assert.Equal("key2", node1.Attributes.ElementAt(1).Key); + Assert.Equal("value2", node1.Attributes.ElementAt(1).Value); + + var node2 = result.NodeInfoCollection[1]; + Assert.Equal(NodeState.Offline, node2.State); + Assert.Single(node2.Addresses); + Assert.Equal("address3", node2.Addresses.ElementAt(0)); + + Assert.Single(node2.Attributes); + + Assert.Equal("key3", node2.Attributes.ElementAt(0).Key); + Assert.Equal("value3", node2.Attributes.ElementAt(0).Value); + + if (useContext) + { + Assert.NotNull(Mocker.NetmapSnapshotRequest); + Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders); + + Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); + Assert.NotNull(Mocker.DateTime); + Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); + Assert.True(Mocker.DateTime.Value <= validTimeoutTo); + } + else + { + Assert.NotNull(Mocker.NetmapSnapshotRequest); + Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders); + Assert.Null(Mocker.DateTime); + } + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs b/src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs new file mode 100644 index 0000000..3f3fb2f --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/NetworkSettingsTests.cs @@ -0,0 +1,68 @@ +using System.Diagnostics.CodeAnalysis; +using FrostFS.SDK.Client; + +namespace FrostFS.SDK.Tests.Unit; + +[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] +public class NetworkSettingsTests : NetworkTestsBase +{ + [Theory] + [InlineData(false)] + [InlineData(true)] + public async void NetworkSettingsTest(bool useContext) + { + Mocker.Parameters.Add("AuditFee", [1]); + Mocker.Parameters.Add("BasicIncomeRate", [2]); + Mocker.Parameters.Add("ContainerFee", [3]); + Mocker.Parameters.Add("ContainerAliasFee", [4]); + Mocker.Parameters.Add("EpochDuration", [5]); + Mocker.Parameters.Add("InnerRingCandidateFee", [6]); + Mocker.Parameters.Add("MaxECDataCount", [7]); + Mocker.Parameters.Add("MaxECParityCount", [8]); + Mocker.Parameters.Add("MaxObjectSize", [9]); + Mocker.Parameters.Add("WithdrawFee", [10]); + Mocker.Parameters.Add("HomomorphicHashingDisabled", [1]); + Mocker.Parameters.Add("MaintenanceModeAllowed", [1]); + + var ctx = useContext + ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) + : default; + + var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); + + var result = await GetClient(DefaultSettings).GetNetworkSettingsAsync(ctx); + + var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); + + Assert.NotNull(result); + + Assert.Equal(Mocker.Parameters["AuditFee"], [(byte)result.AuditFee]); + Assert.Equal(Mocker.Parameters["BasicIncomeRate"], [(byte)result.BasicIncomeRate]); + Assert.Equal(Mocker.Parameters["ContainerFee"], [(byte)result.ContainerFee]); + Assert.Equal(Mocker.Parameters["ContainerAliasFee"], [(byte)result.ContainerAliasFee]); + Assert.Equal(Mocker.Parameters["EpochDuration"], [(byte)result.EpochDuration]); + Assert.Equal(Mocker.Parameters["InnerRingCandidateFee"], [(byte)result.InnerRingCandidateFee]); + Assert.Equal(Mocker.Parameters["MaxECDataCount"], [(byte)result.MaxECDataCount]); + Assert.Equal(Mocker.Parameters["MaxECParityCount"], [(byte)result.MaxECParityCount]); + Assert.Equal(Mocker.Parameters["MaxObjectSize"], [(byte)result.MaxObjectSize]); + Assert.Equal(Mocker.Parameters["WithdrawFee"], [(byte)result.WithdrawFee]); + + Assert.True(result.HomomorphicHashingDisabled); + Assert.True(result.MaintenanceModeAllowed); + + if (useContext) + { + Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); + Assert.NotNull(Mocker.DateTime); + + Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); + Assert.True(Mocker.DateTime.Value <= validTimeoutTo); + } + else + { + Assert.NotNull(Mocker.NetworkInfoRequest); + Assert.Empty(Mocker.NetworkInfoRequest.MetaHeader.XHeaders); + Assert.Null(Mocker.DateTime); + } + } +} diff --git a/src/FrostFS.SDK.Tests/Unit/NetworkTest.cs b/src/FrostFS.SDK.Tests/Unit/NetworkTest.cs deleted file mode 100644 index b1146f7..0000000 --- a/src/FrostFS.SDK.Tests/Unit/NetworkTest.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using FrostFS.Netmap; -using FrostFS.SDK.Client; - -using Google.Protobuf; - -namespace FrostFS.SDK.Tests.Unit; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -public class NetworkTest : NetworkTestsBase -{ - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void NetworkSettingsTest(bool useContext) - { - Mocker.Parameters.Add("AuditFee", [1]); - Mocker.Parameters.Add("BasicIncomeRate", [2]); - Mocker.Parameters.Add("ContainerFee", [3]); - Mocker.Parameters.Add("ContainerAliasFee", [4]); - Mocker.Parameters.Add("EpochDuration", [5]); - Mocker.Parameters.Add("InnerRingCandidateFee", [6]); - Mocker.Parameters.Add("MaxECDataCount", [7]); - Mocker.Parameters.Add("MaxECParityCount", [8]); - Mocker.Parameters.Add("MaxObjectSize", [9]); - Mocker.Parameters.Add("WithdrawFee", [10]); - Mocker.Parameters.Add("HomomorphicHashingDisabled", [1]); - Mocker.Parameters.Add("MaintenanceModeAllowed", [1]); - - var ctx = useContext - ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) - : default; - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient(DefaultSettings).GetNetworkSettingsAsync(ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - - Assert.Equal(Mocker.Parameters["AuditFee"], [(byte)result.AuditFee]); - Assert.Equal(Mocker.Parameters["BasicIncomeRate"], [(byte)result.BasicIncomeRate]); - Assert.Equal(Mocker.Parameters["ContainerFee"], [(byte)result.ContainerFee]); - Assert.Equal(Mocker.Parameters["ContainerAliasFee"], [(byte)result.ContainerAliasFee]); - Assert.Equal(Mocker.Parameters["EpochDuration"], [(byte)result.EpochDuration]); - Assert.Equal(Mocker.Parameters["InnerRingCandidateFee"], [(byte)result.InnerRingCandidateFee]); - Assert.Equal(Mocker.Parameters["MaxECDataCount"], [(byte)result.MaxECDataCount]); - Assert.Equal(Mocker.Parameters["MaxECParityCount"], [(byte)result.MaxECParityCount]); - Assert.Equal(Mocker.Parameters["MaxObjectSize"], [(byte)result.MaxObjectSize]); - Assert.Equal(Mocker.Parameters["WithdrawFee"], [(byte)result.WithdrawFee]); - - Assert.True(result.HomomorphicHashingDisabled); - Assert.True(result.MaintenanceModeAllowed); - - if (useContext) - { - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - } - else - { - Assert.NotNull(Mocker.NetworkInfoRequest); - Assert.Empty(Mocker.NetworkInfoRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - - public async void NetmapSnapshotTest(bool useContext) - { - var body = new NetmapSnapshotResponse.Types.Body - { - Netmap = new Netmap.Netmap { Epoch = 99 } - }; - - var nodeInfo1 = new NodeInfo - { - State = NodeInfo.Types.State.Online, - PublicKey = ByteString.CopyFrom([1, 2, 3]) - }; - - nodeInfo1.Addresses.Add("address1"); - nodeInfo1.Addresses.Add("address2"); - nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); - nodeInfo1.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); - - var nodeInfo2 = new NodeInfo - { - State = NodeInfo.Types.State.Offline, - PublicKey = ByteString.CopyFrom([3, 4, 5]) - }; - - nodeInfo2.Addresses.Add("address3"); - nodeInfo2.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key3", Value = "value3" }); - - body.Netmap.Nodes.Add(nodeInfo1); - body.Netmap.Nodes.Add(nodeInfo2); - - Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body }; - - var ctx = useContext - ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) - : default; - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient(DefaultSettings).GetNetmapSnapshotAsync(ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - - Assert.Equal(99u, result.Epoch); - Assert.Equal(2, result.NodeInfoCollection.Count); - - var node1 = result.NodeInfoCollection[0]; - Assert.Equal(NodeState.Online, node1.State); - Assert.Equal(2, node1.Addresses.Count); - Assert.Equal("address1", node1.Addresses.ElementAt(0)); - Assert.Equal("address2", node1.Addresses.ElementAt(1)); - - Assert.Equal(2, node1.Attributes.Count); - - Assert.Equal("key1", node1.Attributes.ElementAt(0).Key); - Assert.Equal("value1", node1.Attributes.ElementAt(0).Value); - Assert.Equal("key2", node1.Attributes.ElementAt(1).Key); - Assert.Equal("value2", node1.Attributes.ElementAt(1).Value); - - var node2 = result.NodeInfoCollection[1]; - Assert.Equal(NodeState.Offline, node2.State); - Assert.Single(node2.Addresses); - Assert.Equal("address3", node2.Addresses.ElementAt(0)); - - Assert.Single(node2.Attributes); - - Assert.Equal("key3", node2.Attributes.ElementAt(0).Key); - Assert.Equal("value3", node2.Attributes.ElementAt(0).Value); - - if (useContext) - { - Assert.NotNull(Mocker.NetmapSnapshotRequest); - Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders); - - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - } - else - { - Assert.NotNull(Mocker.NetmapSnapshotRequest); - Assert.Empty(Mocker.NetmapSnapshotRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public async void NodeInfoTest(bool useContext) - { - var body = new LocalNodeInfoResponse.Types.Body - { - NodeInfo = new NodeInfo() - { - State = NodeInfo.Types.State.Online, - PublicKey = ByteString.CopyFrom([1, 2, 3]) - }, - Version = new Refs.Version { Major = 2, Minor = 12 } - }; - - body.NodeInfo.Addresses.Add("address1"); - body.NodeInfo.Addresses.Add("address2"); - body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); - body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); - - Mocker.NodeInfoResponse = new LocalNodeInfoResponse { Body = body }; - - var ctx = useContext - ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) - : default; - - var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); - - var result = await GetClient(DefaultSettings).GetNodeInfoAsync(ctx); - - var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); - - Assert.NotNull(result); - - Assert.Equal(NodeState.Online, result.State); - - Assert.Equal(2, result.Addresses.Count); - Assert.Equal("address1", result.Addresses.ElementAt(0)); - Assert.Equal("address2", result.Addresses.ElementAt(1)); - - Assert.Equal(2, result.Attributes.Count); - Assert.Equal("value1", result.Attributes["key1"]); - Assert.Equal("value2", result.Attributes["key2"]); - - Assert.NotNull(Mocker.LocalNodeInfoRequest); - if (useContext) - { - Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders); - Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); - Assert.NotNull(Mocker.DateTime); - - Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); - Assert.True(Mocker.DateTime.Value <= validTimeoutTo); - } - else - { - Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders); - Assert.Null(Mocker.DateTime); - } - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs b/src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs new file mode 100644 index 0000000..6449beb --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/NodeInfoTests.cs @@ -0,0 +1,69 @@ +using FrostFS.Netmap; +using FrostFS.SDK.Client; +using Google.Protobuf; + +namespace FrostFS.SDK.Tests.Unit; + +public class NodeInfoTests : NetworkTestsBase +{ + [Theory] + [InlineData(false)] + [InlineData(true)] + public async void NodeInfoTest(bool useContext) + { + var body = new LocalNodeInfoResponse.Types.Body + { + NodeInfo = new NodeInfo() + { + State = NodeInfo.Types.State.Online, + PublicKey = ByteString.CopyFrom([1, 2, 3]) + }, + Version = new Refs.Version { Major = 2, Minor = 12 } + }; + + body.NodeInfo.Addresses.Add("address1"); + body.NodeInfo.Addresses.Add("address2"); + body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key1", Value = "value1" }); + body.NodeInfo.Attributes.Add(new NodeInfo.Types.Attribute { Key = "key2", Value = "value2" }); + + Mocker.NodeInfoResponse = new LocalNodeInfoResponse { Body = body }; + + var ctx = useContext + ? new CallContext(TimeSpan.FromSeconds(20), Mocker.CancellationTokenSource.Token) + : default; + + var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20); + + var result = await GetClient(DefaultSettings).GetNodeInfoAsync(ctx); + + var validTimeoutTo = DateTime.UtcNow.AddSeconds(20); + + Assert.NotNull(result); + + Assert.Equal(NodeState.Online, result.State); + + Assert.Equal(2, result.Addresses.Count); + Assert.Equal("address1", result.Addresses.ElementAt(0)); + Assert.Equal("address2", result.Addresses.ElementAt(1)); + + Assert.Equal(2, result.Attributes.Count); + Assert.Equal("value1", result.Attributes["key1"]); + Assert.Equal("value2", result.Attributes["key2"]); + + Assert.NotNull(Mocker.LocalNodeInfoRequest); + if (useContext) + { + Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders); + Assert.Equal(Mocker.CancellationTokenSource.Token, Mocker.CancellationToken); + Assert.NotNull(Mocker.DateTime); + + Assert.True(Mocker.DateTime.Value >= validTimeoutFrom); + Assert.True(Mocker.DateTime.Value <= validTimeoutTo); + } + else + { + Assert.Empty(Mocker.LocalNodeInfoRequest.MetaHeader.XHeaders); + Assert.Null(Mocker.DateTime); + } + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs b/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs index 7a595e6..985f02e 100644 --- a/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs +++ b/src/FrostFS.SDK.Tests/Unit/ObjectTest.cs @@ -6,8 +6,9 @@ using System.Text; using FrostFS.Refs; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Mappers.GRPC; - +using FrostFS.SDK.Cryptography; using Google.Protobuf; +using Org.BouncyCastle.Utilities; namespace FrostFS.SDK.Tests.Unit; @@ -60,7 +61,7 @@ public class ObjectTest : ObjectTestsBase var param = new PrmObjectClientCutPut( Mocker.ObjectHeader, payload: new MemoryStream(bytes), - bufferMaxSize: 1024); + bufferMaxSize: blockSize); Random rnd = new(); @@ -87,7 +88,7 @@ public class ObjectTest : ObjectTestsBase // PART1 Assert.Equal(blockSize, objects[0].Payload.Length); - Assert.True(bytes.AsMemory(0, blockSize).ToArray().SequenceEqual(objects[0].Payload)); + Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); Assert.NotNull(objects[0].Header.Split.SplitId); Assert.Null(objects[0].Header.Split.Previous); @@ -96,7 +97,7 @@ public class ObjectTest : ObjectTestsBase // PART2 Assert.Equal(blockSize, objects[1].Payload.Length); - Assert.True(bytes.AsMemory(blockSize, blockSize).ToArray().SequenceEqual(objects[1].Payload)); + Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); Assert.True(objects[1].Header.Attributes.Count == 0); @@ -104,7 +105,7 @@ public class ObjectTest : ObjectTestsBase // last part Assert.Equal(bytes.Length % blockSize, objects[2].Payload.Length); - Assert.True(bytes.AsMemory(2 * blockSize).ToArray().SequenceEqual(objects[2].Payload)); + Assert.Equal(bytes.AsMemory(2 * blockSize).ToArray(), objects[2].Payload); Assert.NotNull(objects[3].Header.Split.Parent); Assert.NotNull(objects[3].Header.Split.ParentHeader); @@ -128,6 +129,351 @@ public class ObjectTest : ObjectTestsBase Assert.Equal(result.Value, modelObjId.ToString()); } + [Fact] + public async void ClientCutWithInterruptionOnFirstPartTest() + { + NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); + + var blockSize = 2560; + byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); + var fileLength = bytes.Length; + + var splitId = Guid.NewGuid(); + var progress = new UploadProgressInfo(splitId); + + var param = new PrmObjectClientCutPut( + Mocker.ObjectHeader, + payload: new MemoryStream(bytes), + bufferMaxSize: blockSize, + progress: progress); + + Random rnd = new(); + + Collection objIds = new([new byte[32], new byte[32], new byte[32]]); + rnd.NextBytes(objIds.ElementAt(0)); + rnd.NextBytes(objIds.ElementAt(1)); + rnd.NextBytes(objIds.ElementAt(2)); + + foreach (var objId in objIds) + Mocker.ResultObjectIds!.Add(objId); + + int sentBlockCount = 0; + Mocker.Callback = () => + { + if (++sentBlockCount == 1) + throw new FrostFsException("some error"); + }; + + bool gotException = false; + try + { + var result = await GetClient().PutClientCutObjectAsync(param, default); + } + catch (FrostFsException ex) + { + if (ex.Message == "some error") + gotException = true; + } + + Assert.True(gotException); + + var singleObjects = Mocker.PutSingleRequests.ToArray(); + + Assert.Empty(singleObjects); + } + + [Fact] + public async void ClientCutWithInterruptionOnMiddlePartTest() + { + NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); + + var blockSize = 2560; + byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); + var fileLength = bytes.Length; + + var splitId = Guid.Parse("67e4bbe9-86ca-474d-9385-6569ce89db61"); + var progress = new UploadProgressInfo(splitId); + + var param = new PrmObjectClientCutPut( + Mocker.ObjectHeader, + payload: new MemoryStream(bytes), + bufferMaxSize: blockSize, + progress: progress); + + Random rnd = new(); + + Collection objIds = new([new byte[32], new byte[32], new byte[32]]); + rnd.NextBytes(objIds.ElementAt(0)); + rnd.NextBytes(objIds.ElementAt(1)); + rnd.NextBytes(objIds.ElementAt(2)); + + foreach (var objId in objIds) + Mocker.ResultObjectIds!.Add(objId); + + int sentBlockCount = 0; + Mocker.Callback = () => + { + if (++sentBlockCount == 2) + throw new FrostFsException("some error"); + }; + + bool gotException = false; + try + { + _ = await GetClient().PutClientCutObjectAsync(param, default); + } + catch (FrostFsException ex) + { + if (ex.Message == "some error") + gotException = true; + } + + Assert.True(gotException); + + var singleObjects = Mocker.PutSingleRequests.ToArray(); + + Assert.NotNull(Mocker.ClientStreamWriter?.Messages); + + var objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); + + Assert.Single(objects); + + Assert.Single(progress.GetParts()); + + var part = progress.GetPart(0); + Assert.Equal(0, part.Offset); + Assert.Equal(2560, part.Length); + + // PART1 + Assert.Equal(blockSize, objects[0].Payload.Length); + Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); + + Assert.NotNull(objects[0].Header.Split.SplitId); + Assert.Null(objects[0].Header.Split.Previous); + Assert.True(objects[0].Header.Attributes.Count == 0); + Assert.Null(objects[0].Header.Split.Parent); + + // resume uploading + sentBlockCount = 10; + + var result = await GetClient().PutClientCutObjectAsync(param, default); + + singleObjects = Mocker.PutSingleRequests.ToArray(); + + Assert.NotNull(Mocker.ClientStreamWriter?.Messages); + + objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); + + Assert.Equal(4, objects.Length); + + // PART1 + Assert.Equal(blockSize, objects[0].Payload.Length); + Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); + + Assert.NotNull(objects[0].Header.Split.SplitId); + Assert.Null(objects[0].Header.Split.Previous); + Assert.True(objects[0].Header.Attributes.Count == 0); + Assert.Null(objects[0].Header.Split.Parent); + + // PART2 + Assert.Equal(blockSize, objects[1].Payload.Length); + Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); + + Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); + Assert.True(objects[1].Header.Attributes.Count == 0); + Assert.Null(objects[1].Header.Split.Parent); + + // last part + Assert.Equal(bytes.Length % blockSize, objects[2].Payload.Length); + Assert.Equal(bytes.AsMemory(2 * blockSize).ToArray(), objects[2].Payload); + + Assert.NotNull(objects[3].Header.Split.Parent); + Assert.NotNull(objects[3].Header.Split.ParentHeader); + Assert.NotNull(objects[3].Header.Split.ParentSignature); + Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); + Assert.True(objects[2].Header.Attributes.Count == 0); + + // link object + Assert.Equal(objects[2].Header.Split.Parent, objects[3].Header.Split.Parent); + Assert.Equal(objects[2].Header.Split.ParentHeader, objects[3].Header.Split.ParentHeader); + Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); + Assert.Equal(0, (int)objects[3].Header.PayloadLength); + Assert.True(objects[3].Header.Attributes.Count == 0); + + Assert.Single(objects[3].Header.Split.ParentHeader.Attributes); + Assert.Equal("k", objects[3].Header.Split.ParentHeader.Attributes[0].Key); + Assert.Equal("v", objects[3].Header.Split.ParentHeader.Attributes[0].Value); + + var modelObjId = FrostFsObjectId.FromHash(objects[3].Header.Split.Parent.Value.ToByteArray()); + + Assert.Equal(result.Value, modelObjId.ToString()); + + Assert.Equal(3, progress.GetParts().Count); + part = progress.GetPart(0); + Assert.Equal(0, part.Offset); + Assert.Equal(2560, part.Length); + + part = progress.GetPart(1); + Assert.Equal(2560, part.Offset); + Assert.Equal(2560, part.Length); + + part = progress.GetPart(2); + Assert.Equal(2 * 2560, part.Offset); + Assert.Equal(1620, part.Length); + } + + [Fact] + public async void ClientCutWithInterruptionOnLastPartTest() + { + NetworkMocker.Parameters.Add("MaxObjectSize", [0x0, 0xa]); + + var blockSize = 2560; + byte[] bytes = File.ReadAllBytes(@".\..\..\..\cat.jpg"); + var fileLength = bytes.Length; + + var splitId = Guid.Parse("67e4bbe9-86ca-474d-9385-6569ce89db61"); + var progress = new UploadProgressInfo(splitId); + + var param = new PrmObjectClientCutPut( + Mocker.ObjectHeader, + payload: new MemoryStream(bytes), + bufferMaxSize: blockSize, + progress: progress); + + Random rnd = new(); + + Collection objIds = new([new byte[32], new byte[32], new byte[32]]); + rnd.NextBytes(objIds.ElementAt(0)); + rnd.NextBytes(objIds.ElementAt(1)); + rnd.NextBytes(objIds.ElementAt(2)); + + foreach (var objId in objIds) + Mocker.ResultObjectIds!.Add(objId); + + int sentBlockCount = 0; + Mocker.Callback = () => + { + if (++sentBlockCount == 3) + throw new FrostFsException("some error"); + }; + + bool gotException = false; + try + { + _ = await GetClient().PutClientCutObjectAsync(param, default); + } + catch (FrostFsException ex) + { + if (ex.Message == "some error") + gotException = true; + } + + Assert.True(gotException); + + var singleObjects = Mocker.PutSingleRequests.ToArray(); + + Assert.NotNull(Mocker.ClientStreamWriter?.Messages); + + var objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); + + Assert.Equal(2, objects.Length); + + Assert.Equal(2, progress.GetParts().Count); + + var part = progress.GetPart(0); + Assert.Equal(0, part.Offset); + Assert.Equal(2560, part.Length); + + part = progress.GetPart(1); + Assert.Equal(2560, part.Offset); + Assert.Equal(2560, part.Length); + + // PART1 + Assert.Equal(blockSize, objects[0].Payload.Length); + Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); + + Assert.NotNull(objects[0].Header.Split.SplitId); + Assert.Null(objects[0].Header.Split.Previous); + Assert.True(objects[0].Header.Attributes.Count == 0); + Assert.Null(objects[0].Header.Split.Parent); + + // PART2 + Assert.Equal(blockSize, objects[1].Payload.Length); + Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); + + Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); + Assert.True(objects[1].Header.Attributes.Count == 0); + Assert.Null(objects[1].Header.Split.Parent); + + // resume uploading + sentBlockCount = 10; + + var result = await GetClient().PutClientCutObjectAsync(param, default); + + singleObjects = Mocker.PutSingleRequests.ToArray(); + + Assert.NotNull(Mocker.ClientStreamWriter?.Messages); + + objects = Mocker.PutSingleRequests.Select(o => o.Body.Object).ToArray(); + + Assert.Equal(4, objects.Length); + + // PART1 + Assert.Equal(blockSize, objects[0].Payload.Length); + Assert.Equal(bytes.AsMemory(0, blockSize).ToArray(), objects[0].Payload); + + Assert.NotNull(objects[0].Header.Split.SplitId); + Assert.Null(objects[0].Header.Split.Previous); + Assert.True(objects[0].Header.Attributes.Count == 0); + Assert.Null(objects[0].Header.Split.Parent); + + // PART2 + Assert.Equal(blockSize, objects[1].Payload.Length); + Assert.Equal(bytes.AsMemory(blockSize, blockSize).ToArray(), objects[1].Payload); + + Assert.Equal(objects[0].Header.Split.SplitId, objects[1].Header.Split.SplitId); + Assert.True(objects[1].Header.Attributes.Count == 0); + Assert.Null(objects[1].Header.Split.Parent); + + // last part + Assert.Equal(bytes.Length % blockSize, objects[2].Payload.Length); + Assert.Equal(bytes.AsMemory(2 * blockSize).ToArray(), objects[2].Payload); + + Assert.NotNull(objects[3].Header.Split.Parent); + Assert.NotNull(objects[3].Header.Split.ParentHeader); + Assert.NotNull(objects[3].Header.Split.ParentSignature); + Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); + Assert.True(objects[2].Header.Attributes.Count == 0); + + // link object + Assert.Equal(objects[2].Header.Split.Parent, objects[3].Header.Split.Parent); + Assert.Equal(objects[2].Header.Split.ParentHeader, objects[3].Header.Split.ParentHeader); + Assert.Equal(objects[2].Header.Split.SplitId, objects[3].Header.Split.SplitId); + Assert.Equal(0, (int)objects[3].Header.PayloadLength); + Assert.True(objects[3].Header.Attributes.Count == 0); + + Assert.Single(objects[3].Header.Split.ParentHeader.Attributes); + Assert.Equal("k", objects[3].Header.Split.ParentHeader.Attributes[0].Key); + Assert.Equal("v", objects[3].Header.Split.ParentHeader.Attributes[0].Value); + + var modelObjId = FrostFsObjectId.FromHash(objects[3].Header.Split.Parent.Value.ToByteArray()); + + Assert.Equal(result.Value, modelObjId.ToString()); + + Assert.Equal(3, progress.GetParts().Count); + part = progress.GetPart(0); + Assert.Equal(0, part.Offset); + Assert.Equal(2560, part.Length); + + part = progress.GetPart(1); + Assert.Equal(2560, part.Offset); + Assert.Equal(2560, part.Length); + + part = progress.GetPart(2); + Assert.Equal(2 * 2560, part.Offset); + Assert.Equal(1620, part.Length); + } + [Fact] public async void DeleteObject() { diff --git a/src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs b/src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs new file mode 100644 index 0000000..3c0245b --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/ObjectToolsTests.cs @@ -0,0 +1,99 @@ +using System.Security.Cryptography; +using System.Text; +using FrostFS.SDK.Client; +using FrostFS.SDK.Cryptography; + +namespace FrostFS.SDK.Tests.Unit; + +public class ObjectToolsTests +{ + internal readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; + + [Fact] + public void CalculateObjectIdTest() + { + var payload = Encoding.UTF8.GetBytes("testPayload"); + + var payloadHash = SHA256.HashData(payload); + + FrostFsContainerId containerId = new("test"); + FrostFsObjectHeader header = new(containerId); + + var ecdsaKey = key.LoadWif(); + var owner = FrostFsOwner.FromKey(ecdsaKey); + + var clientKey = new ClientKey(ecdsaKey); + + var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); + + Assert.NotNull(objId.Value); + Assert.Equal("HuAojwCYi62iUKr1FtSCCkMLLWv1uAnznF8iSb1bRV1N", objId.Value); + } + + [Fact] + public void CalculateObjectIdTest1() + { + var payload = Encoding.UTF8.GetBytes("testPayload"); + + var payloadHash = SHA256.HashData(payload); + var ecdsaKey = key.LoadWif(); + var owner = FrostFsOwner.FromKey(ecdsaKey); + + var clientKey = new ClientKey(ecdsaKey); + FrostFsContainerId containerId = new("test"); + FrostFsObjectHeader header = new(containerId, FrostFsObjectType.Regular, null, null, owner, new FrostFsVersion(2, 13)); + + var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); + + Assert.NotNull(objId.Value); + Assert.Equal("HuAojwCYi62iUKr1FtSCCkMLLWv1uAnznF8iSb1bRV1N", objId.Value); + } + + [Fact] + public void CalculateObjectIdWithAttrTest() + { + var payload = Encoding.UTF8.GetBytes("testPayload"); + + var payloadHash = SHA256.HashData(payload); + var ecdsaKey = key.LoadWif(); + var owner = FrostFsOwner.FromKey(ecdsaKey); + + var clientKey = new ClientKey(ecdsaKey); + FrostFsContainerId containerId = new("test"); + + FrostFsAttributePair[] attribs = [new("key", "val")]; + + FrostFsObjectHeader header = new(containerId, FrostFsObjectType.Regular, attribs, null, owner, new FrostFsVersion(2, 13)); + + var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); + + Assert.NotNull(objId.Value); + Assert.Equal("4zq5NYEbzkrfmdKne3GnpavE24gU2PnuV17ZExb9hcn3", objId.Value); + } + + [Fact] + public void CalculateObjectIdWithSplitIdTest() + { + var payload = Encoding.UTF8.GetBytes("testPayload"); + + var payloadHash = SHA256.HashData(payload); + var ecdsaKey = key.LoadWif(); + var owner = FrostFsOwner.FromKey(ecdsaKey); + + var clientKey = new ClientKey(ecdsaKey); + FrostFsContainerId containerId = new("test"); + + FrostFsAttributePair[] attribs = [new("key", "val")]; + + var guid = Guid.Parse("790a8d04-f5c3-4cd6-b46f-a78ee7e325f2"); + SplitId splitId = new(guid); + FrostFsSplit split = new (splitId); + + FrostFsObjectHeader header = new(containerId, FrostFsObjectType.Regular, attribs, split, owner, new FrostFsVersion(2, 13)); + + var objId = ObjectTools.CalculateObjectId(header, payloadHash, owner, new FrostFsVersion(2, 13), clientKey); + + Assert.NotNull(objId.Value); + Assert.Equal("HCYzsuXyfe5LmQzi58hPQxExGPAFv7dU5TzEACLxM1os", objId.Value); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs b/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs new file mode 100644 index 0000000..9957c63 --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/PlacementPolicyTests.cs @@ -0,0 +1,307 @@ +using System.Xml.Linq; +using FrostFS.Netmap; +using FrostFS.SDK.Client; +using Google.Protobuf.WellKnownTypes; + +namespace FrostFS.SDK.Tests.Unit; + +public class PlacementPolicyTests : NetworkTestsBase +{ + [Theory] + [InlineData(true, 1)] + [InlineData(true, 3)] + [InlineData(true, 5)] + [InlineData(false, 1)] + [InlineData(false, 3)] + [InlineData(false, 5)] + public void PlacementPolicySimpleFullTest(bool unique, uint backupFactor) + { + PlacementPolicy policy = new() + { + ContainerBackupFactor = backupFactor, + Unique = unique + }; + + var result = policy.ToModel(); + + Assert.Equal(backupFactor, result.BackupFactor); + Assert.Equal(unique, result.Unique); + Assert.Empty(result.Filters); + Assert.Empty(result.Replicas); + Assert.Empty(result.Selectors); + } + + [Fact] + public void PlacementPolicyFullTest() + { + PlacementPolicy policy = new() + { + ContainerBackupFactor = 3, + Unique = true + }; + + policy.Filters.AddRange( + [ + new () { Name = "filter1", Key = "filterKey1", Op =Operation.Eq, Value = "testValue1" }, + new () { Name = "filter2", Key = "filterKey2", Op =Operation.And, Value = "testValue2" } + ]); + + policy.Selectors.AddRange( + [ + new () { Name = "name1", Attribute = "attrib1", Clause = Clause.Same, Count = 5, Filter = "filter1" }, + new () { Name = "name2", Attribute = "attrib2", Clause = Clause.Distinct, Count = 4, Filter = "filter2" } + ]); + + policy.Replicas.AddRange( + [ + new () { EcDataCount = 2, EcParityCount = 3, Count = 4, Selector = "selector1"}, + new () { EcDataCount = 5, EcParityCount = 6, Count = 7, Selector = "selector2"}, + ]); + + var result = policy.ToModel(); + + Assert.Equal(3L, result.BackupFactor); + Assert.True(result.Unique); + Assert.Equal(2, result.Filters.Count); + Assert.Equal(2, result.Replicas.Length); + Assert.Equal(2, result.Selectors.Count); + + var rep0 = result.Replicas[0]; + Assert.Equal(2u, rep0.EcDataCount); + Assert.Equal(3u, rep0.EcParityCount); + Assert.Equal(4, 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("selector2", rep1.Selector); + + var f0 = result.Filters[0]; + Assert.Equal("filterKey1", f0.Key); + Assert.Equal("filter1", f0.Name); + Assert.Equal(1, f0.Operation); + Assert.Equal("testValue1", f0.Value); + + var f1 = result.Filters[1]; + Assert.Equal("filterKey2", f1.Key); + Assert.Equal("filter2", f1.Name); + Assert.Equal(8, f1.Operation); + Assert.Equal("testValue2", f1.Value); + + var s0 = result.Selectors[0]; + Assert.Equal("name1", s0.Name); + Assert.Equal("attrib1", s0.Attribute); + Assert.Equal(1, s0.Clause); + Assert.Equal(5L, s0.Count); + Assert.Equal("filter1", s0.Filter); + + var s1 = result.Selectors[1]; + Assert.Equal("name2", s1.Name); + Assert.Equal("attrib2", s1.Attribute); + Assert.Equal(2, s1.Clause); + Assert.Equal(4L, s1.Count); + Assert.Equal("filter2", s1.Filter); + } + + + [Theory] + [InlineData(1, "test" , 0, 0)] + [InlineData(1, "", 1000, 9999)] + [InlineData(1, "some long text to test reasonable length of the selector name", 100000000, 100000001)] + [InlineData(100, "test2", 1, 1)] + [InlineData(1, " ", 2, 3)] + [InlineData(10, "!", 0, 0)] + [InlineData(1, "123", 0, 0)] + public void ReplicaToModelTest(uint count, string selector, uint ecDataCount, uint ecParityCount) + { + Replica replica = new () + { + Count = count, + Selector = selector, + EcDataCount = ecDataCount, + EcParityCount = ecParityCount + }; + + FrostFsReplica model = replica.ToModel(); + + Assert.Equal(count, (uint)model.Count); + Assert.Equal(selector, model.Selector); + Assert.Equal(ecDataCount, model.EcDataCount); + Assert.Equal(ecParityCount, model.EcParityCount); + } + + [Theory] + [InlineData(1, "test", 0, 0)] + [InlineData(1, "", 1000, 9999)] + [InlineData(1, "some long text to test reasonable length of the selector name", 100000000, 100000001)] + [InlineData(100, "test2", 1, 1)] + [InlineData(1, " ", 2, 3)] + [InlineData(10, "!", 0, 0)] + [InlineData(1, "123", 0, 0)] + public void ReplicaToMessagelTest(int count, string selector, uint ecDataCount, uint ecParityCount) + { + FrostFsReplica replica = new () + { + Count = count, + Selector = selector, + EcDataCount = ecDataCount, + EcParityCount = ecParityCount + }; + + Replica message = replica.ToMessage(); + + Assert.Equal((uint)count, message.Count); + Assert.Equal(selector, message.Selector); + Assert.Equal(ecDataCount, message.EcDataCount); + Assert.Equal(ecParityCount, message.EcParityCount); + } + + [Theory] + [InlineData("test", 1, 2, "attribute", "filter")] + [InlineData("test", 0, 0, "longlonglonglonglonglonglonglonglonglonglonglonglong attribute", "longlonglonglonglonglonglonglonglonglonglonglonglong filter")] + [InlineData("test", 0, 1, "attribute", "filter")] + public void SelectorToMessageTest(string name, uint count, int clause, string attr, string filter) + { + FrostFsSelector selector = new (name) + { + Count = count, + Clause = clause, + Attribute = attr, + Filter = filter, + }; + + var message = selector.ToMessage(); + + Assert.Equal(name, message.Name); + Assert.Equal(count, message.Count); + Assert.Equal(clause, (int)message.Clause); + Assert.Equal(attr, message.Attribute); + Assert.Equal(filter, message.Filter); + + } + + [Theory] + [InlineData("test", 1, Clause.Same, "attribute", "filter")] + [InlineData("test", 0, Clause.Distinct, "longlonglonglonglonglonglonglonglonglonglonglonglong attribute", "longlonglonglonglonglonglonglonglonglonglonglonglong filter")] + public void SelectorToModelTest(string name, uint count, Clause clause, string attr, string filter) + { + Selector selector = new () + { + Name = name, + Count = count, + Clause = clause, + Attribute = attr, + Filter = filter + }; + + var model = selector.ToModel(); + + Assert.Equal(name, model.Name); + Assert.Equal(count, model.Count); + Assert.Equal((int)clause, model.Clause); + Assert.Equal(attr, model.Attribute); + Assert.Equal(filter, model.Filter); + } + + [Theory] + [InlineData("", "", 1, "")] + [InlineData("name", "key", 1, "val")] + [InlineData("longlonglonglonglonglonglonglonglonglonglonglonglong name", "longlonglonglonglonglonglonglonglonglonglonglonglong key", 10, "longlonglonglonglonglonglonglonglonglonglonglonglong val")] + public void FilterToMessageTest(string name, string key, int operation, string value) + { + FrostFsFilter filter = new (name, key, operation, value, []); + + var message = filter.ToMessage(); + + Assert.Equal(name, message.Name); + Assert.Equal(key, message.Key); + Assert.Equal(operation, (int)message.Op); + Assert.Equal(value, message.Value); + } + + [Theory] + [InlineData("", "", 1, "")] + [InlineData("name", "key", 2, "val")] + [InlineData("longlonglonglonglonglonglonglonglonglonglonglonglong name", "longlonglonglonglonglonglonglonglonglonglonglonglong key", 10, "longlonglonglonglonglonglonglonglonglonglonglonglong val")] + public void SubFilterToMessageTest(string name, string key, int operation, string value) + { + FrostFsFilter subFilter = new(name, key, operation, value, []); + + FrostFsFilter filter = new("name", "key", 1, "value", [subFilter]); + + var message = filter.ToMessage(); + + 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); + } + + [Fact] + public void SubFiltersToMessageTest() + { + string[] names = ["", "name1", "some pretty long name for name test"]; + string[] keys = ["", "key1", "some pretty long key for name test"]; + int[] operations = [1, 2, 10]; + string[] values = ["", "val1", "some pretty long value for name test"]; + + var subFilter = new FrostFsFilter[3]; + + for (int i = 0; i < 3; i++) + { + subFilter[i] = new FrostFsFilter(names[i], keys[i], operations[i], values[i], []); + } + + FrostFsFilter filter = new("name", "key", 1, "value", subFilter); + + var message = filter.ToMessage(); + + Assert.Equal(3, message.Filters.Count); + + 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); + } + } + + [Theory] + [InlineData("", "", Operation.Unspecified, "")] + [InlineData("name", "key", Operation.Unspecified, "val")] + [InlineData("name", "key", Operation.And, "val")] + [InlineData("name", "key", Operation.Eq, "val")] + [InlineData("name", "key", Operation.Le, "val")] + [InlineData("name", "key", Operation.Like, "val")] + [InlineData("name", "key", Operation.Ge, "val")] + [InlineData("name", "key", Operation.Gt, "val")] + [InlineData("name", "key", Operation.Lt, "val")] + [InlineData("name", "key", Operation.Ne, "val")] + [InlineData("name", "key", Operation.Not, "val")] + [InlineData("name", "key", Operation.Or, "val")] + [InlineData("longlonglonglonglonglonglonglonglonglonglonglonglong name", "longlonglonglonglonglonglonglonglonglonglonglonglong key", Operation.Like, "longlonglonglonglonglonglonglonglonglonglonglonglong val")] + public void FrostFsFilterToModelTest(string name, string key, Operation operation, string value) + { + Filter filter = new() + { + Name = name, + Key = key, + Op = operation, + Value = value + }; + + var model = filter.ToModel(); + + Assert.Equal(name, model.Name); + Assert.Equal(key, model.Key); + Assert.Equal((int)operation, model.Operation); + Assert.Equal(value, model.Value); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Unit/SignatureTests.cs b/src/FrostFS.SDK.Tests/Unit/SignatureTests.cs new file mode 100644 index 0000000..5062319 --- /dev/null +++ b/src/FrostFS.SDK.Tests/Unit/SignatureTests.cs @@ -0,0 +1,39 @@ +using System.Text; +using FrostFS.SDK.Client; +using FrostFS.SDK.Client.Mappers.GRPC; + +namespace FrostFS.SDK.Tests.Unit; + +public class SignatureTests +{ + [Theory] + [InlineData(Refs.SignatureScheme.EcdsaSha512)] + [InlineData(Refs.SignatureScheme.EcdsaRfc6979Sha256)] + [InlineData(Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect)] + + public void SignatureToMessageTest(Refs.SignatureScheme scheme) + { + var key = Encoding.UTF8.GetBytes("datafortest"); + var sign = Encoding.UTF8.GetBytes("signdatafortest"); + + var frostFsScheme = scheme switch + { + Refs.SignatureScheme.EcdsaRfc6979Sha256 => SignatureScheme.EcdsaRfc6979Sha256, + Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect => SignatureScheme.EcdsaRfc6979Sha256WalletConnect, + Refs.SignatureScheme.EcdsaSha512 => SignatureScheme.EcdsaSha512 + }; + + FrostFsSignature signature = new() + { + Key = key, + Scheme = frostFsScheme, + Sign = sign + }; + + var result = signature.ToMessage(); + + Assert.Equal(scheme, result.Scheme); + Assert.Equal(sign, result.Sign.ToByteArray()); + Assert.Equal(key, result.Key.ToByteArray()); + } +} \ No newline at end of file