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

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

View file

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

View file

@ -34,11 +34,11 @@ public interface IFrostFSClient : IDisposable
#region Object
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> PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default);
Task<ObjectId> PutSingleObjectAsync(FrostFsObject obj, 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 ModelsV2.Object ToModel(this Object.Object obj)
public static FrostFsObject ToModel(this Object.Object obj)
{
return new ModelsV2.Object(
return new FrostFsObject(
ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
obj.Header.ToModel(),
obj.Payload.ToByteArray());

View file

@ -44,7 +44,7 @@ public static class ObjectHeaderMapper
SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null
};
if (split.Children != null && split.Children.Any())
if (split.Children != null && split.Children.Count != 0)
head.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
}
@ -81,7 +81,7 @@ public static class ObjectHeaderMapper
Previous = header.Split.Previous?.ToModel()
};
if (header.Split.Children.Any())
if (header.Split.Children.Count != 0)
model.Split.Children.AddRange(header.Split.Children.Select(x => x.ToModel()));
}

View file

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

View file

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

View file

@ -51,10 +51,6 @@ internal class NetmapServiceProvider : ContextAccessor
var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
//var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
// netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken),
// nameof(netmapServiceClient.LocalNodeInfoAsync));
Verifier.CheckResponse(response);
return response.Body.ToModel();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,16 +4,16 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2;
public class Object
public class FrostFsObject
{
public Object(ObjectId objectId, ObjectHeader header, byte[] payload)
public FrostFsObject(ObjectId objectId, ObjectHeader header, byte[] payload)
{
ObjectId = objectId;
Payload = payload;
Header = header;
}
public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
public FrostFsObject(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
{
Payload = payload;
Header = new ObjectHeader(containerId: container, type: objectType, attributes: []);
@ -41,7 +41,7 @@ public class Object
}
}
public class LargeObject(ContainerId container) : Object(container, [])
public class LargeObject(ContainerId container) : FrostFsObject(container, [])
{
private readonly SHA256 payloadHash = SHA256.Create();
@ -64,7 +64,7 @@ public class LargeObject(ContainerId container) : Object(container, [])
}
}
public class LinkObject : Object
public class LinkObject : FrostFsObject
{
public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, [])
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,6 +9,10 @@
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<None Remove="TestData\cat.jpeg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
@ -25,4 +29,10 @@
<ProjectReference Include="..\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="TestData\cat.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</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 string StringKey { get; private set; } = key;
public ECDsa Key { get; private set; } = key.LoadWif();
public ModelsV2.Version Version { get; set; } = DefaultVersion;
public BasicAcl Acl { get; set; } = DefaultAcl;

View file

