[#15] Unit tests #16
43 changed files with 884 additions and 477 deletions
|
@ -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()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
using System;
|
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace FrostFS.SDK.ClientV2;
|
namespace FrostFS.SDK.ClientV2;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, [])
|
||||||
{
|
{
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace FrostFS.Session;
|
using FrostFS.Session;
|
||||||
|
|
||||||
|
namespace FrostFS.SDK.ProtosV2.Interfaces;
|
||||||
|
|
||||||
public interface IRequest : IVerifiableMessage
|
public interface IRequest : IVerifiableMessage
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Google.Protobuf;
|
using FrostFS.SDK.ProtosV2.Interfaces;
|
||||||
|
using Google.Protobuf;
|
||||||
|
|
||||||
namespace FrostFS.Session;
|
namespace FrostFS.Session;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
60
src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs
Normal file
60
src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs
Normal file
29
src/FrostFS.SDK.Tests/Mocks/ClientStreamWriter.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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; } = [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
78
src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs
Normal file
78
src/FrostFS.SDK.Tests/Mocks/NetworkMocker.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
BIN
src/FrostFS.SDK.Tests/TestData/cat.jpg
Normal file
BIN
src/FrostFS.SDK.Tests/TestData/cat.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
Loading…
Reference in a new issue
It looks like something that does not relate to the commit called "Unit tests"
It is better to separate commits logically.