[#15] Unit tests #16

Merged
PavelGrossSpb merged 1 commit from PavelGrossSpb/frostfs-sdk-csharp:UnitTests into master 2024-07-10 14:04:58 +00:00
43 changed files with 884 additions and 477 deletions

View file

@ -13,7 +13,6 @@ using Microsoft.Extensions.Options;
using System;
using System.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);
}
catch (UriFormatException e)
{
var msg = $"Host '{host}' has invalid format. Error: {e.Message}";
throw new ArgumentException(msg);
}
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)
{
throw new ArgumentException($"Host '{host}' has invalid format. Error: {e.Message}");
}
}
}

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

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

It looks like something that does not relate to the commit called "Unit tests" It is better to separate commits logically.
model.Split.Children.AddRange(header.Split.Children.Select(x => x.ToModel()));
}

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(initRequest, ctx);
using var stream = await PutObjectInit(request, 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)
{
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 OwnerId? OwnerId { get; set; }
public List<ObjectAttribute> Attributes { get; set; }
public ContainerId ContainerId { get; set; }
public ulong PayloadLength { get; set; }
public byte[]? PayloadCheckSum { get; set; }
public ObjectType ObjectType { get; set; }
public Version? Version { get; set; }
public Split? Split { get; set; }
public ObjectHeader(
public class ObjectHeader(
ContainerId containerId,
ObjectType type = ObjectType.Regular,
params ObjectAttribute[] attributes
)
{
Attributes = [.. attributes];
ContainerId = containerId;
ObjectType = type;
}
public OwnerId? OwnerId { get; set; }
public List<ObjectAttribute> Attributes { get; set; } = [.. attributes];
public ContainerId ContainerId { get; set; } = containerId;
public ulong PayloadLength { get; set; }
public byte[]? PayloadCheckSum { get; set; }
public ObjectType ObjectType { get; set; } = type;
public Version? Version { get; set; }
public Split? Split { get; set; }
}

View file

@ -4,21 +4,15 @@ using FrostFS.SDK.Cryptography;
namespace FrostFS.SDK.ModelsV2;
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
{
var factory = new PutContainerMockFactory(this.key)
protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
protected IOptions<ClientSettings> Settings { get; set; }
protected ContainerMocker Mocker { get; set; }
protected ContainerTestsBase()
{
Settings = Options.Create(new ClientSettings
{
Key = key,
Host = "http://localhost:8080"
});
Mocker = new ContainerMocker(this.key)
{
PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
Version = new ModelsV2.Version(2, 13),
ContainerGuid = Guid.NewGuid()
};
}
var settings = Options.Create(new ClientSettings
protected IFrostFSClient GetClient()
{
Key = key,
Host = "http://localhost:8080"
});
var fsClient = Client.GetTestInstance(
settings,
return ClientV2.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);
new NetworkMocker(this.key).GetMock().Object,
new SessionMocker(this.key).GetMock().Object,
Mocker.GetMock().Object,
new ObjectMocker(this.key).GetMock().Object);
}
}
var result = await fsClient.CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, factory.PlacementPolicy));
public class ContainerTest : ContainerTestsBase
{
[Fact]
public async void CreateContainerTest()
{
var result = await GetClient().CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, Mocker.PlacementPolicy));
Assert.NotNull(result);
Assert.NotNull(result.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,32 +1,23 @@
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>(),
@ -37,65 +28,181 @@ public class ObjectMockFactory(string key) : ObjectServiceBase(key)
Verifier.CheckRequest(r);
return new AsyncServerStreamingCall<GetResponse>(
new AsyncStreamReaderMock(key, ObjectHeader),
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 containerId = await fsClient.CreateContainerAsync(cnt);
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);
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