[#15] Unit tests #16

Merged
PavelGrossSpb merged 1 commit from PavelGrossSpb/frostfs-sdk-csharp:UnitTests into master 2024-07-10 14:04:58 +00:00
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);
if (channelOptions != null)
return GrpcChannel.ForAddress(uri, channelOptions);
return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
{
HttpHandler = new HttpClientHandler()
});
} }
catch (UriFormatException e) catch (UriFormatException e)
{ {
var msg = $"Host '{host}' has invalid format. Error: {e.Message}"; throw new ArgumentException($"Host '{host}' has invalid format. Error: {e.Message}");
throw new ArgumentException(msg);
} }
if (channelOptions != null)
{
return GrpcChannel.ForAddress(uri, channelOptions);
}
return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
{
HttpHandler = new HttpClientHandler()
});
} }
} }

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)
Review

It looks like something that does not relate to the commit called "Unit tests"
It is better to separate commits logically.

It looks like something that does not relate to the commit called "Unit tests" It is better to separate commits logically.
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)
{ {
Chunk = ByteString.CopyFrom(buffer[..bufferLength]), Body = new PutRequest.Types.Body
{
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(
ContainerId containerId,
ObjectType type = ObjectType.Regular,
params ObjectAttribute[] attributes
)
{ {
public OwnerId? OwnerId { get; set; } public OwnerId? OwnerId { get; set; }
public List<ObjectAttribute> Attributes { get; set; } public List<ObjectAttribute> Attributes { get; set; } = [.. attributes];
public ContainerId ContainerId { get; set; } public ContainerId ContainerId { get; set; } = containerId;
public ulong PayloadLength { get; set; } public ulong PayloadLength { get; set; }
public byte[]? PayloadCheckSum { get; set; } public byte[]? PayloadCheckSum { get; set; }
public ObjectType ObjectType { get; set; } public ObjectType ObjectType { get; set; } = type;
public Version? Version { get; set; } public Version? Version { get; set; }
public Split? Split { get; set; } public Split? Split { get; set; }
public ObjectHeader(
ContainerId containerId,
ObjectType type = ObjectType.Regular,
params ObjectAttribute[] attributes
)
{
Attributes = [.. attributes];
ContainerId = containerId;
ObjectType = type;
}
} }

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() {
protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
protected IOptions<ClientSettings> 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)), 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,
}); 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( public class ContainerTest : ContainerTestsBase
settings, {
null, [Fact]
new NetmapMockFactory(this.key).GetMock().Object, public async void CreateContainerTest()
new SessionMockFactory(this.key).GetMock().Object, {
factory.GetMock().Object, var result = await GetClient().CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, Mocker.PlacementPolicy));
new ObjectMockFactory(this.key).GetMock().Object);
var result = await fsClient.CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, factory.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,101 +1,208 @@
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 mock.Setup(x => x.Get(
{ It.IsAny<GetRequest>(),
}, It.IsAny<Metadata>(),
MetaHeader = ResponseMetaHeader It.IsAny<DateTime?>(),
}; It.IsAny<CancellationToken>()))
.Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
response.VerifyHeader = GetResponseVerificationHeader(response);
mock.Setup(x => x.Get(
It.IsAny<GetRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
{ {
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