diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs index ca1acea..6e9cbd0 100644 --- a/src/FrostFS.SDK.ClientV2/Client.cs +++ b/src/FrostFS.SDK.ClientV2/Client.cs @@ -13,7 +13,6 @@ using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Net.Http; -using System.Security.Cryptography; using System.Threading.Tasks; using Version = FrostFS.SDK.ModelsV2.Version; @@ -168,7 +167,7 @@ public class Client : IFrostFSClient return service.GetObjectHeadAsync(containerId, objectId, ctx!); } - public Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default) + public Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default) { var service = GetObjectService(ref ctx); return service.GetObjectAsync(containerId, objectId, ctx!); @@ -180,7 +179,7 @@ public class Client : IFrostFSClient return service.PutObjectAsync(putObjectParameters, ctx!); } - public Task PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default) + public Task PutSingleObjectAsync(FrostFsObject obj, Context? ctx = default) { var service = GetObjectService(ref ctx); return service.PutSingleObjectAsync(obj, ctx!); @@ -312,26 +311,21 @@ public class Client : IFrostFSClient private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions) { - Uri uri; try { - uri = new Uri(host); + var uri = new Uri(host); + + if (channelOptions != null) + return GrpcChannel.ForAddress(uri, channelOptions); + + return GrpcChannel.ForAddress(uri, new GrpcChannelOptions + { + HttpHandler = new HttpClientHandler() + }); } catch (UriFormatException e) { - var msg = $"Host '{host}' has invalid format. Error: {e.Message}"; - throw new ArgumentException(msg); + throw new ArgumentException($"Host '{host}' has invalid format. Error: {e.Message}"); } - - if (channelOptions != null) - { - return GrpcChannel.ForAddress(uri, channelOptions); - } - - - return GrpcChannel.ForAddress(uri, new GrpcChannelOptions - { - HttpHandler = new HttpClientHandler() - }); } } diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs index df5bcce..bed4136 100644 --- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs @@ -34,11 +34,11 @@ public interface IFrostFSClient : IDisposable #region Object Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default); - Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); + Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default); - Task PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default); + Task PutSingleObjectAsync(FrostFsObject obj, Context? context = default); Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs index be61b13..2804b65 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs @@ -4,9 +4,9 @@ namespace FrostFS.SDK.ClientV2.Mappers.GRPC; public static class ObjectMapper { - public static ModelsV2.Object ToModel(this Object.Object obj) + public static FrostFsObject ToModel(this Object.Object obj) { - return new ModelsV2.Object( + return new FrostFsObject( ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()), obj.Header.ToModel(), obj.Payload.ToByteArray()); diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs index 6ded9d2..2292811 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs @@ -44,7 +44,7 @@ public static class ObjectHeaderMapper SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null }; - if (split.Children != null && split.Children.Any()) + if (split.Children != null && split.Children.Count != 0) head.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage())); } @@ -81,7 +81,7 @@ public static class ObjectHeaderMapper Previous = header.Split.Previous?.ToModel() }; - if (header.Split.Children.Any()) + if (header.Split.Children.Count != 0) model.Split.Children.AddRange(header.Split.Children.Select(x => x.ToModel())); } diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs index 4b85759..e565891 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs @@ -1,5 +1,3 @@ - -using System; using Google.Protobuf; namespace FrostFS.SDK.ClientV2; diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs index b2aadad..6a3036b 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs @@ -1,11 +1,11 @@ using System.Threading.Tasks; using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; -using FrostFS.SDK.ModelsV2; using FrostFS.Container; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using System.Collections.Generic; +using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2; diff --git a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs index 1060a20..441d13b 100644 --- a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs @@ -51,10 +51,6 @@ internal class NetmapServiceProvider : ContextAccessor var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); - //var response = await Context.InvokeAsyncUnaryWithMetrics(() => - // netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken), - // nameof(netmapServiceClient.LocalNodeInfoAsync)); - Verifier.CheckResponse(response); return response.Body.ToModel(); diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs index 2439c6e..1937e70 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs @@ -43,7 +43,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C return response.Body.Header.Header.ToModel(); } - internal async Task GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx) + internal async Task GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx) { var sessionToken = await GetOrCreateSession(ctx); @@ -70,9 +70,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C request.Sign(Context.Key); - var obj = await GetObject(request, ctx); - - return obj; + return await GetObject(request, ctx); } internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx) @@ -89,7 +87,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C return PutStreamObject(parameters, ctx); } - internal async Task PutSingleObjectAsync(ModelsV2.Object modelObject, Context ctx) + internal async Task PutSingleObjectAsync(FrostFsObject modelObject, Context ctx) { var sessionToken = await GetOrCreateSession(ctx); @@ -178,7 +176,8 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C ObjectId? objectId; List sentObjectIds = []; - ModelsV2.Object? currentObject; + + FrostFsObject? currentObject; var networkSettings = await Context.Client.GetNetworkSettingsAsync(ctx); @@ -199,7 +198,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C largeObject.AppendBlock(buffer, bytesCount); - currentObject = new ModelsV2.Object(header.ContainerId, bytesCount < partSize ? buffer.Take(bytesCount).ToArray() : buffer) + currentObject = new FrostFsObject(header.ContainerId, bytesCount < partSize ? buffer.Take(bytesCount).ToArray() : buffer) .SetSplit(split); if (largeObject.PayloadLength == fullLength) @@ -231,6 +230,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } currentObject.AddAttributes(parameters.Header!.Attributes); + return await PutSingleObjectAsync(currentObject, ctx); } @@ -247,7 +247,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C var oid = new ObjectID { Value = hdr.Sha256() }; - var request = new PutRequest + var initRequest = new PutRequest { Body = new PutRequest.Types.Body { @@ -258,8 +258,8 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C } }; - request.AddMetaHeader(); - request.AddObjectSessionToken( + initRequest.AddMetaHeader(); + initRequest.AddObjectSessionToken( sessionToken, hdr.ContainerId, oid, @@ -267,9 +267,10 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C Context.Key ); - request.Sign(Context.Key); + initRequest.Sign(Context.Key); - using var stream = await PutObjectInit(request, ctx); + using var stream = await PutObjectInit(initRequest, ctx); + var buffer = new byte[Constants.ObjectChunkSize]; while (true) @@ -279,14 +280,17 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C if (bufferLength == 0) break; - request.Body = new PutRequest.Types.Body + var chunkRequest = new PutRequest(initRequest) { - Chunk = ByteString.CopyFrom(buffer[..bufferLength]), + Body = new PutRequest.Types.Body + { + Chunk = ByteString.CopyFrom(buffer[..bufferLength]), + }, + VerifyHeader = null }; - request.VerifyHeader = null; - request.Sign(Context.Key); - await stream.Write(request); + chunkRequest.Sign(Context.Key); + await stream.Write(chunkRequest); } var response = await stream.Close(); @@ -295,7 +299,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()); } - private async Task GetObject(GetRequest request, Context ctx) + private async Task GetObject(GetRequest request, Context ctx) { var reader = GetObjectInit(request, ctx); diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs index 95ba626..35f68ae 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs @@ -19,7 +19,7 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx) return new ObjectID { Value = grpcHeader.Sha256() }.ToModel(); } - internal Object.Object CreateObject(ModelsV2.Object @object) + internal Object.Object CreateObject(FrostFsObject @object) { var grpcHeader = @object.Header.ToGrpcMessage(); @@ -100,5 +100,4 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx) Sum = ByteString.CopyFrom(data.Sha256()) }; } - } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs index b37e7fc..dae8ff2 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs @@ -1,4 +1,3 @@ -using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.ModelsV2; using Grpc.Net.Client; using System; diff --git a/src/FrostFS.SDK.ClientV2/Tools/Object.cs b/src/FrostFS.SDK.ClientV2/Tools/Object.cs index e7e59f1..0a0290e 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/Object.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Object.cs @@ -7,31 +7,31 @@ namespace FrostFS.SDK.ClientV2.Extensions; public static class ObjectExtensions { - public static ModelsV2.Object SetPayloadLength(this ModelsV2.Object obj, ulong length) + public static FrostFsObject SetPayloadLength(this FrostFsObject obj, ulong length) { obj.Header.PayloadLength = length; return obj; } - public static ModelsV2.Object AddAttribute(this ModelsV2.Object obj, string key, string value) + public static FrostFsObject AddAttribute(this FrostFsObject obj, string key, string value) { obj.AddAttribute(new ObjectAttribute(key, value)); return obj; } - public static ModelsV2.Object AddAttribute(this ModelsV2.Object obj, ObjectAttribute attribute) + public static FrostFsObject AddAttribute(this FrostFsObject obj, ObjectAttribute attribute) { obj.Header.Attributes.Add(attribute); return obj; } - public static ModelsV2.Object AddAttributes(this ModelsV2.Object obj, IEnumerable attributes) + public static FrostFsObject AddAttributes(this FrostFsObject obj, IEnumerable attributes) { obj.Header.Attributes.AddRange(attributes); return obj; } - public static ModelsV2.Object SetSplit(this ModelsV2.Object obj, Split split) + public static FrostFsObject SetSplit(this FrostFsObject obj, Split split) { obj.Header.Split = split; return obj; @@ -43,7 +43,7 @@ public static class ObjectExtensions return linkObject; } - public static ModelsV2.Object CalculateObjectId(this ModelsV2.Object obj) + public static FrostFsObject CalculateObjectId(this FrostFsObject obj) { if (obj.Payload == null) throw new MissingFieldException("Payload cannot be null"); diff --git a/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs index 6b1d4cf..6e2a5dc 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs @@ -2,6 +2,7 @@ using System.Security.Cryptography; using FrostFS.Refs; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ProtosV2.Interfaces; using FrostFS.Session; namespace FrostFS.SDK.ClientV2; diff --git a/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs index 6016ddf..d9fa633 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs @@ -12,6 +12,7 @@ using Org.BouncyCastle.Math; using FrostFS.Refs; using FrostFS.SDK.Cryptography; using FrostFS.Session; +using FrostFS.SDK.ProtosV2.Interfaces; namespace FrostFS.SDK.ClientV2; @@ -67,20 +68,21 @@ public static class RequestSigner { var hash = new byte[65]; hash[0] = 0x04; - key - .SignHash(SHA512.Create().ComputeHash(data)) - .CopyTo(hash, 1); + + key.SignHash(SHA512.Create().ComputeHash(data)).CopyTo(hash, 1); + return hash; } public static Signature SignMessagePart(this ECDsa key, IMessage? data) { - var data2Sign = data is null ? Array.Empty() : data.ToByteArray(); + var data2Sign = data is null ? [] : data.ToByteArray(); var sig = new Signature { Key = ByteString.CopyFrom(key.PublicKey()), Sign = ByteString.CopyFrom(key.SignData(data2Sign)), }; + return sig; } diff --git a/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs index 5811ff1..6e65c02 100644 --- a/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs @@ -13,6 +13,7 @@ using FrostFS.Refs; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using FrostFS.Session; +using FrostFS.SDK.ProtosV2.Interfaces; namespace FrostFS.SDK.ClientV2; diff --git a/src/FrostFS.SDK.ModelsV2/CallStatistics.cs b/src/FrostFS.SDK.ModelsV2/CallStatistics.cs index e6424b6..a6cbcbf 100644 --- a/src/FrostFS.SDK.ModelsV2/CallStatistics.cs +++ b/src/FrostFS.SDK.ModelsV2/CallStatistics.cs @@ -2,6 +2,6 @@ namespace FrostFS.SDK.ModelsV2; public class CallStatistics { - public string MethodName { get; set; } + public string? MethodName { get; set; } public long ElapsedMicroSeconds { get; set; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Container.cs b/src/FrostFS.SDK.ModelsV2/Containers/Container.cs similarity index 99% rename from src/FrostFS.SDK.ModelsV2/Container.cs rename to src/FrostFS.SDK.ModelsV2/Containers/Container.cs index 4533325..02543b3 100644 --- a/src/FrostFS.SDK.ModelsV2/Container.cs +++ b/src/FrostFS.SDK.ModelsV2/Containers/Container.cs @@ -1,5 +1,4 @@ using System; - using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Netmap; diff --git a/src/FrostFS.SDK.ModelsV2/ContainerId.cs b/src/FrostFS.SDK.ModelsV2/Containers/ContainerId.cs similarity index 100% rename from src/FrostFS.SDK.ModelsV2/ContainerId.cs rename to src/FrostFS.SDK.ModelsV2/Containers/ContainerId.cs diff --git a/src/FrostFS.SDK.ModelsV2/Context.cs b/src/FrostFS.SDK.ModelsV2/Context.cs index e486464..b1038ec 100644 --- a/src/FrostFS.SDK.ModelsV2/Context.cs +++ b/src/FrostFS.SDK.ModelsV2/Context.cs @@ -8,7 +8,7 @@ namespace FrostFS.SDK.ClientV2; public class Context() { - private List interceptors; + private List? interceptors; public CancellationToken CancellationToken { get; set; } = default; public TimeSpan Timeout { get; set; } = default; @@ -16,11 +16,11 @@ public class Context() public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null; - public Action Callback { get; set; } + public Action? Callback { get; set; } public List Interceptors { - get { return interceptors ??= []; } - set { interceptors = value; } + get { return this.interceptors ??= []; } + set { this.interceptors = value; } } } diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs similarity index 82% rename from src/FrostFS.SDK.ModelsV2/Object/Object.cs rename to src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs index e129601..4cbfaae 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/Object.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs @@ -4,16 +4,16 @@ using FrostFS.SDK.ModelsV2.Enums; namespace FrostFS.SDK.ModelsV2; -public class Object +public class FrostFsObject { - public Object(ObjectId objectId, ObjectHeader header, byte[] payload) + public FrostFsObject(ObjectId objectId, ObjectHeader header, byte[] payload) { ObjectId = objectId; Payload = payload; Header = header; } - public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular) + public FrostFsObject(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular) { Payload = payload; Header = new ObjectHeader(containerId: container, type: objectType, attributes: []); @@ -41,7 +41,7 @@ public class Object } } -public class LargeObject(ContainerId container) : Object(container, []) +public class LargeObject(ContainerId container) : FrostFsObject(container, []) { private readonly SHA256 payloadHash = SHA256.Create(); @@ -64,7 +64,7 @@ public class LargeObject(ContainerId container) : Object(container, []) } } -public class LinkObject : Object +public class LinkObject : FrostFsObject { public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, []) { diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs index 83bee87..715f903 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs @@ -1,35 +1,27 @@ using System.Collections.Generic; - using FrostFS.SDK.ModelsV2.Enums; namespace FrostFS.SDK.ModelsV2; -public class ObjectHeader +public class ObjectHeader( + ContainerId containerId, + ObjectType type = ObjectType.Regular, + params ObjectAttribute[] attributes + ) { public OwnerId? OwnerId { get; set; } - public List Attributes { get; set; } + public List Attributes { get; set; } = [.. attributes]; - public ContainerId ContainerId { get; set; } + public ContainerId ContainerId { get; set; } = containerId; public ulong PayloadLength { get; set; } public byte[]? PayloadCheckSum { get; set; } - public ObjectType ObjectType { get; set; } + public ObjectType ObjectType { get; set; } = type; public Version? Version { get; set; } public Split? Split { get; set; } - - public ObjectHeader( - ContainerId containerId, - ObjectType type = ObjectType.Regular, - params ObjectAttribute[] attributes - ) - { - Attributes = [.. attributes]; - ContainerId = containerId; - ObjectType = type; - } } diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs index 2bcf3ab..74e6bfb 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs @@ -4,21 +4,15 @@ using FrostFS.SDK.Cryptography; namespace FrostFS.SDK.ModelsV2; -public class ObjectId +public class ObjectId(string id) { - public string Value { get; } - - public ObjectId(string id) - { - Value = id; - } + public string Value { get; } = id; public static ObjectId FromHash(byte[] hash) { if (hash.Length != Constants.Sha256HashLength) - { throw new FormatException("ObjectID must be a sha256 hash."); - } + return new ObjectId(Base58.Encode(hash)); } diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs index 7efad42..42129c3 100644 --- a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs @@ -1,4 +1,6 @@ -namespace FrostFS.Session; +using FrostFS.Session; + +namespace FrostFS.SDK.ProtosV2.Interfaces; public interface IRequest : IVerifiableMessage { diff --git a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs index d8bd601..5d48399 100644 --- a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs @@ -1,6 +1,7 @@ using Google.Protobuf; using FrostFS.Session; +using FrostFS.SDK.ProtosV2.Interfaces; namespace FrostFS.Container; diff --git a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs index ba2d11e..2c1c8b6 100644 --- a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs @@ -1,4 +1,5 @@ -using FrostFS.Session; +using FrostFS.SDK.ProtosV2.Interfaces; +using FrostFS.Session; using Google.Protobuf; namespace FrostFS.Netmap; diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs index de933fb..dbfdd79 100644 --- a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using FrostFS.SDK.ProtosV2.Interfaces; using FrostFS.Session; using Google.Protobuf; diff --git a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs index 937af24..decf002 100644 --- a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs @@ -1,4 +1,5 @@ -using Google.Protobuf; +using FrostFS.SDK.ProtosV2.Interfaces; +using Google.Protobuf; namespace FrostFS.Session; diff --git a/src/FrostFS.SDK.Tests/ContainerTest.cs b/src/FrostFS.SDK.Tests/ContainerTest.cs index b915304..d41edb8 100644 --- a/src/FrostFS.SDK.Tests/ContainerTest.cs +++ b/src/FrostFS.SDK.Tests/ContainerTest.cs @@ -1,118 +1,112 @@ using FrostFS.SDK.ClientV2; -using FrostFS.SDK.Cryptography; using FrostFS.SDK.ModelsV2; -using FrostFS.SDK.ModelsV2.Enums; -using FrostFS.SDK.ModelsV2.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; using Microsoft.Extensions.Options; +using FrostFS.SDK.ModelsV2.Netmap; +using FrostFS.SDK.ModelsV2.Enums; +using Google.Protobuf; +using FrostFS.SDK.ClientV2.Interfaces; namespace FrostFS.SDK.Tests; -public class ContainerTest -{ - private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - [Fact] - public async void CreateContainerTest() +public abstract class ContainerTestsBase +{ + protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; + + protected IOptions Settings { get; set; } + protected ContainerMocker Mocker { get; set; } + + protected ContainerTestsBase() { - var factory = new PutContainerMockFactory(this.key) + Settings = Options.Create(new ClientSettings + { + Key = key, + Host = "http://localhost:8080" + }); + + Mocker = new ContainerMocker(this.key) { PlacementPolicy = new PlacementPolicy(true, new Replica(1)), Version = new ModelsV2.Version(2, 13), ContainerGuid = Guid.NewGuid() }; + } - var settings = Options.Create(new ClientSettings - { - Key = key, - Host = "http://localhost:8080" - }); + protected IFrostFSClient GetClient() + { + return ClientV2.Client.GetTestInstance( + Settings, + null, + new NetworkMocker(this.key).GetMock().Object, + new SessionMocker(this.key).GetMock().Object, + Mocker.GetMock().Object, + new ObjectMocker(this.key).GetMock().Object); + } +} - var fsClient = Client.GetTestInstance( - settings, - null, - new NetmapMockFactory(this.key).GetMock().Object, - new SessionMockFactory(this.key).GetMock().Object, - factory.GetMock().Object, - new ObjectMockFactory(this.key).GetMock().Object); - - var result = await fsClient.CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, factory.PlacementPolicy)); +public class ContainerTest : ContainerTestsBase +{ + [Fact] + public async void CreateContainerTest() + { + var result = await GetClient().CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, Mocker.PlacementPolicy)); Assert.NotNull(result); Assert.NotNull(result.Value); - Assert.True(Base58.Encode(factory.ContainerGuid.ToBytes()) == result.Value); + Assert.True(Base58.Encode(Mocker.ContainerGuid.ToBytes()) == result.Value); } [Fact] public async void GetContainerTest() { - var factory = new GetContainerMockFactory(this.key) - { - PlacementPolicy = new PlacementPolicy(true, new Replica(1)), - Version = new ModelsV2.Version(2, 13), - Acl = BasicAcl.PublicRO, - ContainerGuid = Guid.NewGuid(), - }; + var cid = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes())); - var settings = Options.Create(new ClientSettings - { - Key = key, - Host = "http://localhost:8080" - }); + Mocker.Acl = BasicAcl.PublicRO; - var fsClient = Client.GetTestInstance( - settings, - null, - new NetmapMockFactory(this.key).GetMock().Object, - new SessionMockFactory(this.key).GetMock().Object, - factory.GetMock().Object, - new ObjectMockFactory(this.key).GetMock().Object); - - var cid = new ContainerId(Base58.Encode(factory.ContainerGuid.ToBytes())); - - var context = new Context(); - - var result = await fsClient.GetContainerAsync(cid, context); + var result = await GetClient().GetContainerAsync(cid); Assert.NotNull(result); - Assert.Equal(factory.Acl, result.BasicAcl); - Assert.Equal(factory.ContainerGuid, result.Nonce); - Assert.Equal(0, factory.PlacementPolicy.CompareTo(result.PlacementPolicy)); - Assert.Equal(factory.Version.ToString(), result.Version!.ToString()); + Assert.Equal(Mocker.Acl, result.BasicAcl); + Assert.Equal(Mocker.ContainerGuid, result.Nonce); + Assert.Equal(0, Mocker.PlacementPolicy.CompareTo(result.PlacementPolicy)); + Assert.Equal(Mocker.Version.ToString(), result.Version!.ToString()); + } + + [Fact] + public async void GetContainerListTest() + { + Mocker.ContainerIds.Add([0xaa]); + Mocker.ContainerIds.Add([0xbb]); + Mocker.ContainerIds.Add([0xcc]); + + var result = GetClient().ListContainersAsync(); + + Assert.NotNull(result); + + int i = 0; + await foreach (var cid in result) + { + var val = Base58.Encode(ByteString.CopyFrom(Mocker.ContainerIds[i++]).ToByteArray()); + Assert.Equal(val, cid.Value); + } + + Assert.Equal(3, i); } [Fact] public async void DeleteContainerAsyncTest() { - var factory = new DeleteContainerMockFactory(this.key) - { - Version = new ModelsV2.Version(2, 13), - Acl = BasicAcl.PublicRW, - ContainerGuid = Guid.NewGuid(), - }; + var cid = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes())); - var settings = Options.Create(new ClientSettings - { - Key = key, - Host = "http://localhost:8080" - }); + await GetClient().DeleteContainerAsync(cid); - var fsClient = Client.GetTestInstance( - settings, - null, - new NetmapMockFactory(this.key).GetMock().Object, - new SessionMockFactory(this.key).GetMock().Object, - factory.GetMock().Object, - new ObjectMockFactory(this.key).GetMock().Object); + Assert.Single(Mocker.Requests); - var cid = new ContainerId(Base58.Encode(factory.ContainerGuid.ToBytes())); - - await fsClient.DeleteContainerAsync(cid); - - Assert.Single(factory.Requests); - - var request = factory.Requests.First(); + var request = Mocker.Requests.First(); Assert.Equal(cid.ToGrpcMessage(), request.Request.Body.ContainerId); } diff --git a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj index 931a072..2b74f0f 100644 --- a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj +++ b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj @@ -9,6 +9,10 @@ true + + + + @@ -25,4 +29,10 @@ + + + PreserveNewest + + + diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs new file mode 100644 index 0000000..45b17bc --- /dev/null +++ b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs @@ -0,0 +1,60 @@ +using FrostFS.Object; +using FrostFS.Session; +using Google.Protobuf; +using Grpc.Core; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; +using System.Security.Cryptography; + +namespace FrostFS.SDK.Tests; + +public class AsyncStreamReaderMock(string key, ObjectHeader objectHeader) : ServiceBase(key), IAsyncStreamReader +{ + public GetResponse Current + { + get + { + var header = new Header + { + ContainerId = objectHeader.ContainerId.ToGrpcMessage(), + PayloadLength = objectHeader.PayloadLength, + Version = objectHeader.Version!.ToGrpcMessage(), + OwnerId = objectHeader.OwnerId!.ToGrpcMessage() + }; + + foreach (var attr in objectHeader.Attributes) + header.Attributes.Add(attr.ToGrpcMessage()); + + var response = new GetResponse + { + Body = new GetResponse.Types.Body + { + Init = new GetResponse.Types.Body.Types.Init + { + Header = header, + ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Array.Empty())) }, + Signature = new Refs.Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Sign = ByteString.CopyFrom(Key.SignData(header.ToByteArray())), + } + } + }, + MetaHeader = new ResponseMetaHeader() + }; + + response.VerifyHeader = GetResponseVerificationHeader(response); + + return response; + } + } + + public Task MoveNext(CancellationToken cancellationToken) + { + return Task.FromResult(true); + } +} + diff --git a/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs b/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs new file mode 100644 index 0000000..fb1fe2d --- /dev/null +++ b/src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs @@ -0,0 +1,29 @@ +using Grpc.Core; +using FrostFS.SDK.ProtosV2.Interfaces; + +namespace FrostFS.SDK.Tests; + +public class ClientStreamWriter : IClientStreamWriter +{ + public List Messages { get; set; } = []; + public bool CompletedTask { get; private set; } + + public WriteOptions? WriteOptions + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public Task CompleteAsync() + { + CompletedTask = true; + return Task.CompletedTask; + } + + public Task WriteAsync(IRequest message) + { + Messages.Add(message); + return Task.CompletedTask; + } +} + diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs index 0ea346d..530294a 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs @@ -17,6 +17,7 @@ namespace FrostFS.SDK.Tests; public abstract class ServiceBase(string key) { + public string StringKey { get; private set; } = key; public ECDsa Key { get; private set; } = key.LoadWif(); public ModelsV2.Version Version { get; set; } = DefaultVersion; public BasicAcl Acl { get; set; } = DefaultAcl; diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock copy.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs similarity index 99% rename from src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock copy.cs rename to src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs index 3a17e88..c0b69a3 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock copy.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerStub.cs @@ -1,7 +1,6 @@ using FrostFS.Container; using Moq; - namespace FrostFS.SDK.Tests; public class ContainerStub(string key) : ContainerServiceBase(key) diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/DeleteContainerMock.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/DeleteContainerMock.cs deleted file mode 100644 index ba03978..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/DeleteContainerMock.cs +++ /dev/null @@ -1,54 +0,0 @@ -using FrostFS.Container; -using FrostFS.Session; -using Grpc.Core; -using Moq; - -namespace FrostFS.SDK.Tests; - -public class DeleteContainerMockFactory(string key) : ContainerServiceBase(key) -{ - public override Mock GetMock() - { - var mock = new Mock(); - - var v = mock.Setup(x => x.DeleteAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Requests.Add(new RequestData(r, m, dt, ct)); - - var response = new DeleteResponse - { - Body = new DeleteResponse.Types.Body(), - MetaHeader = new ResponseMetaHeader() - }; - - var metadata = new Metadata(); - - response.VerifyHeader = GetResponseVerificationHeader(response); - - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(metadata), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => metadata, - () => { }); - }); - - return mock; - } - - public List> Requests = []; -} - -public class RequestData(T request, Metadata m, DateTime? dt, CancellationToken ct) -{ - public T Request => request; - public Metadata Metadata => m; - public DateTime? deadline => dt; - public CancellationToken CancellationToken => ct; -} - \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs index 8b2a496..9a72f09 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs @@ -8,10 +8,13 @@ using FrostFS.SDK.ClientV2; using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.Session; +using FrostFS.Refs; +using System.Collections.Generic; namespace FrostFS.SDK.Tests; -public class GetContainerMockFactory(string key) : ContainerServiceBase(key) +public class ContainerMocker(string key) : ContainerServiceBase(key) { public override Mock GetMock() { @@ -19,7 +22,46 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key) var grpcVersion = Version.ToGrpcMessage(); - var response = new GetResponse + PutResponse putResponse = new() + { + Body = new PutResponse.Types.Body + { + ContainerId = new ContainerID + { + Value = ByteString.CopyFrom(ContainerGuid.ToBytes()) + } + }, + MetaHeader = new ResponseMetaHeader + { + Version = Version is null ? DefaultVersion.ToGrpcMessage() : Version.ToGrpcMessage(), + Epoch = 100, + Ttl = 1 + } + }; + + putResponse.VerifyHeader = GetResponseVerificationHeader(putResponse); + + var metadata = new Metadata(); + var putContainerResponse = new AsyncUnaryCall( + Task.FromResult(putResponse), + Task.FromResult(metadata), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => metadata, + () => { }); + + mock.Setup(x => x.PutAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((PutRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + return putContainerResponse; + }); + + var getResponse = new GetResponse { Body = new GetResponse.Types.Body { @@ -34,7 +76,7 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key) MetaHeader = ResponseMetaHeader }; - response.VerifyHeader = GetResponseVerificationHeader(response); + getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse); mock.Setup(x => x.GetAsync( It.IsAny(), @@ -46,13 +88,74 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key) Verifier.CheckRequest(r); return new AsyncUnaryCall( - Task.FromResult(response), + Task.FromResult(getResponse), Task.FromResult(ResponseMetaData), () => new Grpc.Core.Status(StatusCode.OK, string.Empty), () => ResponseMetaData, () => { }); }); + var listResponse = new ListResponse + { + Body = new ListResponse.Types.Body(), + MetaHeader = ResponseMetaHeader + }; + + foreach (var item in ContainerIds) + { + listResponse.Body.ContainerIds.Add(new ContainerID { Value = ByteString.CopyFrom(item) }); + } + + listResponse.VerifyHeader = GetResponseVerificationHeader(listResponse); + + mock.Setup(x => x.ListAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((ListRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + return new AsyncUnaryCall( + Task.FromResult(listResponse), + Task.FromResult(ResponseMetaData), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => ResponseMetaData, + () => { }); + }); + + var v = mock.Setup(x => x.DeleteAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Requests.Add(new RequestData(r, m, dt, ct)); + + var response = new DeleteResponse + { + Body = new DeleteResponse.Types.Body(), + MetaHeader = new ResponseMetaHeader() + }; + + var metadata = new Metadata(); + + response.VerifyHeader = GetResponseVerificationHeader(response); + + return new AsyncUnaryCall( + Task.FromResult(response), + Task.FromResult(metadata), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => metadata, + () => { }); + }); + return mock; } + + public List ContainerIds { get; set; } = []; + + public List> Requests { get; set; } = []; } diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/PutContainerMock.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/PutContainerMock.cs deleted file mode 100644 index c6abeab..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/PutContainerMock.cs +++ /dev/null @@ -1,88 +0,0 @@ -using FrostFS.Container; -using FrostFS.Session; -using Google.Protobuf; -using Grpc.Core; -using Moq; -using FrostFS.SDK.ClientV2; -using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; -using FrostFS.SDK.ClientV2.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using FrostFS.Refs; - -namespace FrostFS.SDK.Tests; - -public class PutContainerMockFactory(string key) : ContainerServiceBase(key) -{ - public override Mock GetMock() - { - var mock = new Mock(); - - PutResponse response = new() - { - Body = new PutResponse.Types.Body - { - ContainerId = new ContainerID - { - Value = ByteString.CopyFrom(ContainerGuid.ToBytes()) - } - }, - MetaHeader = new ResponseMetaHeader - { - Version = Version is null ? DefaultVersion.ToGrpcMessage() : Version.ToGrpcMessage(), - Epoch = 100, - Ttl = 1 - } - }; - - response.VerifyHeader = PutResponseVerificationHeader(response); - - var metadata = new Metadata(); - var putContainerResponse = new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(metadata), - () => new Grpc.Core.Status(StatusCode.OK, string.Empty), - () => metadata, - () => { }); - - mock.Setup(x => x.PutAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((PutRequest r, Metadata m, DateTime? dt, CancellationToken ct) => - { - Verifier.CheckRequest(r); - - return putContainerResponse; - }); - - return mock; - } - - private ResponseVerificationHeader PutResponseVerificationHeader(PutResponse response) - { - var verifyHeader = new ResponseVerificationHeader - { - MetaSignature = new Signature - { - Key = ByteString.CopyFrom(Key.PublicKey()), - Scheme = SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray())) - }, - BodySignature = new Signature - { - Key = ByteString.CopyFrom(Key.PublicKey()), - Scheme = SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray())) - }, - OriginSignature = new Signature - { - Key = ByteString.CopyFrom(Key.PublicKey()), - Scheme = SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData([])) - } - }; - - return verifyHeader; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs new file mode 100644 index 0000000..8af698b --- /dev/null +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/RequestData.cs @@ -0,0 +1,12 @@ +using Grpc.Core; + +namespace FrostFS.SDK.Tests; + +public class RequestData(T request, Metadata m, DateTime? dt, CancellationToken ct) +{ + public T Request => request; + public Metadata Metadata => m; + public DateTime? Deadline => dt; + public CancellationToken CancellationToken => ct; +} + \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/Mocks/NetmapMock.cs b/src/FrostFS.SDK.Tests/Mocks/NetmapMock.cs deleted file mode 100644 index 07264ff..0000000 --- a/src/FrostFS.SDK.Tests/Mocks/NetmapMock.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Moq; -using FrostFS.Netmap; - -namespace FrostFS.SDK.Tests; - -public class NetmapMockFactory(string key) : ServiceBase(key) -{ - public Mock GetMock() - { - var mock = new Mock(); - - return mock; - } -} diff --git a/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs b/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs new file mode 100644 index 0000000..6cef45f --- /dev/null +++ b/src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs @@ -0,0 +1,78 @@ +using Moq; +using FrostFS.Netmap; +using Grpc.Core; +using FrostFS.SDK.ClientV2; +using Google.Protobuf; +using NuGet.Frameworks; + +namespace FrostFS.SDK.Tests; + +public class NetworkMocker(string key) : ServiceBase(key) +{ + private static readonly string[] parameterKeys = [ + "ContainerFee", + "EpochDuration", + "IRCandidateFee", + "MaxECDataCount", + "MaxECParityCount", + "MaxObjectSize", + "WithdrawalFee", + "HomomorphicHashingDisabled", + "MaintenanceModeAllowed" ]; + + public Dictionary? Parameters { get; set; } + + public Mock GetMock() + { + var mock = new Mock(); + + var networkInfoResponse = new NetworkInfoResponse(); + + var networkConfig = new NetworkConfig(); + + foreach (var key in parameterKeys) + { + networkConfig.Parameters.Add(new NetworkConfig.Types.Parameter + { + Key = ByteString.CopyFromUtf8(key), + Value = (Parameters != null && Parameters.TryGetValue(key, out byte[]? value)) ? ByteString.CopyFrom(value) : ByteString.CopyFrom(0) + }); + } + + var response = new NetworkInfoResponse + { + Body = new NetworkInfoResponse.Types.Body + { + NetworkInfo = new NetworkInfo + { + CurrentEpoch = 99, + MagicNumber = 13, + MsPerBlock = 999, + NetworkConfig = networkConfig + } + }, + MetaHeader = ResponseMetaHeader + }; + + response.VerifyHeader = GetResponseVerificationHeader(response); + + mock.Setup(x => x.NetworkInfoAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((NetworkInfoRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + return new AsyncUnaryCall( + Task.FromResult(response), + Task.FromResult(ResponseMetaData), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => ResponseMetaData, + () => { }); + }); + + return mock; + } +} diff --git a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs index 1d6c9e1..db4df1e 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs @@ -1,101 +1,208 @@ -using Moq; -using FrostFS.Object; -using Grpc.Core; -using FrostFS.SDK.ClientV2; -using FrostFS.Session; using Google.Protobuf; +using Grpc.Core; +using Moq; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ModelsV2; +using FrostFS.Object; +using FrostFS.SDK.ClientV2.Mappers.GRPC; using System.Security.Cryptography; using FrostFS.SDK.Cryptography; -using FrostFS.SDK.ClientV2.Mappers.GRPC; -using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.Tests; -public class ObjectMockFactory(string key) : ObjectServiceBase(key) +public class ObjectMocker(string key) : ObjectServiceBase(key) { public override Mock GetMock() { var mock = new Mock(); - GetResponse response = new() + if (ObjectHeader != null) { - Body = new GetResponse.Types.Body - { - }, - MetaHeader = ResponseMetaHeader - }; - - response.VerifyHeader = GetResponseVerificationHeader(response); - - mock.Setup(x => x.Get( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + mock.Setup(x => x.Get( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) => { Verifier.CheckRequest(r); - return new AsyncServerStreamingCall( - new AsyncStreamReaderMock(key, ObjectHeader), + return new AsyncServerStreamingCall( + new AsyncStreamReaderMock(StringKey, ObjectHeader), Task.FromResult(ResponseMetaData), () => new Grpc.Core.Status(StatusCode.OK, string.Empty), () => ResponseMetaData, () => { }); }); + HeadResponse ??= new Header + { + CreationEpoch = 99, + ContainerId = ObjectHeader.ContainerId.ToGrpcMessage(), + ObjectType = ObjectType.Regular, + OwnerId = ObjectHeader.OwnerId!.ToGrpcMessage(), + PayloadLength = 1, + PayloadHash = new Refs.Checksum { Type = Refs.ChecksumType.Sha256, Sum = ByteString.CopyFrom(SHA256.HashData([0xff])) }, + Version = ObjectHeader.Version!.ToGrpcMessage() + }; + + HeadResponse headResponse = new() + { + Body = new HeadResponse.Types.Body + { + Header = new HeaderWithSignature + { + Header = HeadResponse + } + }, + MetaHeader = ResponseMetaHeader + }; + + headResponse.Body.Header.Header.Attributes.Add(new Header.Types.Attribute { Key = "k", Value = "v" }); + + headResponse.Body.Header.Signature = new Refs.Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Sign = ByteString.CopyFrom(Key.SignData(headResponse.Body.Header.ToByteArray())), + }; + + headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse); + + mock.Setup(x => x.HeadAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((HeadRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + HeadRequests.Add(r); + + return new AsyncUnaryCall( + Task.FromResult(headResponse), + Task.FromResult(ResponseMetaData), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => ResponseMetaData, + () => { }); + }); + + } + + if (ResultObjectId != null) + { + PutResponse putResponse = new() + { + Body = new PutResponse.Types.Body + { + ObjectId = new Refs.ObjectID + { + Value = ByteString.CopyFrom(ResultObjectId) + } + }, + MetaHeader = ResponseMetaHeader, + }; + + putResponse.VerifyHeader = GetResponseVerificationHeader(putResponse); + + mock.Setup(x => x.Put( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((Metadata m, DateTime? dt, CancellationToken ct) => + { + return new AsyncClientStreamingCall( + ClientStreamWriter!, + Task.FromResult(putResponse), + Task.FromResult(ResponseMetaData), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => ResponseMetaData, + () => { }); + }); + } + + PutSingleResponse putSingleResponse = new() + { + Body = new PutSingleResponse.Types.Body(), + MetaHeader = ResponseMetaHeader, + }; + + putSingleResponse.VerifyHeader = GetResponseVerificationHeader(putSingleResponse); + + mock.Setup(x => x.PutSingleAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((PutSingleRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + PutSingleRequests.Add(r); + + return new AsyncUnaryCall( + Task.FromResult(putSingleResponse), + Task.FromResult(ResponseMetaData), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => ResponseMetaData, + () => { }); + }); + + if (ObjectId != null) + { + DeleteResponse deleteResponse = new() + { + Body = new DeleteResponse.Types.Body + { + Tombstone = new Refs.Address + { + ContainerId = ObjectHeader!.ContainerId.ToGrpcMessage(), + ObjectId = ObjectId.ToGrpcMessage() + } + }, + MetaHeader = ResponseMetaHeader + }; + + deleteResponse.VerifyHeader = GetResponseVerificationHeader(deleteResponse); + + mock.Setup(x => x.DeleteAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + DeleteRequests.Add(r); + + return new AsyncUnaryCall( + Task.FromResult(deleteResponse), + Task.FromResult(ResponseMetaData), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => ResponseMetaData, + () => { }); + }); + } + + return mock; } - public ObjectHeader ObjectHeader { get; set; } -} - -public class AsyncStreamReaderMock(string key, ObjectHeader objectHeader) : ServiceBase(key), IAsyncStreamReader -{ - public GetResponse Current - { - get - { - var ecdsaKey = key.LoadWif(); - - var header = new Header - { - ContainerId = objectHeader.ContainerId.ToGrpcMessage(), - PayloadLength = objectHeader.PayloadLength, - Version = objectHeader.Version!.ToGrpcMessage(), - OwnerId = objectHeader.OwnerId!.ToGrpcMessage() - }; - - foreach (var attr in objectHeader.Attributes) - header.Attributes.Add(attr.ToGrpcMessage()); - - var response = new GetResponse - { - Body = new GetResponse.Types.Body - { - Init = new GetResponse.Types.Body.Types.Init - { - Header = header, - ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Array.Empty())) }, - Signature = new Refs.Signature - { - Key = ByteString.CopyFrom(ecdsaKey.PublicKey()), - Sign = ByteString.CopyFrom(ecdsaKey.SignData(header.ToByteArray())), - } - } - }, - MetaHeader = new ResponseMetaHeader() - }; - - response.VerifyHeader = GetResponseVerificationHeader(response); - - return response; - } - } - - public Task MoveNext(CancellationToken cancellationToken) - { - return Task.FromResult(true); - } + public ObjectId? ObjectId { get; set; } + + public ObjectHeader? ObjectHeader { get; set; } + + public Header? HeadResponse { get; set; } + + public byte[]? ResultObjectId { get; set; } + + public ClientStreamWriter? ClientStreamWriter { get; private set; } = new (); + + public List PutSingleRequests { get; private set; } = []; + + public List DeleteRequests { get; private set; } = []; + + public List HeadRequests { get; private set; } = []; } diff --git a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs index 83beaf8..094090d 100644 --- a/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/SessionMock.cs @@ -6,7 +6,7 @@ using Google.Protobuf; namespace FrostFS.SDK.Tests; -public class SessionMockFactory(string key) : ServiceBase(key) +public class SessionMocker(string key) : ServiceBase(key) { public byte[]? SessionId { get; set; } diff --git a/src/FrostFS.SDK.Tests/ObjectTest.cs b/src/FrostFS.SDK.Tests/ObjectTest.cs index 53c2a9a..399dc07 100644 --- a/src/FrostFS.SDK.Tests/ObjectTest.cs +++ b/src/FrostFS.SDK.Tests/ObjectTest.cs @@ -1,66 +1,237 @@ +using FrostFS.Refs; using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Netmap; +using Google.Protobuf; using Microsoft.Extensions.Options; - using System.Security.Cryptography; +using System.Text; namespace FrostFS.SDK.Tests; -public class ObjectTest +public abstract class ObjectTestsBase { - private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; + protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; - [Fact] - public async void GetObjectTest() + protected IOptions Settings { get; set; } + protected ContainerId ContainerId { get; set; } + + protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key); + protected SessionMocker SessionMocker { get; set; } = new SessionMocker(key); + protected ContainerMocker ContainerMocker { get; set; } = new ContainerMocker(key); + protected ObjectMocker Mocker { get; set; } + + protected ObjectTestsBase() { var ecdsaKey = key.LoadWif(); - ContainerId cntId = new("xyz"); - - ObjectHeader header = new(cntId, ModelsV2.Enums.ObjectType.Regular, [new ObjectAttribute("k", "v")]) - { - PayloadLength = 1, - Version = new ModelsV2.Version(2, 13), - OwnerId = OwnerId.FromKey(ecdsaKey) - }; - - var objectMockFactory = new ObjectMockFactory(this.key) - { - PlacementPolicy = new PlacementPolicy(true, new Replica(1)), - Version = new ModelsV2.Version(2, 13), - ContainerGuid = Guid.NewGuid(), - ObjectHeader = header - }; - var settings = Options.Create(new ClientSettings + Settings = Options.Create(new ClientSettings { Key = key, Host = "http://localhost:8080" }); - var fsClient = Client.GetTestInstance( - settings, + Mocker = new ObjectMocker(key) + { + PlacementPolicy = new PlacementPolicy(true, new Replica(1)), + Version = new ModelsV2.Version(2, 13), + ContainerGuid = Guid.NewGuid() + }; + + ContainerId = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes())); + + Mocker.ObjectHeader = new(ContainerId, ModelsV2.Enums.ObjectType.Regular, [new ObjectAttribute("k", "v")]) + { + PayloadLength = 1, + Version = new ModelsV2.Version(2, 13), + OwnerId = OwnerId.FromKey(ecdsaKey) + }; + } + + protected IFrostFSClient GetClient() + { + return Client.GetTestInstance( + Settings, null, - new NetmapMockFactory(this.key).GetMock().Object, - new SessionMockFactory(this.key).GetMock().Object, - new ContainerStub(this.key).GetMock().Object, - objectMockFactory.GetMock().Object); + NetworkMocker.GetMock().Object, + SessionMocker.GetMock().Object, + ContainerMocker.GetMock().Object, + Mocker.GetMock().Object); + } +} - var objectId = fsClient.CalculateObjectId(header); +public class ObjectTest : ObjectTestsBase +{ + [Fact] + public async void GetObjectTest() + { + var client = GetClient(); + var objectId = client.CalculateObjectId(Mocker.ObjectHeader!); - var containerId = new ContainerId(Base58.Encode(objectMockFactory.ContainerGuid.ToBytes())); + var context = new Context + { + Timeout = TimeSpan.FromSeconds(2) + }; - var result = await fsClient.GetObjectAsync(containerId, objectId); + var result = await client.GetObjectAsync(ContainerId, objectId, context); Assert.NotNull(result); - Assert.Equal(header.ContainerId.Value, result.Header.ContainerId.Value); - Assert.Equal(header.OwnerId.ToGrpcMessage().ToString(), result.Header.OwnerId!.Value); - Assert.Equal(header.PayloadLength, result.Header.PayloadLength); + Assert.Equal(Mocker.ObjectHeader!.ContainerId.Value, result.Header.ContainerId.Value); + Assert.Equal(Mocker.ObjectHeader!.OwnerId!.ToGrpcMessage().ToString(), result.Header.OwnerId!.Value); + Assert.Equal(Mocker.ObjectHeader.PayloadLength, result.Header.PayloadLength); Assert.Single(result.Header.Attributes); - Assert.Equal(header.Attributes[0].Key, result.Header.Attributes[0].Key); - Assert.Equal(header.Attributes[0].Value,result.Header.Attributes[0].Value); + Assert.Equal(Mocker.ObjectHeader.Attributes[0].Key, result.Header.Attributes[0].Key); + Assert.Equal(Mocker.ObjectHeader.Attributes[0].Value,result.Header.Attributes[0].Value); + } + + [Fact] + public async void PutObjectTest() + { + Mocker.ResultObjectId = SHA256.HashData([]); + + Random rnd = new(); + var bytes = new byte[1024]; + rnd.NextBytes(bytes); + + var param = new PutObjectParameters + { + Header = Mocker.ObjectHeader, + Payload = new MemoryStream(bytes), + ClientCut = false + }; + + var result = await GetClient().PutObjectAsync(param); + + var sentMessages = Mocker.ClientStreamWriter!.Messages; + + var body1 = sentMessages.ElementAt(0).GetBody() as Object.PutRequest.Types.Body; + var body2 = sentMessages.ElementAt(1).GetBody() as Object.PutRequest.Types.Body; + + Assert.NotNull(result); + Assert.Equal(Mocker.ResultObjectId, result.ToHash()); + + Assert.True(Mocker.ClientStreamWriter.CompletedTask); + + Assert.Equal(0, body1!.Chunk.Length); + Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Init, body1!.ObjectPartCase); + + Assert.Equal(1024, body2!.Chunk.Length); + Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Chunk, body2!.ObjectPartCase); + } + + [Fact] + public async void ClientCutTest() + { + NetworkMocker.Parameters = new Dictionary() { { "MaxObjectSize", [0x0, 0xa] } }; + + var blockSize = 2560; + byte[] bytes = File.ReadAllBytes(@".\..\..\..\TestData\cat.jpg"); + var fileLength = bytes.Length; + + var param = new PutObjectParameters + { + Header = Mocker.ObjectHeader, + Payload = new MemoryStream(bytes), + ClientCut = true + }; + + var result = await GetClient().PutObjectAsync(param); + + var sentMessages = Mocker.PutSingleRequests.ToArray(); + + Assert.Equal(4, sentMessages.Length); + + var object_0 = sentMessages[0].Body.Object; + var object_1 = sentMessages[1].Body.Object; + var object_2 = sentMessages[2].Body.Object; + var object_3 = sentMessages[3].Body.Object; + + Assert.NotNull(object_0.Header.Split.SplitId); + Assert.Null(object_0.Header.Split.Previous); + Assert.Equal(blockSize, (int)object_0.Header.PayloadLength); + Assert.Equal(bytes[..blockSize], object_0.Payload); + Assert.True(object_0.Header.Attributes.Count == 0); + + Assert.Equal(object_0.Header.Split.SplitId, object_1.Header.Split.SplitId); + Assert.Equal(object_0.ObjectId, object_1.Header.Split.Previous); + Assert.Equal(blockSize, (int)object_1.Header.PayloadLength); + Assert.Equal(bytes[blockSize..(blockSize * 2)], object_1.Payload); + Assert.True(object_1.Header.Attributes.Count == 0); + + // last part + Assert.NotNull(object_2.Header.Split.Parent); + Assert.NotNull(object_2.Header.Split.ParentHeader); + Assert.NotNull(object_2.Header.Split.ParentSignature); + Assert.Equal(object_1.Header.Split.SplitId, object_2.Header.Split.SplitId); + Assert.Equal(object_1.ObjectId, object_2.Header.Split.Previous); + Assert.Equal(fileLength%blockSize, (int)object_2.Header.PayloadLength); + Assert.Equal(bytes[((fileLength/blockSize) * blockSize)..fileLength], object_2.Payload); + Assert.True(object_2.Header.Attributes.Count == 0); + + // link object + Assert.Equal(object_2.Header.Split.Parent, object_3.Header.Split.Parent); + Assert.Equal(object_2.Header.Split.ParentHeader, object_3.Header.Split.ParentHeader); + Assert.Equal(object_2.Header.Split.SplitId, object_3.Header.Split.SplitId); + Assert.Equal(0, (int)object_3.Header.PayloadLength); + Assert.Contains(object_0.ObjectId, object_3.Header.Split.Children); + Assert.Contains(object_2.ObjectId, object_3.Header.Split.Children); + Assert.Contains(object_2.ObjectId, object_3.Header.Split.Children); + Assert.True(object_2.Header.Attributes.Count == 0); + + Assert.Single(object_3.Header.Split.ParentHeader.Attributes); + Assert.Equal("k", object_3.Header.Split.ParentHeader.Attributes[0].Key); + Assert.Equal("v", object_3.Header.Split.ParentHeader.Attributes[0].Value); + + var modelObjId = ObjectId.FromHash(object_3.Header.Split.Parent.Value.ToByteArray()); + + Assert.Equal(result.Value, modelObjId.ToString()); + } + + [Fact] + public async void DeleteObject() + { + Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); + + await GetClient().DeleteObjectAsync(ContainerId, Mocker.ObjectId); + + var request = Mocker.DeleteRequests.FirstOrDefault(); + Assert.NotNull(request); + Assert.Equal(ContainerId.ToGrpcMessage(), request.Body.Address.ContainerId); + Assert.Equal(Mocker.ObjectId.ToGrpcMessage(), request.Body.Address.ObjectId); + } + + [Fact] + public async void GetHeaderTest() + { + Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel(); + + var response = await GetClient().GetObjectHeadAsync(ContainerId, Mocker.ObjectId); + + var request = Mocker.HeadRequests.FirstOrDefault(); + Assert.NotNull(request); + Assert.Equal(ContainerId.ToGrpcMessage(), request.Body.Address.ContainerId); + Assert.Equal(Mocker.ObjectId.ToGrpcMessage(), request.Body.Address.ObjectId); + + Assert.NotNull(response); + Assert.Equal(ContainerId.Value, response.ContainerId.Value); + + Assert.Equal(Mocker.ObjectHeader!.OwnerId!.ToGrpcMessage().ToString(), response.OwnerId!.Value); + Assert.Equal(Mocker.ObjectHeader!.Version!.ToString(), response.Version!.ToString()); + + Assert.Equal(Mocker.HeadResponse!.PayloadLength, response.PayloadLength); + + Assert.Equal(ObjectType.Regular, response.ObjectType); + + Assert.Single(response.Attributes); + + Assert.Equal(Mocker.HeadResponse.Attributes[0].Key, response.Attributes.First().Key); + Assert.Equal(Mocker.HeadResponse.Attributes[0].Value, response.Attributes.First().Value); + + Assert.Null(response.Split); } } diff --git a/src/FrostFS.SDK.Tests/ClientTestLive.cs b/src/FrostFS.SDK.Tests/SmokeTests.cs similarity index 85% rename from src/FrostFS.SDK.Tests/ClientTestLive.cs rename to src/FrostFS.SDK.Tests/SmokeTests.cs index bfd50ca..6a33209 100644 --- a/src/FrostFS.SDK.Tests/ClientTestLive.cs +++ b/src/FrostFS.SDK.Tests/SmokeTests.cs @@ -5,14 +5,13 @@ 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; +namespace FrostFS.SDK.SmokeTests; -public class ClientTestLive +public class SmokeTests { private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; private readonly string url = "http://172.29.238.97:8080"; @@ -46,11 +45,24 @@ public class ClientTestLive 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.Equal(33, result.PublicKey.Length); Assert.Single(result.Addresses); Assert.Equal(9, result.Attributes.Count); } + [Fact] + public async void NodeInfo_Statictics_Test() + { + var ctx = new Context + { + Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds") + }; + + using var fsClient = Client.GetInstance(GetOptions(this.key, this.url)); + + var result = await fsClient.GetNodeInfoAsync(); + } + [Fact] public async void SimpleScenarioTest() { @@ -76,9 +88,7 @@ public class ClientTestLive Assert.NotNull(container); - Random rnd = new(); - var bytes = new byte[6 * 1024 * 1024 + 100]; - rnd.NextBytes(bytes); + var bytes = GetRandomBytes(6 * 1024 * 1024 + 100); var param = new PutObjectParameters { @@ -141,25 +151,21 @@ public class ClientTestLive await Cleanup(fsClient); - var containerId = await fsClient.CreateContainerAsync( - new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)))); + var cnt = new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))); - var context = new Context - { - Timeout = TimeSpan.FromSeconds(10) + var containerId = await fsClient.CreateContainerAsync(cnt); + + var context = new Context + { + Timeout = TimeSpan.FromSeconds(10), + Interceptors = new([new MetricsInterceptor()]) }; - 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); + byte[] bytes = GetRandomBytes(150 * 1024 * 1024); var param = new PutObjectParameters { @@ -200,6 +206,8 @@ public class ClientTestLive ms.Write(chunk); } + Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes)); + await Cleanup(fsClient); await Task.Delay(2000); @@ -210,15 +218,21 @@ public class ClientTestLive } } + private static byte[] GetRandomBytes(int size) + { + Random rnd = new(); + var bytes = new byte[size]; + rnd.NextBytes(bytes); + return bytes; + } + private static IOptions GetOptions(string key, string url) { - var settings = new ClientSettings + return Options.Create(new ClientSettings { Key = key, Host = url - }; - - return Options.Create(settings); + }); } static async Task Cleanup(IFrostFSClient fsClient) @@ -243,7 +257,7 @@ public class ClientTestLive if (DateTime.UtcNow >= ctx.Deadline) throw new TimeoutException(); } - catch (Grpc.Core.RpcException) + catch (RpcException) { throw; } @@ -268,7 +282,7 @@ public class MetricsInterceptor() : Interceptor call.Dispose); } - private async Task HandleUnaryResponse(AsyncUnaryCall call) + private static async Task HandleUnaryResponse(AsyncUnaryCall call) { var watch = new Stopwatch(); watch.Start(); diff --git a/src/FrostFS.SDK.Tests/TestData/cat.jpg b/src/FrostFS.SDK.Tests/TestData/cat.jpg new file mode 100644 index 0000000..03236d5 Binary files /dev/null and b/src/FrostFS.SDK.Tests/TestData/cat.jpg differ