[#16] Unit tests
All checks were successful
DCO / DCO (pull_request) Successful in 42s

Signed-off-by: Pavel Gross <p.gross@yadro.com>
This commit is contained in:
Pavel Gross 2024-07-04 13:29:29 +03:00 committed by p.gross
parent ae67b12313
commit fefa2da218
43 changed files with 884 additions and 477 deletions

View file

@ -13,7 +13,6 @@ using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks; using System.Threading.Tasks;
using Version = FrostFS.SDK.ModelsV2.Version; using Version = FrostFS.SDK.ModelsV2.Version;
@ -168,7 +167,7 @@ public class Client : IFrostFSClient
return service.GetObjectHeadAsync(containerId, objectId, ctx!); return service.GetObjectHeadAsync(containerId, objectId, ctx!);
} }
public Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default) public Task<FrostFsObject> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
{ {
var service = GetObjectService(ref ctx); var service = GetObjectService(ref ctx);
return service.GetObjectAsync(containerId, objectId, ctx!); return service.GetObjectAsync(containerId, objectId, ctx!);
@ -180,7 +179,7 @@ public class Client : IFrostFSClient
return service.PutObjectAsync(putObjectParameters, ctx!); return service.PutObjectAsync(putObjectParameters, ctx!);
} }
public Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default) public Task<ObjectId> PutSingleObjectAsync(FrostFsObject obj, Context? ctx = default)
{ {
var service = GetObjectService(ref ctx); var service = GetObjectService(ref ctx);
return service.PutSingleObjectAsync(obj, ctx!); return service.PutSingleObjectAsync(obj, ctx!);
@ -312,26 +311,21 @@ public class Client : IFrostFSClient
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions) private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
{ {
Uri uri;
try try
{ {
uri = new Uri(host); var uri = new Uri(host);
}
catch (UriFormatException e)
{
var msg = $"Host '{host}' has invalid format. Error: {e.Message}";
throw new ArgumentException(msg);
}
if (channelOptions != null) if (channelOptions != null)
{
return GrpcChannel.ForAddress(uri, channelOptions); return GrpcChannel.ForAddress(uri, channelOptions);
}
return GrpcChannel.ForAddress(uri, new GrpcChannelOptions return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
{ {
HttpHandler = new HttpClientHandler() HttpHandler = new HttpClientHandler()
}); });
} }
catch (UriFormatException e)
{
throw new ArgumentException($"Host '{host}' has invalid format. Error: {e.Message}");
}
}
} }

View file

@ -34,11 +34,11 @@ public interface IFrostFSClient : IDisposable
#region Object #region Object
Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default); Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); Task<FrostFsObject> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
Task<ObjectId> PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default); Task<ObjectId> PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default);
Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default); Task<ObjectId> PutSingleObjectAsync(FrostFsObject obj, Context? context = default);
Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);

View file

@ -4,9 +4,9 @@ namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
public static class ObjectMapper 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()), ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
obj.Header.ToModel(), obj.Header.ToModel(),
obj.Payload.ToByteArray()); obj.Payload.ToByteArray());

View file

@ -44,7 +44,7 @@ public static class ObjectHeaderMapper
SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null 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())); head.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
} }
@ -81,7 +81,7 @@ public static class ObjectHeaderMapper
Previous = header.Split.Previous?.ToModel() 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())); model.Split.Children.AddRange(header.Split.Children.Select(x => x.ToModel()));
} }

View file

@ -1,5 +1,3 @@
using System;
using Google.Protobuf; using Google.Protobuf;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;

View file

@ -1,11 +1,11 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
using FrostFS.SDK.ModelsV2;
using FrostFS.Container; using FrostFS.Container;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using System.Collections.Generic; using System.Collections.Generic;
using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;

View file

@ -51,10 +51,6 @@ internal class NetmapServiceProvider : ContextAccessor
var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); 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); Verifier.CheckResponse(response);
return response.Body.ToModel(); return response.Body.ToModel();

View file