@ -1,7 +1,6 @@
using FrostFS.Container;
using Moq;
namespace FrostFS.SDK.Tests;
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.ClientV2.Mappers.GRPC.Netmap;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.Session;
using FrostFS.Refs;
using System.Collections.Generic;
namespace FrostFS.SDK.Tests;
public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
public class ContainerMocker(string key) : ContainerServiceBase(key)
{
public override Mock<ContainerService.ContainerServiceClient> GetMock()
{
@ -19,7 +22,46 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
var grpcVersion = Version.ToGrpcMessage();
var response = new GetResponse
PutResponse putResponse = new()
{
Body = new PutResponse.Types.Body
{
ContainerId = new ContainerID
{
Value = ByteString.CopyFrom(ContainerGuid.ToBytes())
}
},
MetaHeader = new ResponseMetaHeader
{
Version = Version is null ? DefaultVersion.ToGrpcMessage() : Version.ToGrpcMessage(),
Epoch = 100,
Ttl = 1
}
};
putResponse.VerifyHeader = GetResponseVerificationHeader(putResponse);
var metadata = new Metadata();
var putContainerResponse = new AsyncUnaryCall<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
{
@ -34,7 +76,7 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
MetaHeader = ResponseMetaHeader
};
response.VerifyHeader = GetResponseVerificationHeader(response);
getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse);
mock.Setup(x => x.GetAsync(
It.IsAny<GetRequest>(),
@ -46,13 +88,74 @@ public class GetContainerMockFactory(string key) : ContainerServiceBase(key)
Verifier.CheckRequest(r);
return new AsyncUnaryCall<GetResponse>(
Task.FromResult(response),
Task.FromResult(getResponse),
Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData,
() => { });
});
var listResponse = new ListResponse
{
Body = new ListResponse.Types.Body(),
MetaHeader = ResponseMetaHeader
};
foreach (var item in ContainerIds)
{
listResponse.Body.ContainerIds.Add(new ContainerID { Value = ByteString.CopyFrom(item) });
}
listResponse.VerifyHeader = GetResponseVerificationHeader(listResponse);
mock.Setup(x => x.ListAsync(
It.IsAny<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;
}
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 Grpc.Core;
using Moq;
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ModelsV2;
using FrostFS.Object;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using System.Security.Cryptography;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.Tests;
public class ObjectMockFactory(string key) : ObjectServiceBase(key)
public class ObjectMocker(string key) : ObjectServiceBase(key)
{
public override Mock<ObjectService.ObjectServiceClient> GetMock()
{
var mock = new Mock<ObjectService.ObjectServiceClient>();
GetResponse response = new()
if (ObjectHeader != null)
{
Body = new GetResponse.Types.Body
{
},
MetaHeader = ResponseMetaHeader
};
response.VerifyHeader = GetResponseVerificationHeader(response);
mock.Setup(x => x.Get(
It.IsAny<GetRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
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);
return new AsyncServerStreamingCall<GetResponse>(
new AsyncStreamReaderMock(key, ObjectHeader),
return new AsyncServerStreamingCall<GetResponse>(
new AsyncStreamReaderMock(StringKey, ObjectHeader),
Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData,
() => { });
});
HeadResponse ??= new Header
{
CreationEpoch = 99,
ContainerId = ObjectHeader.ContainerId.ToGrpcMessage(),
ObjectType = ObjectType.Regular,
OwnerId = ObjectHeader.OwnerId!.ToGrpcMessage(),
PayloadLength = 1,
PayloadHash = new Refs.Checksum { Type = Refs.ChecksumType.Sha256, Sum = ByteString.CopyFrom(SHA256.HashData([0xff])) },
Version = ObjectHeader.Version!.ToGrpcMessage()
};
HeadResponse headResponse = new()
{
Body = new HeadResponse.Types.Body
{
Header = new HeaderWithSignature
{
Header = HeadResponse
}
},
MetaHeader = ResponseMetaHeader
};
headResponse.Body.Header.Header.Attributes.Add(new Header.Types.Attribute { Key = "k", Value = "v" });
headResponse.Body.Header.Signature = new Refs.Signature
{
Key = ByteString.CopyFrom(Key.PublicKey()),
Sign = ByteString.CopyFrom(Key.SignData(headResponse.Body.Header.ToByteArray())),
};
headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse);
mock.Setup(x => x.HeadAsync(
It.IsAny<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;
}
public ObjectHeader ObjectHeader { get; set; }
}
public class AsyncStreamReaderMock(string key, ObjectHeader objectHeader) : ServiceBase(key), IAsyncStreamReader<GetResponse>
{
public GetResponse Current
{
get
{
var ecdsaKey = key.LoadWif();
var header = new Header
{
ContainerId = objectHeader.ContainerId.ToGrpcMessage(),
PayloadLength = objectHeader.PayloadLength,
Version = objectHeader.Version!.ToGrpcMessage(),
OwnerId = objectHeader.OwnerId!.ToGrpcMessage()
};
foreach (var attr in objectHeader.Attributes)
header.Attributes.Add(attr.ToGrpcMessage());
var response = new GetResponse
{
Body = new GetResponse.Types.Body
{
Init = new GetResponse.Types.Body.Types.Init
{
Header = header,
ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Array.Empty<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);
}
public ObjectId? ObjectId { get; set; }
public ObjectHeader? ObjectHeader { get; set; }
public Header? HeadResponse { get; set; }
public byte[]? ResultObjectId { get; set; }
public ClientStreamWriter? ClientStreamWriter { get; private set; } = new ();
public List<PutSingleRequest> PutSingleRequests { get; private set; } = [];
public List<DeleteRequest> DeleteRequests { get; private set; } = [];
public List<HeadRequest> HeadRequests { get; private set; } = [];
}

View file

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

View file

@ -1,66 +1,237 @@
using FrostFS.Refs;
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
using Google.Protobuf;
using Microsoft.Extensions.Options;
using System.Security.Cryptography;
using System.Text;
namespace FrostFS.SDK.Tests;
public class ObjectTest
public abstract class ObjectTestsBase
{
private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
protected static readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
[Fact]
public async void GetObjectTest()
protected IOptions<ClientSettings> Settings { get; set; }
protected ContainerId ContainerId { get; set; }
protected NetworkMocker NetworkMocker { get; set; } = new NetworkMocker(key);
protected SessionMocker SessionMocker { get; set; } = new SessionMocker(key);
protected ContainerMocker ContainerMocker { get; set; } = new ContainerMocker(key);
protected ObjectMocker Mocker { get; set; }
protected ObjectTestsBase()
{
var ecdsaKey = key.LoadWif();
ContainerId cntId = new("xyz");
ObjectHeader header = new(cntId, ModelsV2.Enums.ObjectType.Regular, [new ObjectAttribute("k", "v")])
{
PayloadLength = 1,
Version = new ModelsV2.Version(2, 13),
OwnerId = OwnerId.FromKey(ecdsaKey)
};
var objectMockFactory = new ObjectMockFactory(this.key)
{
PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
Version = new ModelsV2.Version(2, 13),
ContainerGuid = Guid.NewGuid(),
ObjectHeader = header
};
var settings = Options.Create(new ClientSettings
Settings = Options.Create(new ClientSettings
{
Key = key,
Host = "http://localhost:8080"
});
var fsClient = Client.GetTestInstance(
settings,
Mocker = new ObjectMocker(key)
{
PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
Version = new ModelsV2.Version(2, 13),
ContainerGuid = Guid.NewGuid()
};
ContainerId = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes()));
Mocker.ObjectHeader = new(ContainerId, ModelsV2.Enums.ObjectType.Regular, [new ObjectAttribute("k", "v")])
{
PayloadLength = 1,
Version = new ModelsV2.Version(2, 13),
OwnerId = OwnerId.FromKey(ecdsaKey)
};
}
protected IFrostFSClient GetClient()
{
return Client.GetTestInstance(
Settings,
null,
new NetmapMockFactory(this.key).GetMock().Object,
new SessionMockFactory(this.key).GetMock().Object,
new ContainerStub(this.key).GetMock().Object,
objectMockFactory.GetMock().Object);
NetworkMocker.GetMock().Object,
SessionMocker.GetMock().Object,
ContainerMocker.GetMock().Object,
Mocker.GetMock().Object);
}
}
var objectId = fsClient.CalculateObjectId(header);
public class ObjectTest : ObjectTestsBase
{
[Fact]
public async void GetObjectTest()
{
var client = GetClient();
var objectId = client.CalculateObjectId(Mocker.ObjectHeader!);
var containerId = new ContainerId(Base58.Encode(objectMockFactory.ContainerGuid.ToBytes()));
var context = new Context
{
Timeout = TimeSpan.FromSeconds(2)
};
var result = await fsClient.GetObjectAsync(containerId, objectId);
var result = await client.GetObjectAsync(ContainerId, objectId, context);
Assert.NotNull(result);
Assert.Equal(header.ContainerId.Value, result.Header.ContainerId.Value);
Assert.Equal(header.OwnerId.ToGrpcMessage().ToString(), result.Header.OwnerId!.Value);
Assert.Equal(header.PayloadLength, result.Header.PayloadLength);
Assert.Equal(Mocker.ObjectHeader!.ContainerId.Value, result.Header.ContainerId.Value);
Assert.Equal(Mocker.ObjectHeader!.OwnerId!.ToGrpcMessage().ToString(), result.Header.OwnerId!.Value);
Assert.Equal(Mocker.ObjectHeader.PayloadLength, result.Header.PayloadLength);
Assert.Single(result.Header.Attributes);
Assert.Equal(header.Attributes[0].Key, result.Header.Attributes[0].Key);
Assert.Equal(header.Attributes[0].Value,result.Header.Attributes[0].Value);
Assert.Equal(Mocker.ObjectHeader.Attributes[0].Key, result.Header.Attributes[0].Key);
Assert.Equal(Mocker.ObjectHeader.Attributes[0].Value,result.Header.Attributes[0].Value);
}
[Fact]
public async void PutObjectTest()
{
Mocker.ResultObjectId = SHA256.HashData([]);
Random rnd = new();
var bytes = new byte[1024];
rnd.NextBytes(bytes);
var param = new PutObjectParameters
{
Header = Mocker.ObjectHeader,
Payload = new MemoryStream(bytes),
ClientCut = false
};
var result = await GetClient().PutObjectAsync(param);
var sentMessages = Mocker.ClientStreamWriter!.Messages;
var body1 = sentMessages.ElementAt(0).GetBody() as Object.PutRequest.Types.Body;
var body2 = sentMessages.ElementAt(1).GetBody() as Object.PutRequest.Types.Body;
Assert.NotNull(result);
Assert.Equal(Mocker.ResultObjectId, result.ToHash());
Assert.True(Mocker.ClientStreamWriter.CompletedTask);
Assert.Equal(0, body1!.Chunk.Length);
Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Init, body1!.ObjectPartCase);
Assert.Equal(1024, body2!.Chunk.Length);
Assert.Equal(Object.PutRequest.Types.Body.ObjectPartOneofCase.Chunk, body2!.ObjectPartCase);
}
[Fact]
public async void ClientCutTest()
{
NetworkMocker.Parameters = new Dictionary<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.Netmap;
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Options;
using Grpc.Core.Interceptors;
using System.Diagnostics;
namespace FrostFS.SDK.Tests;
namespace FrostFS.SDK.SmokeTests;
public class ClientTestLive
public class SmokeTests
{
private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
private readonly string url = "http://172.29.238.97:8080";
@ -46,11 +45,24 @@ public class ClientTestLive
Assert.Equal(2, result.Version.Major);
Assert.Equal(13, result.Version.Minor);
Assert.Equal(NodeState.Online, result.State);
Assert.True(result.PublicKey.Length > 0);
Assert.Equal(33, result.PublicKey.Length);
Assert.Single(result.Addresses);
Assert.Equal(9, result.Attributes.Count);
}
[Fact]
public async void NodeInfo_Statictics_Test()
{
var ctx = new Context
{
Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds")
};
using var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
var result = await fsClient.GetNodeInfoAsync();
}
[Fact]
public async void SimpleScenarioTest()
{
@ -76,9 +88,7 @@ public class ClientTestLive
Assert.NotNull(container);
Random rnd = new();
var bytes = new byte[6 * 1024 * 1024 + 100];
rnd.NextBytes(bytes);
var bytes = GetRandomBytes(6 * 1024 * 1024 + 100);
var param = new PutObjectParameters
{
@ -141,25 +151,21 @@ public class ClientTestLive
await Cleanup(fsClient);
var containerId = await fsClient.CreateContainerAsync(
new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))));
var cnt = new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)));
var context = new Context
{
Timeout = TimeSpan.FromSeconds(10)
var containerId = await fsClient.CreateContainerAsync(cnt);
var context = new Context
{
Timeout = TimeSpan.FromSeconds(10),
Interceptors = new([new MetricsInterceptor()])
};
var metrics = new MetricsInterceptor();
context.Interceptors.Add(metrics);
var container = await GetContainer(fsClient, containerId, context);
Assert.NotNull(container);
Random rnd = new();
var bytes = new byte[6 * 1024 * 1024 + 100];
rnd.NextBytes(bytes);
byte[] bytes = GetRandomBytes(150 * 1024 * 1024);
var param = new PutObjectParameters
{
@ -200,6 +206,8 @@ public class ClientTestLive
ms.Write(chunk);
}
Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
await Cleanup(fsClient);
await Task.Delay(2000);
@ -210,15 +218,21 @@ public class ClientTestLive
}
}
private static byte[] GetRandomBytes(int size)
{
Random rnd = new();
var bytes = new byte[size];
rnd.NextBytes(bytes);
return bytes;
}
private static IOptions<ClientSettings> GetOptions(string key, string url)
{
var settings = new ClientSettings
return Options.Create(new ClientSettings
{
Key = key,
Host = url
};
return Options.Create(settings);
});
}
static async Task Cleanup(IFrostFSClient fsClient)
@ -243,7 +257,7 @@ public class ClientTestLive
if (DateTime.UtcNow >= ctx.Deadline)
throw new TimeoutException();
}
catch (Grpc.Core.RpcException)
catch (RpcException)
{
throw;
}
@ -268,7 +282,7 @@ public class MetricsInterceptor() : Interceptor
call.Dispose);
}
private async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
private static async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
{
var watch = new Stopwatch();
watch.Start();

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB