diff --git a/README.md b/README.md index eccf0a2..add47ea 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ var placementPolicy = new FrostFsPlacementPolicy(true, new FrostFsReplica(1)); var createContainerParam = new PrmContainerCreate( new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1)))); -var containerId = await client.CreateContainerAsync(createContainerParam); +var containerId = await client.PutContainerAsync(createContainerParam); using var fileStream = File.OpenRead(@"C:\Users\Paul\Pictures\cat.jpeg"); diff --git a/src/FrostFS.SDK.Client/FrostFSClient.cs b/src/FrostFS.SDK.Client/FrostFSClient.cs index 27017fe..83f917e 100644 --- a/src/FrostFS.SDK.Client/FrostFSClient.cs +++ b/src/FrostFS.SDK.Client/FrostFSClient.cs @@ -212,9 +212,15 @@ public class FrostFSClient : IFrostFSClient return GetContainerService().ListContainersAsync(args, ctx); } + [Obsolete("Use PutContainerAsync method")] public Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx) { - return GetContainerService().CreateContainerAsync(args, ctx); + return GetContainerService().PutContainerAsync(args, ctx); + } + + public Task PutContainerAsync(PrmContainerCreate args, CallContext ctx) + { + return GetContainerService().PutContainerAsync(args, ctx); } public Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx) @@ -314,18 +320,6 @@ public class FrostFSClient : IFrostFSClient } #endregion - private async void CheckFrostFsVersionSupport(CallContext ctx) - { - var service = GetNetmapService(); - var localNodeInfo = await service.GetLocalNodeInfoAsync(ctx).ConfigureAwait(false); - - if (!localNodeInfo.Version.IsSupported(ClientCtx.Version)) - { - var msg = $"FrostFS {localNodeInfo.Version} is not supported."; - throw new FrostFsException(msg); - } - } - private CallInvoker? CreateInvoker() { CallInvoker? callInvoker = null; @@ -361,7 +355,7 @@ public class FrostFSClient : IFrostFSClient { var invoker = CreateInvoker(); - NetmapServiceClient = NetmapServiceClient ?? ( + NetmapServiceClient ??= ( invoker != null ? new NetmapServiceClient(invoker) : new NetmapServiceClient(ClientCtx.Channel)); @@ -378,7 +372,7 @@ public class FrostFSClient : IFrostFSClient { var invoker = CreateInvoker(); - SessionServiceClient = SessionServiceClient ?? ( + SessionServiceClient ??= ( invoker != null ? new SessionServiceClient(invoker) : new SessionServiceClient(ClientCtx.Channel)); @@ -387,7 +381,6 @@ public class FrostFSClient : IFrostFSClient } return SessionServiceProvider; - } private ApeManagerServiceProvider GetApeManagerService() @@ -396,7 +389,7 @@ public class FrostFSClient : IFrostFSClient { var invoker = CreateInvoker(); - ApeManagerServiceClient = ApeManagerServiceClient ?? ( + ApeManagerServiceClient ??= ( invoker != null ? new APEManagerServiceClient(invoker) : new APEManagerServiceClient(ClientCtx.Channel)); @@ -413,7 +406,7 @@ public class FrostFSClient : IFrostFSClient { var invoker = CreateInvoker(); - AccountingServiceClient = AccountingServiceClient ?? ( + AccountingServiceClient ??= ( invoker != null ? new AccountingServiceClient(invoker) : new AccountingServiceClient(ClientCtx.Channel)); @@ -430,7 +423,7 @@ public class FrostFSClient : IFrostFSClient { var invoker = CreateInvoker(); - ContainerServiceClient = ContainerServiceClient ?? ( + ContainerServiceClient ??= ( invoker != null ? new ContainerServiceClient(invoker) : new ContainerServiceClient(ClientCtx.Channel)); diff --git a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs index cce7364..594cc63 100644 --- a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs @@ -33,8 +33,11 @@ public interface IFrostFSClient IAsyncEnumerable ListContainersAsync(PrmContainerGetAll args, CallContext ctx); + [Obsolete("Use PutContainerAsync method")] Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx); + Task PutContainerAsync(PrmContainerCreate args, CallContext ctx); + Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx); #endregion diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs index 30410aa..ee1c8a4 100644 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs +++ b/src/FrostFS.SDK.Client/Mappers/Netmap/PlacementPolicy.cs @@ -17,9 +17,9 @@ public static class PlacementPolicyMapper return new FrostFsPlacementPolicy( placementPolicy.Unique, placementPolicy.ContainerBackupFactor, - new System.Collections.ObjectModel.Collection(placementPolicy.Selectors.Select(selector => selector.ToModel()).ToList()), - new System.Collections.ObjectModel.Collection(placementPolicy.Filters.Select(filter => filter.ToModel()).ToList()), - placementPolicy.Replicas.Select(replica => replica.ToModel()).ToArray() + [.. placementPolicy.Selectors.Select(s => s.ToModel())], + [.. placementPolicy.Filters.Select(f => f.ToModel())], + [.. placementPolicy.Replicas.Select(r => r.ToModel())] ); } } \ 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 bc415e1..cd7a0b7 100644 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs +++ b/src/FrostFS.SDK.Client/Mappers/Netmap/Replica.cs @@ -50,13 +50,15 @@ public static class PolicyMapper throw new ArgumentNullException(nameof(selector)); } - return new FrostFsSelector(selector.Name) + var model = new FrostFsSelector(selector.Name) { Count = selector.Count, Clause = (int)selector.Clause, Attribute = selector.Attribute, Filter = selector.Filter }; + + return model; } public static Filter ToMessage(this FrostFsFilter filter) @@ -86,6 +88,13 @@ public static class PolicyMapper throw new ArgumentNullException(nameof(filter)); } - return new FrostFsFilter(filter.Name, filter.Key, (int)filter.Op, filter.Value, filter.Filters.Select(f => f.ToModel()).ToArray()); + var model = new FrostFsFilter( + filter.Name, + filter.Key, + (int)filter.Op, + filter.Value, + [.. filter.Filters.Select(f => f.ToModel())]); + + return model; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs index cfec8fd..a28100e 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/ObjectAttributeMapper.cs @@ -8,11 +8,6 @@ public static class ObjectAttributeMapper { public static Header.Types.Attribute ToMessage(this FrostFsAttributePair attribute) { - if (attribute is null) - { - throw new ArgumentNullException(nameof(attribute)); - } - return new Header.Types.Attribute { Key = attribute.Key, diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs index ebd3c20..11ee079 100644 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs +++ b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsFilter.cs @@ -1,4 +1,7 @@ -namespace FrostFS.SDK; +using System.Linq; +using FrostFS.Netmap; + +namespace FrostFS.SDK; public class FrostFsFilter(string name, string key, int operation, string value, FrostFsFilter[] filters) : IFrostFsFilter { @@ -7,4 +10,19 @@ public class FrostFsFilter(string name, string key, int operation, string value, public int Operation { get; } = operation; public string Value { get; } = value; public FrostFsFilter[] Filters { get; } = filters; + + internal Filter GetMessage() + { + var filter = new Filter() + { + Name = Name, + Key = Key, + Op = (Operation)Operation, + Value = Value, + }; + + filter.Filters.AddRange(Filters.Select(f => f.GetMessage())); + + return filter; + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs index 184514b..abfce6c 100644 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs +++ b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsPlacementPolicy.cs @@ -51,6 +51,16 @@ public struct FrostFsPlacementPolicy(bool unique, ContainerBackupFactor = BackupFactor }; + if (Selectors != null && Selectors.Count > 0) + { + policy.Selectors.AddRange(Selectors.Select(s => s.GetMessage())); + } + + if (Filters != null && Filters.Count > 0) + { + policy.Filters.AddRange(Filters.Select(s => s.ToMessage())); + } + foreach (var replica in Replicas) { policy.Replicas.Add(replica.ToMessage()); diff --git a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs index 3369d6c..c058967 100644 --- a/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs +++ b/src/FrostFS.SDK.Client/Models/Netmap/FrostFsSelector.cs @@ -1,10 +1,24 @@ -namespace FrostFS.SDK; +using FrostFS.Netmap; + +namespace FrostFS.SDK; public class FrostFsSelector(string name) { - public string Name { get; set; } = name; + public string Name { get; } = name; public uint Count { get; set; } public int Clause { get; set; } public string? Attribute { get; set; } public string? Filter { get; set; } + + internal Selector GetMessage() + { + return new Selector() + { + Name = Name, + Clause = (Clause)Clause, + Count = Count, + Filter = Filter ?? string.Empty, + Attribute = Attribute ?? string.Empty, + }; + } } diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs index 64d865f..9db7898 100644 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs +++ b/src/FrostFS.SDK.Client/Models/Object/FrostFsAttributePair.cs @@ -1,8 +1,36 @@ namespace FrostFS.SDK; -public class FrostFsAttributePair(string key, string value) +public struct FrostFsAttributePair(string key, string value) : System.IEquatable { public string Key { get; set; } = key; public string Value { get; set; } = value; + + public override bool Equals(object obj) + { + if (obj == null || obj is not FrostFsAttributePair) + return false; + + return Equals((FrostFsAttributePair)obj); + } + + public override int GetHashCode() + { + return Key.GetHashCode() ^ Value.GetHashCode(); + } + + public static bool operator ==(FrostFsAttributePair left, FrostFsAttributePair right) + { + return left.Equals(right); + } + + public static bool operator !=(FrostFsAttributePair left, FrostFsAttributePair right) + { + return !(left == right); + } + + public bool Equals(FrostFsAttributePair other) + { + return GetHashCode().Equals(other.GetHashCode()); + } } diff --git a/src/FrostFS.SDK.Client/Pool/Pool.cs b/src/FrostFS.SDK.Client/Pool/Pool.cs index 3abfe60..f0b7a6e 100644 --- a/src/FrostFS.SDK.Client/Pool/Pool.cs +++ b/src/FrostFS.SDK.Client/Pool/Pool.cs @@ -568,10 +568,17 @@ public partial class Pool : IFrostFSClient return client.Client!.ListContainersAsync(args, ctx); } + [Obsolete("Use PutContainerAsync method")] public async Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx) { var client = Connection(); - return await client.Client!.CreateContainerAsync(args, ctx).ConfigureAwait(false); + return await client.Client!.PutContainerAsync(args, ctx).ConfigureAwait(false); + } + + public async Task PutContainerAsync(PrmContainerCreate args, CallContext ctx) + { + var client = Connection(); + return await client.Client!.PutContainerAsync(args, ctx).ConfigureAwait(false); } public async Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx) diff --git a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs index ca76e08..d7ba34c 100644 --- a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs @@ -71,7 +71,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService } } - internal async Task CreateContainerAsync(PrmContainerCreate args, CallContext ctx) + internal async Task PutContainerAsync(PrmContainerCreate args, CallContext ctx) { var grpcContainer = args.Container.GetContainer(); diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs new file mode 100644 index 0000000..37a8c13 --- /dev/null +++ b/src/FrostFS.SDK.Tests/Smoke/Client/ContainerTests/ContainerTests.cs @@ -0,0 +1,283 @@ +using System.Collections.Concurrent; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using FrostFS.SDK.Client; +using Grpc.Core; + +namespace FrostFS.SDK.Tests.Smoke; + +[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] +[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] +public class ContainerTests : SmokeTestsBase +{ + [Fact] + public async void FailCreateContainerByTimeoutTest() + { + var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + + try + { + _ = await CreateContainer(client, + ctx: new CallContext(TimeSpan.FromMilliseconds(1)), + token: null, + unique: true, + backupFactor: 1, + selectors: [], + filter: [], + containerAttributes: [new("testKey1", "testValue1")], + new FrostFsReplica(1)); + + Assert.Fail("Exception is expected"); + } + catch (RpcException ex) + { + Assert.Equal(StatusCode.DeadlineExceeded, ex.Status.StatusCode); + } + } + + [Fact] + public async void CreateContainerTest1() + { + var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + await Cleanup(client); + + client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + + FrostFsContainerId containerId = await CreateContainer(client, + ctx: default, + token: null, + unique: true, + backupFactor: 1, + selectors: [], + filter: [], + containerAttributes: [new ("testKey1", "testValue1")], + new FrostFsReplica(3)); + + Assert.NotNull(containerId); + + var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); + + Assert.NotNull(container); + + Assert.NotNull(container.Attributes); + Assert.Equal(2, container.Attributes.Count); + Assert.Equal("testKey1", container.Attributes[0].Key); + Assert.Equal("testValue1", container.Attributes[0].Value); + Assert.Equal("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", container.Attributes[1].Key); + Assert.Equal("true", container.Attributes[1].Value); + + Assert.True(container.PlacementPolicy.HasValue); + + Assert.Equal(1u, container.PlacementPolicy.Value.BackupFactor); + Assert.True(container.PlacementPolicy.Value.Unique); + Assert.Empty(container.PlacementPolicy.Value.Selectors); + Assert.Empty(container.PlacementPolicy.Value.Filters); + + Assert.Single(container.PlacementPolicy.Value.Replicas); + Assert.Equal(3, 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); + + Assert.Equal(OwnerId!.ToString(), container.Owner!.Value); + + Assert.NotNull(container.Version); + + Assert.Equal(Version!.Major, container.Version.Major); + Assert.Equal(Version.Minor, container.Version.Minor); + } + + [Fact] + public async void CreateContainerTest2() + { + var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + + await Cleanup(client); + + client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + + Collection filters = [ + new ("filter1", "filterKey1", 1, "testValue1", []), + new ("filter2", "filterKey2", 2, "testValue2", [new ("subFilter2", "subFilterKey2", 3, "testValue3",[])]) + ]; + + Collection selectors = [ + new ("selector1") { + Count = 1, + Clause = 1, + Attribute = "attribute1", + Filter = "filter1" + }, + new ("selector2") { + Count = 2, + Clause = 2, + Attribute = "attribute2", + Filter = "filter2" + }, + ]; + + FrostFsContainerId containerId = await CreateContainer(client, + ctx: default, + token: null, + unique: false, + backupFactor: 2, + selectors, + filter: filters, + containerAttributes: [], + new FrostFsReplica(1)); + + Assert.NotNull(containerId); + + var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); + + Assert.NotNull(container); + + Assert.NotNull(container.Attributes); + Assert.Single(container.Attributes); + Assert.Equal("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", container.Attributes[0].Key); + Assert.Equal("true", container.Attributes[0].Value); + + Assert.True(container.PlacementPolicy.HasValue); + + Assert.Equal(2u, container.PlacementPolicy.Value.BackupFactor); + Assert.False(container.PlacementPolicy.Value.Unique); + + Assert.NotEmpty(container.PlacementPolicy.Value.Selectors); + + Assert.Equal(2, container.PlacementPolicy.Value.Selectors.Count); + + var selector1 = container.PlacementPolicy.Value.Selectors[0]; + Assert.Equal("selector1", selector1.Name); + Assert.Equal(1, selector1.Clause); + Assert.Equal(1u, selector1.Count); + Assert.Equal("attribute1", selector1.Attribute); + Assert.Equal("filter1", selector1.Filter); + + var selector2 = container.PlacementPolicy.Value.Selectors[1]; + Assert.Equal("selector2", selector2.Name); + Assert.Equal(2, selector2.Clause); + Assert.Equal(2u, selector2.Count); + Assert.Equal("attribute2", selector2.Attribute); + Assert.Equal("filter2", selector2.Filter); + + Assert.NotEmpty(container.PlacementPolicy.Value.Filters); + + Assert.Equal(2, container.PlacementPolicy.Value.Filters.Count); + + var filter1 = container.PlacementPolicy.Value.Filters[0]; + Assert.Equal("filter1", filter1.Name); + Assert.Equal("filterKey1", filter1.Key); + Assert.Equal("testValue1", filter1.Value); + Assert.Equal(1, filter1.Operation); + Assert.Empty(filter1.Filters); + + var filter2 = container.PlacementPolicy.Value.Filters[1]; + Assert.Equal("filter2", filter2.Name); + Assert.Equal("filterKey2", filter2.Key); + Assert.Equal("testValue2", filter2.Value); + Assert.Equal(2, filter2.Operation); + Assert.NotEmpty(filter2.Filters); + + Assert.Single(filter2.Filters); + + var subFilter = filter2.Filters.First(); + Assert.Equal("subFilter2", subFilter.Name); + Assert.Equal("subFilterKey2", subFilter.Key); + Assert.Equal("testValue3", subFilter.Value); + Assert.Equal(3, subFilter.Operation); + Assert.Empty(subFilter.Filters); + + Assert.Single(container.PlacementPolicy.Value.Replicas); + Assert.Equal(1, 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); + + Assert.Equal(OwnerId!.ToString(), container.Owner!.Value); + + Assert.NotNull(container.Version); + + Assert.Equal(Version!.Major, container.Version.Major); + Assert.Equal(Version.Minor, container.Version.Minor); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + public async void ListAndDeleteContainersTest(int countainerCount) + { + var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + + await Cleanup(client); + + var tasks = new Task[countainerCount]; + + var createdContainers = new ConcurrentDictionary(); + + for (int i = 0; i < countainerCount; i++) + { + int index = i; + tasks[i] = Task.Run(async () => + { + FrostFsContainerId containerId = await CreateContainer(client, + ctx: default, + token: null, + unique: true, + backupFactor: 1, + selectors: [], + filter: [], + containerAttributes: [new($"testKey{index}", $"testValue{index}")], + new FrostFsReplica(3)); + + createdContainers.TryAdd(containerId.ToString(), index); + }); + } + + #pragma warning disable xUnit1031 // Timeout is used + if (!Task.WaitAll(tasks, 20000)) + { + Assert.Fail("cannot create containers"); + } + #pragma warning restore xUnit1031 + + var containers = client.ListContainersAsync(new PrmContainerGetAll(), default); + + var receivedContainers = new List(); + await foreach (var cntr in containers) + { + receivedContainers.Add(cntr.ToString()); + } + + Assert.Equal(countainerCount, receivedContainers.Count); + + foreach (var cntrId in receivedContainers) + { + if (!createdContainers.TryGetValue(cntrId, out var index)) + { + Assert.Fail("Cannot find corresponding container"); + } + + FrostFsContainerId container = new(cntrId); + var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container), default); + + Assert.NotNull(containerInfo); + Assert.NotNull(containerInfo.Attributes); + + Assert.Contains(new FrostFsAttributePair($"testKey{index}", $"testValue{index}"), containerInfo.Attributes); + } + + tasks = new Task[countainerCount]; + + for (int i = 0; i < receivedContainers.Count; i++) + { + tasks[i] = client.DeleteContainerAsync(new PrmContainerDelete(new FrostFsContainerId(receivedContainers[i]), lightWait), default); + } + + await Task.WhenAll(tasks); + + await foreach (var _ in client.ListContainersAsync(default, default)) + { + Assert.Fail("Containers exist"); + } + } +} diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs b/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs new file mode 100644 index 0000000..c3df8e5 --- /dev/null +++ b/src/FrostFS.SDK.Tests/Smoke/Client/MiscTests/InterceptorTest.cs @@ -0,0 +1,37 @@ +using FrostFS.SDK.Client; +using FrostFS.SDK.SmokeTests; + +namespace FrostFS.SDK.Tests.Smoke; + +public class InterceptorTests() : SmokeTestsBase +{ + [Fact] + public async void NodeInfoCallbackAndInterceptorTest() + { + bool callbackInvoked = false; + bool interceptorInvoked = false; + + var options = ClientOptions; + options.Value.Callback = (cs) => + { + callbackInvoked = true; + Assert.True(cs.ElapsedMicroSeconds > 0); + }; + + options.Value.Interceptors.Add(new CallbackInterceptor(s => interceptorInvoked = true)); + + var client = FrostFSClient.GetInstance(options, GrpcChannel); + + var result = await client.GetNodeInfoAsync(default); + + Assert.True(callbackInvoked); + Assert.True(interceptorInvoked); + + Assert.Equal(2, result.Version.Major); + Assert.Equal(13, result.Version.Minor); + Assert.Equal(NodeState.Online, result.State); + Assert.Equal(33, result.PublicKey.Length); + Assert.NotNull(result.Addresses); + Assert.True(result.Attributes.Count > 0); + } +} diff --git a/src/FrostFS.SDK.Tests/Smoke/NetworkTests/NetworkTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs similarity index 100% rename from src/FrostFS.SDK.Tests/Smoke/NetworkTests/NetworkTests.cs rename to src/FrostFS.SDK.Tests/Smoke/Client/NetworkTests/NetworkTests.cs diff --git a/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs new file mode 100644 index 0000000..36a97cd --- /dev/null +++ b/src/FrostFS.SDK.Tests/Smoke/Client/ObjectTests/ObjectTests.cs @@ -0,0 +1,324 @@ +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; +using FrostFS.SDK.Client; +using FrostFS.SDK.Client.Interfaces; +using FrostFS.SDK.Cryptography; +using Xunit.Abstractions; + +namespace FrostFS.SDK.Tests.Smoke; + +[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] +[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] +public class ObjectTests(ITestOutputHelper testOutputHelper) : SmokeTestsBase +{ + private readonly ITestOutputHelper _testOutputHelper = testOutputHelper; + + const string clientCut = "clientCut"; + const string serverCut = "serverCut"; + const string singleObject = "singleObject"; + + [Theory] + [InlineData(true, 1, 1)] + [InlineData(false, 1, 1)] + [InlineData(true, 1, 3)] + [InlineData(false, 1, 3)] + [InlineData(true, 2, 3)] + [InlineData(false, 2, 3)] + [InlineData(true, 2, 1)] + [InlineData(false, 2, 1)] + public async void FullScenario(bool unique, uint backupFactor, int replicas) + { + var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); + _testOutputHelper.WriteLine("client created"); + + await Cleanup(client); + _testOutputHelper.WriteLine("existing containers removed"); + + FrostFsContainerId containerId = await CreateContainer(client, + ctx: default, + token: null, + unique: unique, + backupFactor: backupFactor, + selectors: [], + filter: [], + containerAttributes: [], + new FrostFsReplica(replicas)); + + Assert.NotNull(containerId); + _testOutputHelper.WriteLine("container created"); + + await AddObjectRules(client, containerId); + _testOutputHelper.WriteLine("rules added"); + + await RunSuite(client, containerId); + } + + private async Task RunSuite(IFrostFSClient client, FrostFsContainerId containerId) + { + int[] objectSizes = [1, 257, 6 * 1024, 20 * 1024]; + + string[] objectTypes = [clientCut, serverCut, singleObject]; + + foreach (var objectSize in objectSizes) + { + _testOutputHelper.WriteLine($"test set for object size {objectSize}"); + + var bytes = GetRandomBytes(objectSize); + var hash = SHA256.HashData(bytes); + + FrostFsObjectId objectId; + foreach (var type in objectTypes) + { + switch (type) + { + case serverCut: + objectId = await CreateObjectServerCut(client, containerId, bytes); + break; + case clientCut: + objectId = await CreateObjectClientCut(client, containerId, bytes); + break; + case singleObject: + if (objectSize > 1 * 1024 * 1024) + continue; + objectId = await PutSingleObject(client, containerId, bytes); + + break; + default: + throw new ArgumentException("unexpected object type"); + } + + Assert.NotNull(objectId); + + _testOutputHelper.WriteLine($"\tobject created"); + + await ValidateContent(client, containerId, hash, objectId); + _testOutputHelper.WriteLine($"\tcontent validated"); + + await ValidateFilters(client, containerId, objectId, null, (ulong)bytes.Length); + _testOutputHelper.WriteLine($"\tfilters validated"); + + // if (type != clientCut) + // { + // await ValidatePatch(client, containerId, bytes, objectId); + // _testOutputHelper.WriteLine($"\tpatch validated"); + // } + + await ValidateRange(client, containerId, bytes, objectId); + _testOutputHelper.WriteLine($"\trange validated"); + + await ValidateRangeHash(client, containerId, bytes, objectId); + _testOutputHelper.WriteLine($"\trange hash validated"); + + await RemoveObject(client, containerId, objectId); + _testOutputHelper.WriteLine($"\tobject removed"); + } + } + } + + private static async Task ValidateRangeHash(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId) + { + if (bytes.Length < 200) + return; + + var rangeParam = new PrmRangeHashGet(containerId, objectId, [new FrostFsRange(100, 64)], bytes); + + var hashes = await client.GetRangeHashAsync(rangeParam, default); + + foreach (var h in hashes) + { + var x = h[..32].ToArray(); + Assert.NotNull(x); + Assert.True(x.Length > 0); + } + } + + private static async Task ValidateRange(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId) + { + if (bytes.Length < 200) + return; + + var rangeParam = new PrmRangeGet(containerId, objectId, new FrostFsRange(50, 100)); + + var rangeReader = await client.GetRangeAsync(rangeParam, default); + + var downloadedBytes = new byte[rangeParam.Range.Length]; + MemoryStream ms = new(downloadedBytes); + + ReadOnlyMemory? chunk; + while ((chunk = await rangeReader!.ReadChunk()) != null) + { + ms.Write(chunk.Value.Span); + } + + Assert.Equal(SHA256.HashData(bytes.AsSpan().Slice(50, 100)), SHA256.HashData(downloadedBytes)); + } + + private static async Task ValidatePatch(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes, FrostFsObjectId objectId) + { + if (bytes.Length < 1024 + 64) + return; + + var patch = new byte[1024]; + for (int i = 0; i < patch.Length; i++) + { + patch[i] = 32; + } + + var range = new FrostFsRange(64, (ulong)patch.Length); + + var patchParams = new PrmObjectPatch( + new FrostFsAddress(containerId, objectId), + payload: new MemoryStream(patch), + maxChunkLength: 1024, + range: range); + + 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) + { + 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]); + } + + + private async Task ValidateFilters(IFrostFSClient client, FrostFsContainerId containerId, FrostFsObjectId objectId, SplitId? splitId, ulong length) + { + var ecdsaKey = keyString.LoadWif(); + + var networkInfo = await client.GetNetmapSnapshotAsync(default); + + await CheckFilter(client, containerId, new FilterByContainerId(FrostFsMatchType.Equals, containerId)); + + await CheckFilter(client, containerId, new FilterByOwnerId(FrostFsMatchType.Equals, FrostFsOwner.FromKey(ecdsaKey))); + + if (splitId != null) + { + await CheckFilter(client, containerId, new FilterBySplitId(FrostFsMatchType.Equals, splitId)); + } + + await CheckFilter(client, containerId, new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test")); + + await CheckFilter(client, containerId, new FilterByObjectId(FrostFsMatchType.Equals, objectId)); + + await CheckFilter(client, containerId, new FilterByVersion(FrostFsMatchType.Equals, networkInfo.NodeInfoCollection[0].Version)); + + await CheckFilter(client, containerId, new FilterByEpoch(FrostFsMatchType.Equals, networkInfo.Epoch)); + + await CheckFilter(client, containerId, new FilterByPayloadLength(FrostFsMatchType.Equals, length)); + + // var checkSum = CheckSum.CreateCheckSum(hash); + // await CheckFilter(client, containerId, new FilterByPayloadHash(FrostFsMatchType.Equals, checkSum)); + + await CheckFilter(client, containerId, new FilterByPhysicallyStored()); + } + + private static async Task RemoveObject(IFrostFSClient client, FrostFsContainerId containerId, FrostFsObjectId objectId) + { + await client.DeleteObjectAsync(new PrmObjectDelete(containerId, objectId), default); + + try + { + _ = await client.GetObjectAsync( + new PrmObjectGet(containerId, objectId), + default); + + Assert.Fail("Exception is expected here"); + } + catch (FrostFsResponseException ex) + { + Assert.Equal("object already removed", ex.Status!.Message); + } + } + + private static async Task ValidateContent(IFrostFSClient client, FrostFsContainerId containerId, byte[] hash, FrostFsObjectId objectId) + { + var @object = await client.GetObjectAsync( + new PrmObjectGet(containerId, objectId), + default); + + var downloadedBytes = new byte[@object.Header.PayloadLength]; + MemoryStream ms = new(downloadedBytes); + + ReadOnlyMemory? chunk = null; + while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) + { + ms.Write(chunk.Value.Span); + } + + Assert.Equal(hash, SHA256.HashData(downloadedBytes)); + } + + private static async Task CreateObjectServerCut(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) + { + var header = new FrostFsObjectHeader( + containerId: containerId, + type: FrostFsObjectType.Regular, + [new FrostFsAttributePair("fileName", "test")]); + + var param = new PrmObjectPut(header); + + var objectWriter = await client.PutObjectAsync(param, default).ConfigureAwait(true); + + await objectWriter.WriteAsync(bytes); + return await objectWriter.CompleteAsync(); + } + + private static async Task CreateObjectClientCut(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) + { + var header = new FrostFsObjectHeader( + containerId: containerId, + type: FrostFsObjectType.Regular, + [new FrostFsAttributePair("fileName", "test")]); + + var param = new PrmObjectClientCutPut(header, payload: new MemoryStream(bytes)); + + return await client.PutClientCutObjectAsync(param, default).ConfigureAwait(true); + } + + private static async Task PutSingleObject(IFrostFSClient client, FrostFsContainerId containerId, byte[] bytes) + { + var header = new FrostFsObjectHeader( + containerId: containerId, + type: FrostFsObjectType.Regular, + [new FrostFsAttributePair("fileName", "test")]); + + + var obj = new FrostFsObject(header) { SingleObjectPayload = bytes }; + + var param = new PrmSingleObjectPut(obj); + + return await client.PutSingleObjectAsync(param, default).ConfigureAwait(true); + } + + private static async Task CheckFilter(IFrostFSClient client, FrostFsContainerId containerId, IObjectFilter filter) + { + var resultObjectsCount = 0; + + PrmObjectSearch searchParam = new(containerId, null, [], filter); + + await foreach (var objId in client.SearchObjectsAsync(searchParam, default)) + { + resultObjectsCount++; + var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objId), default); + } + + Assert.True(0 < resultObjectsCount, $"Filter for {filter.Key} doesn't work"); + } +} diff --git a/src/FrostFS.SDK.Tests/Smoke/SessionTests/SessionTests.cs b/src/FrostFS.SDK.Tests/Smoke/Client/SessionTests/SessionTests.cs similarity index 100% rename from src/FrostFS.SDK.Tests/Smoke/SessionTests/SessionTests.cs rename to src/FrostFS.SDK.Tests/Smoke/Client/SessionTests/SessionTests.cs diff --git a/src/FrostFS.SDK.Tests/Smoke/ContainerTests/ContainerTests.cs b/src/FrostFS.SDK.Tests/Smoke/ContainerTests/ContainerTests.cs deleted file mode 100644 index ff7ebe8..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/ContainerTests/ContainerTests.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using FrostFS.SDK.Client; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class ContainerTests : SmokeTestsBase -{ - [Fact] - public async void CreateContanerTest() - { - var options = ClientOptions; - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - await Cleanup(client); - - var ctx = new CallContext(TimeSpan.FromSeconds(20)); - - FrostFsContainerId containerId = await CreateContainer(client, unique: true, backupFactor: 1, selectors: [], filter: [], new FrostFsReplica(3)); - - Assert.NotNull(containerId); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - Assert.NotNull(container); - - Assert.Single(container.Attributes); - Assert.Equal("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", container.Attributes[0].Key); - Assert.Equal("true", container.Attributes[0].Value); - - Assert.True(container.PlacementPolicy.HasValue); - - Assert.Equal(1u, container.PlacementPolicy.Value.BackupFactor); - Assert.True(container.PlacementPolicy.Value.Unique); - Assert.Empty(container.PlacementPolicy.Value.Selectors); - Assert.Empty(container.PlacementPolicy.Value.Filters); - - Assert.Single(container.PlacementPolicy.Value.Replicas); - Assert.Equal(3, 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); - - Assert.Equal(OwnerId.ToString(), container.Owner.Value); - - Assert.Equal(Version.Major, container.Version.Major); - Assert.Equal(Version.Minor, container.Version.Minor); - } - - [Theory] - [InlineData(1)] - [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB - [InlineData(6 * 1024 * 1024 + 100)] - public async void SimpleScenarioWithSessionTest(int objectSize) - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default); - - await Cleanup(client); - - var ctx = new CallContext(TimeSpan.FromSeconds(20)); - - var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo( - new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(3)), - [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), - PrmWait.DefaultParams); - - var container = await client.CreateContainerAsync(createContainerParam, ctx); - - var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container), ctx); - Assert.NotNull(containerInfo); - - await AddObjectRules(client, container); - - var bytes = GetRandomBytes(objectSize); - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: container, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]), - sessionToken: token); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); - - bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(container, token, [], filter), default)) - { - hasObject = true; - - var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(container, objectId, false, token), default); - - var objHeader = res.HeaderInfo; - Assert.NotNull(objHeader); - Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - Assert.Equal("fileName", objHeader.Attributes.First().Key); - Assert.Equal("test", objHeader.Attributes.First().Value); - } - - Assert.True(hasObject); - - var @object = await client.GetObjectAsync(new PrmObjectGet(container, objectId, token), default); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - - await Cleanup(client); - - await foreach (var _ in client.ListContainersAsync(default, default)) - { - Assert.Fail("Containers exist"); - } - } -} diff --git a/src/FrostFS.SDK.Tests/Smoke/ObjectTests/ObjectTests.cs b/src/FrostFS.SDK.Tests/Smoke/ObjectTests/ObjectTests.cs deleted file mode 100644 index 1291932..0000000 --- a/src/FrostFS.SDK.Tests/Smoke/ObjectTests/ObjectTests.cs +++ /dev/null @@ -1,614 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Interfaces; -using FrostFS.SDK.Cryptography; -using FrostFS.SDK.SmokeTests; - -namespace FrostFS.SDK.Tests.Smoke; - -[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] -[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] -public class ObjectTests : SmokeTestsBase -{ - [Fact] - public async void CreateObjectWithSessionToken() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - await AddObjectRules(client, containerId); - - var bytes = GetRandomBytes(1024); - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]), - sessionToken: token); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId), default) - .ConfigureAwait(true); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk().ConfigureAwait(true)) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - - await Cleanup(client).ConfigureAwait(true); - } - - [Fact] - public async void FilterTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - await AddObjectRules(client, containerId); - - var bytes = new byte[] { 1, 2, 3 }; - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")], - new FrostFsSplit())); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var ecdsaKey = keyString.LoadWif(); - - var networkInfo = await client.GetNetmapSnapshotAsync(default); - - await CheckFilter(client, containerId, new FilterByContainerId(FrostFsMatchType.Equals, containerId)); - - await CheckFilter(client, containerId, new FilterByOwnerId(FrostFsMatchType.Equals, FrostFsOwner.FromKey(ecdsaKey))); - - await CheckFilter(client, containerId, new FilterBySplitId(FrostFsMatchType.Equals, param.Header!.Split!.SplitId)); - - await CheckFilter(client, containerId, new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test")); - - await CheckFilter(client, containerId, new FilterByObjectId(FrostFsMatchType.Equals, objectId)); - - await CheckFilter(client, containerId, new FilterByVersion(FrostFsMatchType.Equals, networkInfo.NodeInfoCollection[0].Version)); - - await CheckFilter(client, containerId, new FilterByEpoch(FrostFsMatchType.Equals, networkInfo.Epoch)); - - await CheckFilter(client, containerId, new FilterByPayloadLength(FrostFsMatchType.Equals, 3)); - - var checkSum = CheckSum.CreateCheckSum(bytes); - - // await CheckFilter(client, containerId, new FilterByPayloadHash(FrostFsMatchType.Equals, checkSum)); - - await CheckFilter(client, containerId, new FilterByPhysicallyStored()); - } - - private static async Task CheckFilter(IFrostFSClient client, FrostFsContainerId containerId, IObjectFilter filter) - { - var resultObjectsCount = 0; - - PrmObjectSearch searchParam = new(containerId, null, [], filter); - - await foreach (var objId in client.SearchObjectsAsync(searchParam, default)) - { - resultObjectsCount++; - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objId), default); - } - - Assert.True(0 < resultObjectsCount, $"Filter for {filter.Key} doesn't work"); - } - - [Theory] - [InlineData(1)] - [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB - [InlineData(6 * 1024 * 1024 + 100)] - public async void SimpleScenarioTest(int objectSize) - { - bool callbackInvoked = false; - - var options = ClientOptions; - - options.Value.Callback = new((cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }); - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - await Cleanup(client); - - var ctx = new CallContext(TimeSpan.FromSeconds(20)); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - Assert.NotNull(container); - Assert.True(callbackInvoked); - - await AddObjectRules(client, containerId); - - var bytes = GetRandomBytes(objectSize); - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")])); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var filter1 = new FilterByOwnerId(FrostFsMatchType.Equals, OwnerId!); - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter1), default)) - { - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId, false), default); - } - - var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); - - bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter), default)) - { - hasObject = true; - - var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId), default); - - var objHeader = res.HeaderInfo; - Assert.NotNull(objHeader); - Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - Assert.Equal("fileName", objHeader.Attributes.First().Key); - Assert.Equal("test", objHeader.Attributes.First().Value); - } - - Assert.True(hasObject); - - var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId), default); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - } - - [Theory] - [InlineData(1)] - [InlineData(500 * 1024)] - [InlineData(3 * 1024 * 1024)] - public async void PutSingleObjectTest(int objectSize) - { - var options = ClientOptions; - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - await Cleanup(client); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - Assert.NotNull(container); - - await AddObjectRules(client, containerId); - - var bytes = GetRandomBytes(objectSize); - - var header = new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName1", "test1")]); - - var obj = new FrostFsObject(header) { SingleObjectPayload = bytes }; - - var param = new PrmSingleObjectPut(obj); - - var objectId = await client.PutSingleObjectAsync(param, default).ConfigureAwait(true); - - var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName1", "test1"); - - bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, [], filter), default)) - { - hasObject = true; - - var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId), default); - - var objHeader = res.HeaderInfo; - Assert.NotNull(objHeader); - Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - Assert.Equal("fileName1", objHeader.Attributes.First().Key); - Assert.Equal("test1", objHeader.Attributes.First().Value); - } - - Assert.True(hasObject); - - var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId), default); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - - await Cleanup(client); - - await foreach (var _ in client.ListContainersAsync(default, default)) - { - Assert.Fail("Containers exist"); - } - } - - [Fact] - public async void PatchTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - Assert.NotNull(container); - - await AddObjectRules(client, containerId); - - var bytes = new byte[1024]; - for (int i = 0; i < 1024; i++) - { - bytes[i] = 31; - } - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")])); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var patch = new byte[256]; - for (int i = 0; i < patch.Length; i++) - { - patch[i] = 32; - } - - var range = new FrostFsRange(64, (ulong)patch.Length); - - var patchParams = new PrmObjectPatch( - new FrostFsAddress(containerId, objectId), - payload: new MemoryStream(patch), - maxChunkLength: 256, - range: range); - - 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 = null; - 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]); - - await Cleanup(client); - - await foreach (var _ in client.ListContainersAsync(default, default)) - { - Assert.Fail("Containers exist"); - } - } - - [Fact] - public async void RangeTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - Assert.NotNull(container); - - await AddObjectRules(client, containerId); - - var bytes = new byte[256]; - for (int i = 0; i < 256; i++) - { - bytes[i] = (byte)i; - } - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular)); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var rangeParam = new PrmRangeGet(containerId, objectId, new FrostFsRange(50, 100)); - - var rangeReader = await client.GetRangeAsync(rangeParam, default); - - var downloadedBytes = new byte[rangeParam.Range.Length]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk; - while ((chunk = await rangeReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes.AsSpan().Slice(50, 100)), SHA256.HashData(downloadedBytes)); - } - - [Fact] - public async void RangeHashTest() - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - await Cleanup(client); - - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(3)); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), default); - Assert.NotNull(container); - - await AddObjectRules(client, containerId); - - var bytes = new byte[256]; - for (int i = 0; i < 256; i++) - { - bytes[i] = (byte)i; - } - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular)); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var rangeParam = new PrmRangeHashGet(containerId, objectId, [new FrostFsRange(100, 64)], bytes); - - var hashes = await client.GetRangeHashAsync(rangeParam, default); - - foreach (var hash in hashes) - { - var x = hash[..32].ToArray(); - Assert.NotNull(x); - Assert.True(x.Length > 0); - } - } - - [Theory] - [InlineData(1)] - [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB - [InlineData(6 * 1024 * 1024 + 100)] - public async void SimpleScenarioWithSessionTest(int objectSize) - { - var client = FrostFSClient.GetInstance(ClientOptions, GrpcChannel); - - var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default); - - await Cleanup(client); - - var ctx = new CallContext(TimeSpan.FromSeconds(20)); - - var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo( - new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(3)), - [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")]), - PrmWait.DefaultParams); - - var container = await client.CreateContainerAsync(createContainerParam, ctx); - - var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container), ctx); - Assert.NotNull(containerInfo); - - await AddObjectRules(client, container); - - var bytes = GetRandomBytes(objectSize); - - var param = new PrmObjectPut( - new FrostFsObjectHeader( - containerId: container, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]), - sessionToken: token); - - var stream = await client.PutObjectAsync(param, default).ConfigureAwait(true); - - await stream.WriteAsync(bytes.AsMemory()); - var objectId = await stream.CompleteAsync(); - - var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); - - bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(container, token, [], filter), default)) - { - hasObject = true; - - var res = await client.GetObjectHeadAsync(new PrmObjectHeadGet(container, objectId, false, token), default); - - var objHeader = res.HeaderInfo; - Assert.NotNull(objHeader); - Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - Assert.Equal("fileName", objHeader.Attributes.First().Key); - Assert.Equal("test", objHeader.Attributes.First().Value); - } - - Assert.True(hasObject); - - var @object = await client.GetObjectAsync(new PrmObjectGet(container, objectId, token), default); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - - await Cleanup(client); - - await foreach (var _ in client.ListContainersAsync(default, default)) - { - Assert.Fail("Containers exist"); - } - } - - [Fact] - public async void ClientCutScenarioTest() - { - var options = ClientOptions; - options.Value.Interceptors.Add(new CallbackInterceptor()); - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - await Cleanup(client); - - int[] replicas = [3]; //1 - - foreach (var rep in replicas) - { - FrostFsContainerId containerId = await CreateContainer(client, unique:true, backupFactor: 1, selectors:[], filter:[], new FrostFsReplica(rep)); - - var ctx = new CallContext(TimeSpan.FromSeconds(10)); - - var container = await client.GetContainerAsync(new PrmContainerGet(containerId), ctx); - - Assert.NotNull(container); - - await AddObjectRules(client, containerId); - - var mb = 1024 * 1024; - - int[] objectSizes = [1, 1024, 6 * mb + 1]; //64 * mb, 64 * mb - 1, , 2 * 64 * mb + 256]; - - foreach (var objectSize in objectSizes) - { - byte[] bytes = GetRandomBytes(objectSize); - - var param = new PrmObjectClientCutPut( - new FrostFsObjectHeader( - containerId: containerId, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]), - payload: new MemoryStream(bytes)); - - var objectId = await client.PutClientCutObjectAsync(param, default).ConfigureAwait(true); - - var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId), default); - - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); - - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } - - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); - } - } - } - - [Fact] - public async void NodeInfoCallbackAndInterceptorTest() - { - bool callbackInvoked = false; - bool interceptorInvoked = false; - - var options = ClientOptions; - options.Value.Callback = (cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }; - - options.Value.Interceptors.Add(new CallbackInterceptor(s => interceptorInvoked = true)); - - var client = FrostFSClient.GetInstance(options, GrpcChannel); - - var result = await client.GetNodeInfoAsync(default); - - Assert.True(callbackInvoked); - Assert.True(interceptorInvoked); - - Assert.Equal(2, result.Version.Major); - Assert.Equal(13, result.Version.Minor); - Assert.Equal(NodeState.Online, result.State); - Assert.Equal(33, result.PublicKey.Length); - Assert.NotNull(result.Addresses); - Assert.True(result.Attributes.Count > 0); - } -} diff --git a/src/FrostFS.SDK.Tests/Multithread/MultiThreadTestsBase.cs b/src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultiThreadTestsBase.cs similarity index 100% rename from src/FrostFS.SDK.Tests/Multithread/MultiThreadTestsBase.cs rename to src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultiThreadTestsBase.cs diff --git a/src/FrostFS.SDK.Tests/Multithread/MultithreadPoolSmokeTests.cs b/src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultithreadPoolSmokeTests.cs similarity index 92% rename from src/FrostFS.SDK.Tests/Multithread/MultithreadPoolSmokeTests.cs rename to src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultithreadPoolSmokeTests.cs index 4cb7e4d..194ef98 100644 --- a/src/FrostFS.SDK.Tests/Multithread/MultithreadPoolSmokeTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultithreadPoolSmokeTests.cs @@ -4,16 +4,12 @@ using System.Security.Cryptography; using FrostFS.SDK.Client; using FrostFS.SDK.Cryptography; -using Microsoft.Extensions.Options; - namespace FrostFS.SDK.Tests.Smoke; [SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] [SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] public class MultithreadPoolSmokeTests : SmokeTestsBase { - private static readonly PrmWait lightWait = new(100, 1); - private InitParameters GetDefaultParams() { return new InitParameters((url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url))) @@ -170,7 +166,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["key1", "value1"]); - var containerId = await pool.CreateContainerAsync(createContainerParam, default); + var containerId = await pool.PutContainerAsync(createContainerParam, default); var bytes = GetRandomBytes(1024); @@ -219,7 +215,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), lightWait); - var containerId = await pool.CreateContainerAsync(createContainerParam, default); + var containerId = await pool.PutContainerAsync(createContainerParam, default); var bytes = new byte[] { 1, 2, 3 }; @@ -315,7 +311,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await pool.CreateContainerAsync(createContainerParam, default); + var createdContainer = await pool.PutContainerAsync(createContainerParam, default); var container = await pool.GetContainerAsync(new PrmContainerGet(createdContainer), default); Assert.NotNull(container); @@ -399,7 +395,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), PrmWait.DefaultParams); - var container = await pool.CreateContainerAsync(createContainerParam, ctx); + var container = await pool.PutContainerAsync(createContainerParam, ctx); var containerInfo = await pool.GetContainerAsync(new PrmContainerGet(container), ctx); Assert.NotNull(containerInfo); @@ -482,7 +478,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), lightWait); - var containerId = await pool.CreateContainerAsync(createContainerParam, default); + var containerId = await pool.PutContainerAsync(createContainerParam, default); var ctx = new CallContext(TimeSpan.FromSeconds(10)); @@ -545,37 +541,4 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase Assert.Fail($"Container {cid.GetValue()} exist"); } } - - private static byte[] GetRandomBytes(int size) - { - Random rnd = new(); - var bytes = new byte[size]; - rnd.NextBytes(bytes); - return bytes; - } - - private static IOptions GetSingleOwnerOptions(string key, string url) - { - return Options.Create(new ClientSettings - { - Key = key, - Host = url - }); - } - - private static IOptions GetOptions(string url) - { - return Options.Create(new ClientSettings - { - Host = url - }); - } - - static async Task Cleanup(Pool pool) - { - await foreach (var cid in pool.ListContainersAsync(default, default)) - { - await pool.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default).ConfigureAwait(true); - } - } } diff --git a/src/FrostFS.SDK.Tests/Multithread/MultithreadSmokeClientTests.cs b/src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultithreadSmokeClientTests.cs similarity index 94% rename from src/FrostFS.SDK.Tests/Multithread/MultithreadSmokeClientTests.cs rename to src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultithreadSmokeClientTests.cs index b239e58..32aeb2e 100644 --- a/src/FrostFS.SDK.Tests/Multithread/MultithreadSmokeClientTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/PoolTests/Multithread/MultithreadSmokeClientTests.cs @@ -14,8 +14,6 @@ namespace FrostFS.SDK.Tests.Smoke; [SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] public class MultithreadSmokeClientTests : SmokeTestsBase { - private static readonly PrmWait lightWait = new(100, 1); - [Fact] public async void AccountTest() { @@ -103,7 +101,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["key1", "value1"]); - var containerId = await client.CreateContainerAsync(createContainerParam, default); + var containerId = await client.PutContainerAsync(createContainerParam, default); var bytes = GetRandomBytes(1024); @@ -147,7 +145,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), lightWait); - var containerId = await client.CreateContainerAsync(createContainerParam, default); + var containerId = await client.PutContainerAsync(createContainerParam, default); var bytes = new byte[] { 1, 2, 3 }; @@ -241,7 +239,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, ctx); + var createdContainer = await client.PutContainerAsync(createContainerParam, ctx); var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); Assert.NotNull(container); @@ -312,7 +310,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, default); + var createdContainer = await client.PutContainerAsync(createContainerParam, default); var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); Assert.NotNull(container); @@ -392,7 +390,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, default); + var createdContainer = await client.PutContainerAsync(createContainerParam, default); var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); Assert.NotNull(container); @@ -446,7 +444,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await client.CreateContainerAsync(createContainerParam, default); + var createdContainer = await client.PutContainerAsync(createContainerParam, default); var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer), default); Assert.NotNull(container); @@ -502,7 +500,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), PrmWait.DefaultParams); - var container = await client.CreateContainerAsync(createContainerParam, ctx); + var container = await client.PutContainerAsync(createContainerParam, ctx); var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container), ctx); Assert.NotNull(containerInfo); @@ -583,7 +581,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), lightWait); - var containerId = await client.CreateContainerAsync(createContainerParam, default); + var containerId = await client.PutContainerAsync(createContainerParam, default); var ctx = new CallContext(TimeSpan.FromSeconds(10)); @@ -685,30 +683,4 @@ public class MultithreadSmokeClientTests : SmokeTestsBase Assert.Single(result.Addresses); Assert.Equal(9, result.Attributes.Count); } - - private static byte[] GetRandomBytes(int size) - { - Random rnd = new(); - var bytes = new byte[size]; - rnd.NextBytes(bytes); - return bytes; - } - - private static IOptions GetClientOptions(string key, string url) - { - return Options.Create(new ClientSettings - { - Key = key, - Host = url - }); - } - - - static async Task Cleanup(IFrostFSClient client) - { - await foreach (var cid in client.ListContainersAsync(default, default)) - { - await client.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default); - } - } } diff --git a/src/FrostFS.SDK.Tests/Smoke/PoolTests/PoolSmokeTests.cs b/src/FrostFS.SDK.Tests/Smoke/PoolTests/PoolSmokeTests.cs index 94b2c8b..9e7ea80 100644 --- a/src/FrostFS.SDK.Tests/Smoke/PoolTests/PoolSmokeTests.cs +++ b/src/FrostFS.SDK.Tests/Smoke/PoolTests/PoolSmokeTests.cs @@ -10,8 +10,6 @@ namespace FrostFS.SDK.Tests.Smoke; [SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "No secure purpose")] public class PoolSmokeTests : SmokeTestsBase { - private static readonly PrmWait lightWait = new(100, 1); - private InitParameters GetDefaultParams() { return new InitParameters((url) => Grpc.Net.Client.GrpcChannel.ForAddress(new Uri(url))) @@ -168,7 +166,7 @@ public class PoolSmokeTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["key1", "value1"]); - var containerId = await pool.CreateContainerAsync(createContainerParam, default); + var containerId = await pool.PutContainerAsync(createContainerParam, default); var bytes = GetRandomBytes(1024); @@ -217,7 +215,7 @@ public class PoolSmokeTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), lightWait); - var containerId = await pool.CreateContainerAsync(createContainerParam, default); + var containerId = await pool.PutContainerAsync(createContainerParam, default); var bytes = new byte[] { 1, 2, 3 }; @@ -315,7 +313,7 @@ public class PoolSmokeTests : SmokeTestsBase PrmWait.DefaultParams, xheaders: ["testKey", "testValue"]); - var createdContainer = await pool.CreateContainerAsync(createContainerParam, ctx); + var createdContainer = await pool.PutContainerAsync(createContainerParam, ctx); var container = await pool.GetContainerAsync(new PrmContainerGet(createdContainer), default); Assert.NotNull(container); @@ -399,7 +397,7 @@ public class PoolSmokeTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), PrmWait.DefaultParams); - var container = await pool.CreateContainerAsync(createContainerParam, ctx); + var container = await pool.PutContainerAsync(createContainerParam, ctx); var containerInfo = await pool.GetContainerAsync(new PrmContainerGet(container), ctx); Assert.NotNull(containerInfo); @@ -483,7 +481,7 @@ public class PoolSmokeTests : SmokeTestsBase new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))), lightWait); - var containerId = await pool.CreateContainerAsync(createContainerParam, default); + var containerId = await pool.PutContainerAsync(createContainerParam, default); var ctx = new CallContext(TimeSpan.FromSeconds(10)); @@ -545,20 +543,4 @@ public class PoolSmokeTests : SmokeTestsBase Assert.Fail($"Container {cid.GetValue()} exist"); } } - - private static byte[] GetRandomBytes(int size) - { - Random rnd = new(); - var bytes = new byte[size]; - rnd.NextBytes(bytes); - return bytes; - } - - static async Task Cleanup(Pool pool) - { - await foreach (var cid in pool.ListContainersAsync(default, default)) - { - await pool.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default).ConfigureAwait(true); - } - } } diff --git a/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs b/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs index e126677..ac76a54 100644 --- a/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs +++ b/src/FrostFS.SDK.Tests/Smoke/SmokeTestsBase.cs @@ -1,6 +1,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; +using System.Security.Principal; using System.Text; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Interfaces; @@ -9,6 +10,7 @@ using FrostFS.SDK.Cryptography; using Grpc.Core; using Microsoft.Extensions.Options; +using Xunit.Abstractions; namespace FrostFS.SDK.Tests.Smoke; @@ -60,36 +62,55 @@ public abstract class SmokeTestsBase } protected static async Task CreateContainer(IFrostFSClient client, + CallContext ctx, + FrostFsSessionToken? token, bool unique, uint backupFactor, Collection selectors, Collection filter, + Collection containerAttributes, params FrostFsReplica[] replicas) { - var networkSettings = await client.GetNetworkSettingsAsync(default); + ArgumentNullException.ThrowIfNull(client); + var networkSettings = await client.GetNetworkSettingsAsync(ctx); + + var attributes = containerAttributes ?? []; + + if (networkSettings.HomomorphicHashingDisabled) + attributes.Add(new("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")); + var containerInfo = new FrostFsContainerInfo( new FrostFsPlacementPolicy(unique, backupFactor, selectors, filter, replicas), - networkSettings.HomomorphicHashingDisabled ? [new FrostFsAttributePair("__SYSTEM__DISABLE_HOMOMORPHIC_HASHING", "true")] : []); + [.. attributes]); var createContainerParam = new PrmContainerCreate( containerInfo, PrmWait.DefaultParams, + token, xheaders: ["key1", "value1"]); - return await client.CreateContainerAsync(createContainerParam, default); + return await client.PutContainerAsync(createContainerParam, ctx); } protected static async Task Cleanup(IFrostFSClient client) { + ArgumentNullException.ThrowIfNull(client); + + var tasks = new List(); await foreach (var cid in client.ListContainersAsync(default, default)) { - await client.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default); + tasks.Add(client.DeleteContainerAsync(new PrmContainerDelete(cid, lightWait), default)); } + + await Task.WhenAll(tasks); } protected static async Task AddObjectRules(IFrostFSClient client, FrostFsContainerId containerId) { + ArgumentNullException.ThrowIfNull(client); + ArgumentNullException.ThrowIfNull(containerId); + var addChainPrm = new PrmApeChainAdd( new FrostFsChainTarget(FrostFsTargetType.Container, containerId.GetValue()), new FrostFsChain diff --git a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs index 2088b4b..db1e193 100644 --- a/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs +++ b/src/FrostFS.SDK.Tests/Unit/ContainerTest.cs @@ -16,7 +16,7 @@ public class ContainerTest : ContainerTestsBase { var param = new PrmContainerCreate(new FrostFsContainerInfo(Mocker.PlacementPolicy), PrmWait.DefaultParams); - var result = await GetClient().CreateContainerAsync(param, default); + var result = await GetClient().PutContainerAsync(param, default); Assert.NotNull(result); Assert.NotNull(result.GetValue());