@ -43,7 +43,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return response.Body.Header.Header.ToModel(); return response.Body.Header.Header.ToModel();
} }
internal async Task<ModelsV2.Object> GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx) internal async Task<FrostFsObject> GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
{ {
var sessionToken = await GetOrCreateSession(ctx); var sessionToken = await GetOrCreateSession(ctx);
@ -70,9 +70,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
request.Sign(Context.Key); request.Sign(Context.Key);
var obj = await GetObject(request, ctx); return await GetObject(request, ctx);
return obj;
} }
internal Task<ObjectId> PutObjectAsync(PutObjectParameters parameters, Context ctx) internal Task<ObjectId> PutObjectAsync(PutObjectParameters parameters, Context ctx)
@ -89,7 +87,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return PutStreamObject(parameters, ctx); return PutStreamObject(parameters, ctx);
} }
internal async Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object modelObject, Context ctx) internal async Task<ObjectId> PutSingleObjectAsync(FrostFsObject modelObject, Context ctx)
{ {
var sessionToken = await GetOrCreateSession(ctx); var sessionToken = await GetOrCreateSession(ctx);
@ -178,7 +176,8 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
ObjectId? objectId; ObjectId? objectId;
List<ObjectId> sentObjectIds = []; List<ObjectId> sentObjectIds = [];
ModelsV2.Object? currentObject;
FrostFsObject? currentObject;
var networkSettings = await Context.Client.GetNetworkSettingsAsync(ctx); var networkSettings = await Context.Client.GetNetworkSettingsAsync(ctx);
@ -199,7 +198,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
largeObject.AppendBlock(buffer, bytesCount); 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); .SetSplit(split);
if (largeObject.PayloadLength == fullLength) if (largeObject.PayloadLength == fullLength)
@ -231,6 +230,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
} }
currentObject.AddAttributes(parameters.Header!.Attributes); currentObject.AddAttributes(parameters.Header!.Attributes);
return await PutSingleObjectAsync(currentObject, ctx); return await PutSingleObjectAsync(currentObject, ctx);
} }
@ -247,7 +247,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
var oid = new ObjectID { Value = hdr.Sha256() }; var oid = new ObjectID { Value = hdr.Sha256() };
var request = new PutRequest var initRequest = new PutRequest
{ {
Body = new PutRequest.Types.Body Body = new PutRequest.Types.Body
{ {
@ -258,8 +258,8 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
} }
}; };
request.AddMetaHeader(); initRequest.AddMetaHeader();
request.AddObjectSessionToken( initRequest.AddObjectSessionToken(
sessionToken, sessionToken,
hdr.ContainerId, hdr.ContainerId,
oid, oid,
@ -267,9 +267,10 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
Context.Key Context.Key
); );
request.Sign(Context.Key); initRequest.Sign(Context.Key);
using var stream = await PutObjectInit(initRequest, ctx);
using var stream = await PutObjectInit(request, ctx);
var buffer = new byte[Constants.ObjectChunkSize]; var buffer = new byte[Constants.ObjectChunkSize];
while (true) while (true)
@ -279,14 +280,17 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
if (bufferLength == 0) if (bufferLength == 0)
break; break;
request.Body = new PutRequest.Types.Body var chunkRequest = new PutRequest(initRequest)
{
Body = new PutRequest.Types.Body
{ {
Chunk = ByteString.CopyFrom(buffer[..bufferLength]), Chunk = ByteString.CopyFrom(buffer[..bufferLength]),
},
VerifyHeader = null
}; };
request.VerifyHeader = null; chunkRequest.Sign(Context.Key);
request.Sign(Context.Key); await stream.Write(chunkRequest);
await stream.Write(request);
} }
var response = await stream.Close(); var response = await stream.Close();
@ -295,7 +299,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()); return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
} }
private async Task<ModelsV2.Object> GetObject(GetRequest request, Context ctx) private async Task<FrostFsObject> GetObject(GetRequest request, Context ctx)
{ {
var reader = GetObjectInit(request, ctx); var reader = GetObjectInit(request, ctx);

View file

@ -19,7 +19,7 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx)
return new ObjectID { Value = grpcHeader.Sha256() }.ToModel(); 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(); var grpcHeader = @object.Header.ToGrpcMessage();
@ -100,5 +100,4 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx)
Sum = ByteString.CopyFrom(data.Sha256()) Sum = ByteString.CopyFrom(data.Sha256())
}; };
} }
} }

View file

@ -1,4 +1,3 @@
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using Grpc.Net.Client; using Grpc.Net.Client;
using System; using System;

View file

