frostfs-sdk-csharp/src/FrostFS.SDK.Tests/Unit/PlacementVectorTests.cs
Pavel Gross 0816be732a
All checks were successful
DCO / DCO (pull_request) Successful in 23s
lint-build / dotnet8.0 (pull_request) Successful in 34s
lint-build / dotnet8.0 (push) Successful in 35s
[#69] Fix for Patch and uint types
Signed-off-by: Pavel Gross <p.gross@yadro.com>
2025-04-30 16:26:36 +03:00

275 lines
8.4 KiB
C#

using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
using FrostFS.SDK.Client.Models.Netmap.Placement;
using Xunit.Abstractions;
namespace FrostFS.SDK.Tests.Unit;
[SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")]
public class PlacementVectorTests(ITestOutputHelper testOutputHelper)
{
private static readonly JsonSerializerOptions serializeOptions = new()
{
PropertyNameCaseInsensitive = true
};
private readonly ITestOutputHelper _testOutputHelper = testOutputHelper;
[Fact]
public void PlacementTest()
{
var path = ".\\..\\..\\..\\TestData\\PlacementTests";
Assert.True(Directory.Exists(path));
var files = Directory.GetFiles(path);
FrostFsVersion v = new(2, 13);
var addresses = new string[] { "localhost", "server1" };
foreach (var file in files.Where(f => f.EndsWith(".json", StringComparison.OrdinalIgnoreCase)))
{
//if (!file.EndsWith("selector_invalid.json"))
// continue;
var fileName = file[(file.LastIndexOf("..\\", StringComparison.OrdinalIgnoreCase) + 3)..];
_testOutputHelper.WriteLine($"Open file {fileName}");
var str = File.ReadAllText(file);
Assert.False(string.IsNullOrEmpty(str));
var testCase = JsonSerializer.Deserialize<TestCase>(str, serializeOptions);
Assert.NotNull(testCase);
Assert.NotNull(testCase.Nodes);
Assert.True(testCase.Nodes.Length > 0);
_testOutputHelper.WriteLine($"Test case: \"{testCase.Name}\"");
var nodes = testCase.Nodes
.Select(n => new FrostFsNodeInfo(v,
n.State,
addresses.AsReadOnly(),
n.Attributes?.ToDictionary(x => x.Key, x => x.Value) ?? [],
n.PublicKeyBytes
)
)
.ToArray()
.AsReadOnly();
var netmap = new FrostFsNetmapSnapshot(100, nodes);
Assert.NotNull(testCase.Tests);
foreach (var test in testCase.Tests)
{
_testOutputHelper.WriteLine($"Start test \"{test.Name}\"");
var policy = new FrostFsPlacementPolicy(
test.Policy!.Unique,
test.Policy.ContainerBackupFactor,
new Collection<FrostFsSelector>(test.Policy.Selectors?.Select(s => s.Selector).ToList() ?? []),
new Collection<FrostFsFilter>(test.Policy.Filters?.Select(f => f.Filter).ToList() ?? []),
test.Policy.Replicas?.Select(r => new FrostFsReplica(r.Count, r.Selector)).ToArray() ?? []
);
try
{
var result = netmap.ContainerNodes(policy, test.PivotBytes);
if (test.Result == null)
{
if (!string.IsNullOrEmpty(test.Error))
{
Assert.Fail("Error is expected but has not been thrown");
}
else
{
Assert.NotNull(test.Policy?.Replicas);
Assert.Equal(result.Length, test.Policy.Replicas.Length);
for (int i = 0; i < result.Length; i++)
{
Assert.Empty(result[i]);
}
}
}
else
{
Assert.Equal(test.Result.Length, result.Length);
for (var i = 0; i < test.Result.Length; i++)
{
Assert.Equal(test.Result[i].Length, result[i].Length);
for (var j = 0; j < test.Result[i].Length; j++)
{
CompareNodes(nodes[test.Result[i][j]].Attributes, result[i][j]);
}
}
if (test.Placement?.Result != null && test.Placement.PivotBytes != null)
{
var placementResult = netmap.PlacementVectors(result, test.Placement.PivotBytes);
Assert.Equal(test.Placement.Result.Length, placementResult.Length);
for (int i = 0; i < placementResult.Length; i++)
{
Assert.Equal(test.Placement.Result[i].Length, placementResult[i].Length);
for (int j = 0; j < placementResult[i].Length; j++)
{
CompareNodes(nodes[test.Placement.Result[i][j]].Attributes, placementResult[i][j]);
}
}
}
}
}
catch (Exception ex)
{
if (!string.IsNullOrEmpty(test.Error))
{
Assert.Contains(test.Error, ex.Message, StringComparison.InvariantCulture);
}
else
{
throw;
}
}
_testOutputHelper.WriteLine($"Done");
}
}
}
private static void CompareNodes(IReadOnlyDictionary<string, string> attrs, FrostFsNodeInfo nodeInfo)
{
Assert.Equal(attrs.Count, nodeInfo.Attributes.Count);
Assert.True(attrs.OrderBy(k => k.Key).SequenceEqual(nodeInfo.Attributes.OrderBy(x => x.Key)));
}
}
public class TestCase
{
public string? Name { get; set; }
public Node[]? Nodes { get; set; }
public TestData[]? Tests { get; set; }
}
public class Node
{
[JsonPropertyName("attributes")]
public KeyValuePair<string, string>[]? Attributes { get; set; }
public string? PublicKey { get; set; }
internal byte[]? PublicKeyBytes => string.IsNullOrEmpty(PublicKey) ? [] : Convert.FromBase64String(PublicKey);
public string[]? Addresses { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter<NodeState>))]
public NodeState State { get; set; } = NodeState.Online;
}
public class TestData
{
public string? Name { get; set; }
public PolicyDto? Policy { get; set; }
public string? Pivot { get; set; }
public int[][]? Result { get; set; }
public string? Error { get; set; }
internal byte[]? PivotBytes => Pivot != null ? Convert.FromBase64String(Pivot) : null;
public ResultData? Placement { get; set; }
}
public class PolicyDto
{
public bool Unique { get; set; }
public uint ContainerBackupFactor { get; set; }
public FilterDto[]? Filters { get; set; }
public ReplicaDto[]? Replicas { get; set; }
public SelectorDto[]? Selectors { get; set; }
}
public class SelectorDto()
{
public uint Count { get; set; }
public string? Name { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter<ClauseValues>))]
public ClauseValues Clause { get; set; }
public string? Attribute { get; set; }
public string? Filter { get; set; }
public FrostFsSelector Selector => new(Name ?? string.Empty)
{
Count = Count,
Clause = (int)Clause,
Filter = Filter,
Attribute = Attribute
};
}
public class FilterDto
{
public string? Name { get; set; }
public string? Key { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter<Operation>))]
public Operation Op { get; set; }
public string? Value { get; set; }
public FilterDto[]? Filters { get; set; }
public FrostFsFilter Filter => new(
Name ?? string.Empty,
Key ?? string.Empty,
(int)Op,
Value ?? string.Empty,
Filters != null ? [.. Filters.Select(f => f.Filter)] : []);
}
public class ReplicaDto
{
public uint Count { get; set; }
public string? Selector { get; set; }
}
public class ResultData
{
public string? Pivot { get; set; }
public int[][]? Result { get; set; }
internal byte[]? PivotBytes => Pivot != null ? Convert.FromBase64String(Pivot) : null;
}
public enum ClauseValues
{
UNSPECIFIED = 0,
SAME,
DISTINCT
}