using System.Security.Cryptography; using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Netmap; using Grpc.Core; using Grpc.Net.Client; using Microsoft.Extensions.Options; using Grpc.Core.Interceptors; using System.Diagnostics; namespace FrostFS.SDK.Tests; public class ClientTestLive { private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; private readonly string url = "http://172.29.238.97:8080"; [Fact] public async void NetworkMapTest() { using var fsClient = Client.GetInstance(GetOptions(this.key, this.url)); var result = await fsClient.GetNetmapSnapshotAsync(); Assert.True(result.Epoch > 0); Assert.Single(result.NodeInfoCollection); var item = result.NodeInfoCollection[0]; Assert.Equal(2, item.Version.Major); Assert.Equal(13, item.Version.Minor); Assert.Equal(NodeState.Online, item.State); Assert.True(item.PublicKey.Length > 0); Assert.Single(item.Addresses); Assert.Equal(9, item.Attributes.Count); } [Fact] public async void NodeInfoTest() { using var fsClient = Client.GetInstance(GetOptions(this.key, this.url)); var result = await fsClient.GetNodeInfoAsync(); Assert.Equal(2, result.Version.Major); Assert.Equal(13, result.Version.Minor); Assert.Equal(NodeState.Online, result.State); Assert.True(result.PublicKey.Length > 0); Assert.Single(result.Addresses); Assert.Equal(9, result.Attributes.Count); } [Fact] public async void SimpleScenarioTest() { using var fsClient = Client.GetInstance(GetOptions(this.key, this.url)); await Cleanup(fsClient); var containerId = await fsClient.CreateContainerAsync( new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))), new Context { Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) } ); var context = new Context { Timeout = TimeSpan.FromSeconds(10), Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) }; var container = await GetContainer(fsClient, containerId, context); Assert.NotNull(container); Random rnd = new(); var bytes = new byte[6 * 1024 * 1024 + 100]; rnd.NextBytes(bytes); var param = new PutObjectParameters { Header = new ObjectHeader( containerId: containerId, type: ObjectType.Regular, new ObjectAttribute("fileName", "test")), Payload = new MemoryStream(bytes), ClientCut = false }; var objectId = await fsClient.PutObjectAsync(param, new Context { Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) }); var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test"); bool hasObject = false; await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter])) { hasObject = true; var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId); Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); Assert.Single(objHeader.Attributes); Assert.Equal("fileName", objHeader.Attributes.First().Key); Assert.Equal("test", objHeader.Attributes.First().Value); } Assert.True(hasObject); var @object = await fsClient.GetObjectAsync(containerId, objectId!); var downloadedBytes = new byte[@object.Header.PayloadLength]; MemoryStream ms = new(downloadedBytes); byte[]? chunk = null; while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) { ms.Write(chunk); } Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes)); await Cleanup(fsClient); await Task.Delay(2000); await foreach (var _ in fsClient.ListContainersAsync()) { Assert.Fail("Containers exist"); } } [Fact] public async void ClientCutScenarioTest() { using var fsClient = Client.GetInstance(GetOptions(this.key, this.url)); await Cleanup(fsClient); var containerId = await fsClient.CreateContainerAsync( new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)))); var context = new Context { Timeout = TimeSpan.FromSeconds(10) }; var metrics = new MetricsInterceptor(); context.Interceptors.Add(metrics); var container = await GetContainer(fsClient, containerId, context); Assert.NotNull(container); Random rnd = new(); var bytes = new byte[6 * 1024 * 1024 + 100]; rnd.NextBytes(bytes); var param = new PutObjectParameters { Header = new ObjectHeader( containerId: containerId, type: ObjectType.Regular, new ObjectAttribute("fileName", "test")), Payload = new MemoryStream(bytes), ClientCut = true }; var objectId = await fsClient.PutObjectAsync(param); var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test"); bool hasObject = false; await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter])) { hasObject = true; var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId); Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); Assert.Single(objHeader.Attributes); Assert.Equal("fileName", objHeader.Attributes.First().Key); Assert.Equal("test", objHeader.Attributes.First().Value); } Assert.True(hasObject); var @object = await fsClient.GetObjectAsync(containerId, objectId!); var downloadedBytes = new byte[@object.Header.PayloadLength]; MemoryStream ms = new(downloadedBytes); byte[]? chunk = null; while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) { ms.Write(chunk); } await Cleanup(fsClient); await Task.Delay(2000); await foreach (var _ in fsClient.ListContainersAsync()) { Assert.Fail("Containers exist"); } } private static IOptions GetOptions(string key, string url) { var settings = new ClientSettings { Key = key, Host = url }; return Options.Create(settings); } static async Task Cleanup(IFrostFSClient fsClient) { await foreach (var cid in fsClient.ListContainersAsync()) { await fsClient.DeleteContainerAsync(cid); } } static async Task GetContainer(IFrostFSClient fsClient, ContainerId id, Context ctx) { while (true) { try { await Task.Delay(100); return await fsClient.GetContainerAsync(id, ctx); } catch (ApplicationException) { if (DateTime.UtcNow >= ctx.Deadline) throw new TimeoutException(); } catch (Grpc.Core.RpcException) { throw; } } } } public class MetricsInterceptor() : Interceptor { public override AsyncUnaryCall AsyncUnaryCall( TRequest request, ClientInterceptorContext context, AsyncUnaryCallContinuation continuation) { var call = continuation(request, context); return new AsyncUnaryCall( HandleUnaryResponse(call), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); } private async Task HandleUnaryResponse(AsyncUnaryCall call) { var watch = new Stopwatch(); watch.Start(); var response = await call.ResponseAsync; watch.Stop(); var elapsed = watch.ElapsedTicks * 1_000_000/Stopwatch.Frequency; return response; } }