@ -7,31 +7,31 @@ namespace FrostFS.SDK.ClientV2.Extensions;
public static class ObjectExtensions 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; obj.Header.PayloadLength = length;
return obj; 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)); obj.AddAttribute(new ObjectAttribute(key, value));
return obj; 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); obj.Header.Attributes.Add(attribute);
return obj; return obj;
} }
public static ModelsV2.Object AddAttributes(this ModelsV2.Object obj, IEnumerable<ObjectAttribute> attributes) public static FrostFsObject AddAttributes(this FrostFsObject obj, IEnumerable<ObjectAttribute> attributes)
{ {
obj.Header.Attributes.AddRange(attributes); obj.Header.Attributes.AddRange(attributes);
return obj; 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; obj.Header.Split = split;
return obj; return obj;
@ -43,7 +43,7 @@ public static class ObjectExtensions
return linkObject; return linkObject;
} }
public static ModelsV2.Object CalculateObjectId(this ModelsV2.Object obj) public static FrostFsObject CalculateObjectId(this FrostFsObject obj)
{ {
if (obj.Payload == null) if (obj.Payload == null)
throw new MissingFieldException("Payload cannot be null"); throw new MissingFieldException("Payload cannot be null");

View file

@ -2,6 +2,7 @@ using System.Security.Cryptography;
using FrostFS.Refs; using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ProtosV2.Interfaces;
using FrostFS.Session; using FrostFS.Session;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;

View file

@ -12,6 +12,7 @@ using Org.BouncyCastle.Math;
using FrostFS.Refs; using FrostFS.Refs;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.Session; using FrostFS.Session;
using FrostFS.SDK.ProtosV2.Interfaces;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
@ -67,20 +68,21 @@ public static class RequestSigner
{ {
var hash = new byte[65]; var hash = new byte[65];
hash[0] = 0x04; hash[0] = 0x04;
key
.SignHash(SHA512.Create().ComputeHash(data)) key.SignHash(SHA512.Create().ComputeHash(data)).CopyTo(hash, 1);
.CopyTo(hash, 1);
return hash; return hash;
} }
public static Signature SignMessagePart(this ECDsa key, IMessage? data) public static Signature SignMessagePart(this ECDsa key, IMessage? data)
{ {
var data2Sign = data is null ? Array.Empty<byte>() : data.ToByteArray(); var data2Sign = data is null ? [] : data.ToByteArray();
var sig = new Signature var sig = new Signature
{ {
Key = ByteString.CopyFrom(key.PublicKey()), Key = ByteString.CopyFrom(key.PublicKey()),
Sign = ByteString.CopyFrom(key.SignData(data2Sign)), Sign = ByteString.CopyFrom(key.SignData(data2Sign)),
}; };
return sig; return sig;
} }

View file

@ -13,6 +13,7 @@ using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.Session; using FrostFS.Session;
using FrostFS.SDK.ProtosV2.Interfaces;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;

View file

@ -2,6 +2,6 @@ namespace FrostFS.SDK.ModelsV2;
public class CallStatistics public class CallStatistics
{ {
public string MethodName { get; set; } public string? MethodName { get; set; }
public long ElapsedMicroSeconds { get; set; } public long ElapsedMicroSeconds { get; set; }
} }

View file

@ -1,5 +1,4 @@
using System; using System;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.SDK.ModelsV2.Netmap;

View file

@ -8,7 +8,7 @@ namespace FrostFS.SDK.ClientV2;
public class Context() public class Context()
{ {
private List<Interceptor> interceptors; private List<Interceptor>? interceptors;
public CancellationToken CancellationToken { get; set; } = default; public CancellationToken CancellationToken { get; set; } = default;
public TimeSpan Timeout { 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 DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null;
public Action<CallStatistics> Callback { get; set; } public Action<CallStatistics>? Callback { get; set; }
public List<Interceptor> Interceptors public List<Interceptor> Interceptors
{ {
get { return interceptors ??= []; } get { return this.interceptors ??= []; }
set { interceptors = value; } set { this.interceptors = value; }
} }
} }

View file

@ -4,16 +4,16 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2; 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; ObjectId = objectId;
Payload = payload; Payload = payload;
Header = header; Header = header;
} }
public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular) public FrostFsObject(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
{ {
Payload = payload; Payload = payload;
Header = new ObjectHeader(containerId: container, type: objectType, attributes: []); 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(); 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, []) public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, [])
{ {

View file

@ -1,35 +1,27 @@
using System.Collections.Generic; using System.Collections.Generic;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class ObjectHeader public class ObjectHeader(
{
public OwnerId? OwnerId { get; set; }
public List<ObjectAttribute> Attributes { get; set; }
public ContainerId ContainerId { get; set; }
public ulong PayloadLength { get; set; }
public byte[]? PayloadCheckSum { get; set; }
public ObjectType ObjectType { get; set; }
public Version? Version { get; set; }
public Split? Split { get; set; }
public ObjectHeader(
ContainerId containerId, ContainerId containerId,
ObjectType type = ObjectType.Regular, ObjectType type = ObjectType.Regular,
params ObjectAttribute[] attributes params ObjectAttribute[] attributes
) )
{ {
Attributes = [.. attributes]; public OwnerId? OwnerId { get; set; }
ContainerId = containerId;
ObjectType = type; public List<ObjectAttribute> Attributes { get; set; } = [.. attributes];
}
public ContainerId ContainerId { get; set; } = containerId;
public ulong PayloadLength { get; set; }
public byte[]? PayloadCheckSum { get; set; }
public ObjectType ObjectType { get; set; } = type;
public Version? Version { get; set; }
public Split? Split { get; set; }
} }

View file

@ -4,21 +4,15 @@ using FrostFS.SDK.Cryptography;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class ObjectId public class ObjectId(string id)
{ {
public string Value { get; } public string Value { get; } = id;
public ObjectId(string id)
{
Value = id;
}
public static ObjectId FromHash(byte[] hash) public static ObjectId FromHash(byte[] hash)
{ {
if (hash.Length != Constants.Sha256HashLength) if (hash.Length != Constants.Sha256HashLength)
{
throw new FormatException("ObjectID must be a sha256 hash."); throw new FormatException("ObjectID must be a sha256 hash.");
}
return new ObjectId(Base58.Encode(hash)); return new ObjectId(Base58.Encode(hash));
} }

View file

@ -1,4 +1,6 @@
namespace FrostFS.Session; using FrostFS.Session;
namespace FrostFS.SDK.ProtosV2.Interfaces;
public interface IRequest : IVerifiableMessage public interface IRequest : IVerifiableMessage
{ {

View file

@ -1,6 +1,7 @@
using Google.Protobuf; using Google.Protobuf;
using FrostFS.Session; using FrostFS.Session;
using FrostFS.SDK.ProtosV2.Interfaces;
namespace FrostFS.Container; namespace FrostFS.Container;

View file

@ -1,4 +1,5 @@
using FrostFS.Session; using FrostFS.SDK.ProtosV2.Interfaces;
using FrostFS.Session;
using Google.Protobuf; using Google.Protobuf;
namespace FrostFS.Netmap; namespace FrostFS.Netmap;

View file

@ -1,4 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using FrostFS.SDK.ProtosV2.Interfaces;
using FrostFS.Session; using FrostFS.Session;
using Google.Protobuf; using Google.Protobuf;

View file

@ -1,4 +1,5 @@
using Google.Protobuf; using FrostFS.SDK.ProtosV2.Interfaces;
using Google.Protobuf;
namespace FrostFS.Session; namespace FrostFS.Session;

View file

@ -1,118 +1,112 @@
using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
using Microsoft.Extensions.Options; 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; namespace FrostFS.SDK.Tests;
public class ContainerTest
{
private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
[Fact] public abstract class ContainerTestsBase
public async void CreateContainerTest()
{ {
var factory = new PutContainerMockFactory(this.key) protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
protected IOptions<ClientSettings> Settings { get; set; }
protected ContainerMocker Mocker { get; set; }
protected ContainerTestsBase()
{
Settings = Options.Create(new ClientSettings
{
Key = key,
Host = "http://localhost:8080"
});
Mocker = new ContainerMocker(this.key)
{ {
PlacementPolicy = new PlacementPolicy(true, new Replica(1)), PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
Version = new ModelsV2.Version(2, 13), Version = new ModelsV2.Version(2, 13),
ContainerGuid = Guid.NewGuid() ContainerGuid = Guid.NewGuid()
}; };
}
var settings = Options.Create(new ClientSettings protected IFrostFSClient GetClient()
{ {
Key = key, return ClientV2.Client.GetTestInstance(
Host = "http://localhost:8080" Settings,
});
var fsClient = Client.GetTestInstance(
settings,
null, null,
new NetmapMockFactory(this.key).GetMock().Object, new NetworkMocker(this.key).GetMock().Object,
new SessionMockFactory(this.key).GetMock().Object, new SessionMocker(this.key).GetMock().Object,
factory.GetMock().Object, Mocker.GetMock().Object,
new ObjectMockFactory(this.key).GetMock().Object); new ObjectMocker(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);
Assert.NotNull(result.Value); Assert.NotNull(result.Value);
Assert.True(Base58.Encode(factory.ContainerGuid.ToBytes()) == result.Value); Assert.True(Base58.Encode(Mocker.ContainerGuid.ToBytes()) == result.Value);
} }
[Fact] [Fact]
public async void GetContainerTest() public async void GetContainerTest()
{ {
var factory = new GetContainerMockFactory(this.key) var cid = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes()));
{
PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
Version = new ModelsV2.Version(2, 13),
Acl = BasicAcl.PublicRO,
ContainerGuid = Guid.NewGuid(),
};
var settings = Options.Create(new ClientSettings Mocker.Acl = BasicAcl.PublicRO;
{
Key = key,
Host = "http://localhost:8080"
});
var fsClient = Client.GetTestInstance( var result = await GetClient().GetContainerAsync(cid);
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);
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal(factory.Acl, result.BasicAcl); Assert.Equal(Mocker.Acl, result.BasicAcl);
Assert.Equal(factory.ContainerGuid, result.Nonce); Assert.Equal(Mocker.ContainerGuid, result.Nonce);
Assert.Equal(0, factory.PlacementPolicy.CompareTo(result.PlacementPolicy)); Assert.Equal(0, Mocker.PlacementPolicy.CompareTo(result.PlacementPolicy));
Assert.Equal(factory.Version.ToString(), result.Version!.ToString()); 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] [Fact]
public async void DeleteContainerAsyncTest() public async void DeleteContainerAsyncTest()
{ {
var factory = new DeleteContainerMockFactory(this.key) var cid = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes()));
{
Version = new ModelsV2.Version(2, 13),
Acl = BasicAcl.PublicRW,
ContainerGuid = Guid.NewGuid(),
};
var settings = Options.Create(new ClientSettings await GetClient().DeleteContainerAsync(cid);
{
Key = key,
Host = "http://localhost:8080"
});
var fsClient = Client.GetTestInstance( Assert.Single(Mocker.Requests);
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 request = Mocker.Requests.First();
await fsClient.DeleteContainerAsync(cid);
Assert.Single(factory.Requests);
var request = factory.Requests.First();
Assert.Equal(cid.ToGrpcMessage(), request.Request.Body.ContainerId); Assert.Equal(cid.ToGrpcMessage(), request.Request.Body.ContainerId);
} }

View file

@ -9,6 +9,10 @@
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="TestData\cat.jpeg" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" /> <PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
@ -25,4 +29,10 @@
<ProjectReference Include="..\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj" /> <ProjectReference Include="..\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="TestData\cat.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

View file

@ -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<GetResponse>
{
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<byte>())) },
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<bool> MoveNext(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
}

View file

@ -0,0 +1,29 @@
using Grpc.Core;
using FrostFS.SDK.ProtosV2.Interfaces;
namespace FrostFS.SDK.Tests;
public class ClientStreamWriter : IClientStreamWriter<IRequest>
{
public List<IRequest> 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;
}
}

View file

@ -17,6 +17,7 @@ namespace FrostFS.SDK.Tests;
public abstract class ServiceBase(string key) public abstract class ServiceBase(string key)
{ {
public string StringKey { get; private set; } = key;
public ECDsa Key { get; private set; } = key.LoadWif(); public ECDsa Key { get; private set; } = key.LoadWif();
public ModelsV2.Version Version { get; set; } = DefaultVersion; public ModelsV2.Version Version { get; set; } = DefaultVersion;
public BasicAcl Acl { get; set; } = DefaultAcl; public BasicAcl Acl { get; set; } = DefaultAcl;

View file

@ -1,7 +1,6 @@
using FrostFS.Container; using FrostFS.Container;
using Moq; using Moq;
namespace FrostFS.SDK.Tests; namespace FrostFS.SDK.Tests;
public class ContainerStub(string key) : ContainerServiceBase(key) public class ContainerStub(string key) : ContainerServiceBase(key)

View file

@ -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<ContainerService.ContainerServiceClient> GetMock()
{
var mock = new Mock<ContainerService.ContainerServiceClient>();
var v = mock.Setup(x => x.DeleteAsync(
It.IsAny<DeleteRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Requests.Add(new RequestData<DeleteRequest>(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<DeleteResponse>(
Task.FromResult(response),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => { });
});
return mock;
}
public List<RequestData<DeleteRequest>> Requests = [];
}
public class RequestData<T>(T request, Metadata m, DateTime? dt, CancellationToken ct)
{
public T Request => request;
public Metadata Metadata => m;
public DateTime? deadline => dt;
public CancellationToken CancellationToken => ct;
}

View file

@ -8,10 +8,13 @@ using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.Session;
using FrostFS.Refs;
using System.Collections.Generic;
namespace FrostFS.SDK.Tests; namespace FrostFS.SDK.Tests;
public class GetContainerMockFactory(string key) : ContainerServiceBase(key) public class ContainerMocker(string key) : ContainerServiceBase(key)
{ {
public override Mock<ContainerService.ContainerServiceClient> GetMock() public override Mock<ContainerService.ContainerServiceClient> GetMock()
{ {
@ -19,7 +22,46 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
var grpcVersion = Version.ToGrpcMessage(); 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<PutResponse>(
Task.FromResult(putResponse),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => { });
mock.Setup(x => x.PutAsync(
It.IsAny<PutRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((PutRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Verifier.CheckRequest(r);
return putContainerResponse;
});
var getResponse = new GetResponse
{ {
Body = new GetResponse.Types.Body Body = new GetResponse.Types.Body
{ {
@ -34,7 +76,7 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
MetaHeader = ResponseMetaHeader MetaHeader = ResponseMetaHeader
}; };
response.VerifyHeader = GetResponseVerificationHeader(response); getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse);
mock.Setup(x => x.GetAsync( mock.Setup(x => x.GetAsync(
It.IsAny<GetRequest>(), It.IsAny<GetRequest>(),
@ -46,13 +88,74 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
Verifier.CheckRequest(r); Verifier.CheckRequest(r);
return new AsyncUnaryCall<GetResponse>( return new AsyncUnaryCall<GetResponse>(
Task.FromResult(response), Task.FromResult(getResponse),
Task.FromResult(ResponseMetaData), Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty), () => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData, () => 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<ListRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((ListRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Verifier.CheckRequest(r);
return new AsyncUnaryCall<ListResponse>(
Task.FromResult(listResponse),
Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData,
() => { });
});
var v = mock.Setup(x => x.DeleteAsync(
It.IsAny<DeleteRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Requests.Add(new RequestData<DeleteRequest>(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<DeleteResponse>(
Task.FromResult(response),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => { });
});
return mock; return mock;
} }
public List<byte[]> ContainerIds { get; set; } = [];
public List<RequestData<DeleteRequest>> Requests { get; set; } = [];
} }

View file

@ -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<ContainerService.ContainerServiceClient> GetMock()
{
var mock = new Mock<ContainerService.ContainerServiceClient>();
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<PutResponse>(
Task.FromResult(response),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => { });
mock.Setup(x => x.PutAsync(
It.IsAny<PutRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.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;
}
}

View file

@ -0,0 +1,12 @@
using Grpc.Core;
namespace FrostFS.SDK.Tests;
public class RequestData<T>(T request, Metadata m, DateTime? dt, CancellationToken ct)
{
public T Request => request;
public Metadata Metadata => m;
public DateTime? Deadline => dt;
public CancellationToken CancellationToken => ct;
}

View file

@ -1,14 +0,0 @@
using Moq;
using FrostFS.Netmap;
namespace FrostFS.SDK.Tests;
public class NetmapMockFactory(string key) : ServiceBase(key)
{
public Mock<NetmapService.NetmapServiceClient> GetMock()
{
var mock = new Mock<NetmapService.NetmapServiceClient>();
return mock;
}
}

View file

@ -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<string, byte[]>? Parameters { get; set; }
public Mock<NetmapService.NetmapServiceClient> GetMock()
{
var mock = new Mock<NetmapService.NetmapServiceClient>();
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<NetworkInfoRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((NetworkInfoRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Verifier.CheckRequest(r);
return new AsyncUnaryCall<NetworkInfoResponse>(
Task.FromResult(response),
Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData,
() => { });
});
return mock;
}
}

View file

@ -1,32 +1,23 @@
using Moq;
using FrostFS.Object;
using Grpc.Core;
using FrostFS.SDK.ClientV2;
using FrostFS.Session;
using Google.Protobuf; 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 System.Security.Cryptography;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.Tests; namespace FrostFS.SDK.Tests;
public class ObjectMockFactory(string key) : ObjectServiceBase(key) public class ObjectMocker(string key) : ObjectServiceBase(key)
{ {
public override Mock<ObjectService.ObjectServiceClient> GetMock() public override Mock<ObjectService.ObjectServiceClient> GetMock()
{ {
var mock = new Mock<ObjectService.ObjectServiceClient>(); var mock = new Mock<ObjectService.ObjectServiceClient>();
GetResponse response = new() if (ObjectHeader != null)
{ {
Body = new GetResponse.Types.Body
{
},
MetaHeader = ResponseMetaHeader
};
response.VerifyHeader = GetResponseVerificationHeader(response);
mock.Setup(x => x.Get( mock.Setup(x => x.Get(
It.IsAny<GetRequest>(), It.IsAny<GetRequest>(),
It.IsAny<Metadata>(), It.IsAny<Metadata>(),
@ -37,65 +28,181 @@ public class ObjectMockFactory(string key) : ObjectServiceBase(key)
Verifier.CheckRequest(r); Verifier.CheckRequest(r);
return new AsyncServerStreamingCall<GetResponse>( return new AsyncServerStreamingCall<GetResponse>(
new AsyncStreamReaderMock(key, ObjectHeader), new AsyncStreamReaderMock(StringKey, ObjectHeader),
Task.FromResult(ResponseMetaData), Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty), () => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData, () => 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<HeadRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((HeadRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Verifier.CheckRequest(r);
HeadRequests.Add(r);
return new AsyncUnaryCall<HeadResponse>(
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<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((Metadata m, DateTime? dt, CancellationToken ct) =>
{
return new AsyncClientStreamingCall<PutRequest, PutResponse>(
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<PutSingleRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((PutSingleRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Verifier.CheckRequest(r);
PutSingleRequests.Add(r);
return new AsyncUnaryCall<PutSingleResponse>(
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<DeleteRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{
Verifier.CheckRequest(r);
DeleteRequests.Add(r);
return new AsyncUnaryCall<DeleteResponse>(
Task.FromResult(deleteResponse),
Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData,
() => { });
});
}
return mock; return mock;
} }
public ObjectHeader ObjectHeader { get; set; } public ObjectId? ObjectId { get; set; }
}
public ObjectHeader? ObjectHeader { get; set; }
public class AsyncStreamReaderMock(string key, ObjectHeader objectHeader) : ServiceBase(key), IAsyncStreamReader<GetResponse>
{ public Header? HeadResponse { get; set; }
public GetResponse Current
{ public byte[]? ResultObjectId { get; set; }
get
{ public ClientStreamWriter? ClientStreamWriter { get; private set; } = new ();
var ecdsaKey = key.LoadWif();
public List<PutSingleRequest> PutSingleRequests { get; private set; } = [];
var header = new Header
{ public List<DeleteRequest> DeleteRequests { get; private set; } = [];
ContainerId = objectHeader.ContainerId.ToGrpcMessage(),
PayloadLength = objectHeader.PayloadLength, public List<HeadRequest> HeadRequests { get; private set; } = [];
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<byte>())) },
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<bool> MoveNext(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
} }

View file

@ -6,7 +6,7 @@ using Google.Protobuf;
namespace FrostFS.SDK.Tests; namespace FrostFS.SDK.Tests;
public class SessionMockFactory(string key) : ServiceBase(key) public class SessionMocker(string key) : ServiceBase(key)
{ {
public byte[]? SessionId { get; set; } public byte[]? SessionId { get; set; }

View file

@ -1,66 +1,237 @@
using FrostFS.Refs;
using FrostFS.SDK.ClientV2; using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.SDK.ModelsV2.Netmap;
using Google.Protobuf;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace FrostFS.SDK.Tests; namespace FrostFS.SDK.Tests;
public class ObjectTest public abstract class ObjectTestsBase
{ {
private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
[Fact] protected IOptions<ClientSettings> Settings { get; set; }
public async void GetObjectTest() 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(); var ecdsaKey = key.LoadWif();
ContainerId cntId = new("xyz");
ObjectHeader header = new(cntId, ModelsV2.Enums.ObjectType.Regular, [new ObjectAttribute("k", "v")]) Settings = Options.Create(new ClientSettings
{
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
{ {
Key = key, Key = key,
Host = "http://localhost:8080" Host = "http://localhost:8080"
}); });
var fsClient = Client.GetTestInstance( Mocker = new ObjectMocker(key)
settings, {
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, null,
new NetmapMockFactory(this.key).GetMock().Object, NetworkMocker.GetMock().Object,
new SessionMockFactory(this.key).GetMock().Object, SessionMocker.GetMock().Object,
new ContainerStub(this.key).GetMock().Object, ContainerMocker.GetMock().Object,
objectMockFactory.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.NotNull(result);
Assert.Equal(header.ContainerId.Value, result.Header.ContainerId.Value); Assert.Equal(Mocker.ObjectHeader!.ContainerId.Value, result.Header.ContainerId.Value);
Assert.Equal(header.OwnerId.ToGrpcMessage().ToString(), result.Header.OwnerId!.Value); Assert.Equal(Mocker.ObjectHeader!.OwnerId!.ToGrpcMessage().ToString(), result.Header.OwnerId!.Value);
Assert.Equal(header.PayloadLength, result.Header.PayloadLength); Assert.Equal(Mocker.ObjectHeader.PayloadLength, result.Header.PayloadLength);
Assert.Single(result.Header.Attributes); Assert.Single(result.Header.Attributes);
Assert.Equal(header.Attributes[0].Key, result.Header.Attributes[0].Key); Assert.Equal(Mocker.ObjectHeader.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].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<string, byte[]>() { { "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);
} }
} }

View file

@ -5,14 +5,13 @@ using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.SDK.ModelsV2.Netmap;
using Grpc.Core; using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Grpc.Core.Interceptors; using Grpc.Core.Interceptors;
using System.Diagnostics; 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 key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
private readonly string url = "http://172.29.238.97:8080"; 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(2, result.Version.Major);
Assert.Equal(13, result.Version.Minor); Assert.Equal(13, result.Version.Minor);
Assert.Equal(NodeState.Online, result.State); Assert.Equal(NodeState.Online, result.State);
Assert.True(result.PublicKey.Length > 0); Assert.Equal(33, result.PublicKey.Length);
Assert.Single(result.Addresses); Assert.Single(result.Addresses);
Assert.Equal(9, result.Attributes.Count); 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] [Fact]
public async void SimpleScenarioTest() public async void SimpleScenarioTest()
{ {
@ -76,9 +88,7 @@ public class ClientTestLive
Assert.NotNull(container); Assert.NotNull(container);
Random rnd = new(); var bytes = GetRandomBytes(6 * 1024 * 1024 + 100);
var bytes = new byte[6 * 1024 * 1024 + 100];
rnd.NextBytes(bytes);
var param = new PutObjectParameters var param = new PutObjectParameters
{ {
@ -141,25 +151,21 @@ public class ClientTestLive
await Cleanup(fsClient); await Cleanup(fsClient);
var containerId = await fsClient.CreateContainerAsync( var cnt = new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)));
new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))));
var containerId = await fsClient.CreateContainerAsync(cnt);
var context = new Context var context = new Context
{ {
Timeout = TimeSpan.FromSeconds(10) Timeout = TimeSpan.FromSeconds(10),
Interceptors = new([new MetricsInterceptor()])
}; };
var metrics = new MetricsInterceptor();
context.Interceptors.Add(metrics);
var container = await GetContainer(fsClient, containerId, context); var container = await GetContainer(fsClient, containerId, context);
Assert.NotNull(container); Assert.NotNull(container);
Random rnd = new(); byte[] bytes = GetRandomBytes(150 * 1024 * 1024);
var bytes = new byte[6 * 1024 * 1024 + 100];
rnd.NextBytes(bytes);
var param = new PutObjectParameters var param = new PutObjectParameters
{ {
@ -200,6 +206,8 @@ public class ClientTestLive
ms.Write(chunk); ms.Write(chunk);
} }
Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
await Cleanup(fsClient); await Cleanup(fsClient);
await Task.Delay(2000); 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<ClientSettings> GetOptions(string key, string url) private static IOptions<ClientSettings> GetOptions(string key, string url)
{ {
var settings = new ClientSettings return Options.Create(new ClientSettings
{ {
Key = key, Key = key,
Host = url Host = url
}; });
return Options.Create(settings);
} }
static async Task Cleanup(IFrostFSClient fsClient) static async Task Cleanup(IFrostFSClient fsClient)
@ -243,7 +257,7 @@ public class ClientTestLive
if (DateTime.UtcNow >= ctx.Deadline) if (DateTime.UtcNow >= ctx.Deadline)
throw new TimeoutException(); throw new TimeoutException();
} }
catch (Grpc.Core.RpcException) catch (RpcException)
{ {
throw; throw;
} }
@ -268,7 +282,7 @@ public class MetricsInterceptor() : Interceptor
call.Dispose); call.Dispose);
} }
private async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call) private static async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
{ {
var watch = new Stopwatch(); var watch = new Stopwatch();
watch.Start(); watch.Start();

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB