[#29] Client: Add PlacementVector unit tests
All checks were successful
DCO / DCO (pull_request) Successful in 25s
lint-build / dotnet8.0 (pull_request) Successful in 1m5s
lint-build / dotnet8.0 (push) Successful in 41s

Signed-off-by: Pavel Gross <p.gross@yadro.com>
This commit is contained in:
Pavel Gross 2025-01-13 10:34:44 +03:00
parent 568bdc67e8
commit 43e300c773
33 changed files with 3054 additions and 234 deletions

View file

@ -23,7 +23,7 @@ public abstract class ServiceBase(string key)
public FrostFsPlacementPolicy PlacementPolicy { get; set; } = DefaultPlacementPolicy;
public static FrostFsVersion DefaultVersion { get; } = new(2, 13);
public static FrostFsPlacementPolicy DefaultPlacementPolicy { get; } = new FrostFsPlacementPolicy(true, new FrostFsReplica(1));
public static FrostFsPlacementPolicy DefaultPlacementPolicy { get; } = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1));
#pragma warning disable CA2227 // this is specific object, should be treated as is
public Metadata? Metadata { get; set; }

View file

@ -166,7 +166,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase
var token = await pool.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["key1", "value1"]);
@ -216,7 +216,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase
await Cleanup(pool);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await pool.CreateContainerAsync(createContainerParam, default);
@ -311,7 +311,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase
await Cleanup(pool);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -396,7 +396,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.FromSeconds(20));
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams);
var container = await pool.CreateContainerAsync(createContainerParam, ctx);
@ -479,7 +479,7 @@ public class MultithreadPoolSmokeTests : SmokeTestsBase
await Cleanup(pool);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await pool.CreateContainerAsync(createContainerParam, default);

View file

@ -99,7 +99,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["key1", "value1"]);
@ -144,7 +144,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await client.CreateContainerAsync(createContainerParam, default);
@ -237,7 +237,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.FromSeconds(20));
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -308,7 +308,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -388,7 +388,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -442,7 +442,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -499,7 +499,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.FromSeconds(20));
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams);
var container = await client.CreateContainerAsync(createContainerParam, ctx);
@ -580,7 +580,7 @@ public class MultithreadSmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await client.CreateContainerAsync(createContainerParam, default);

View file

@ -164,7 +164,7 @@ public class PoolSmokeTests : SmokeTestsBase
var token = await pool.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["key1", "value1"]);
@ -214,7 +214,7 @@ public class PoolSmokeTests : SmokeTestsBase
await Cleanup(pool);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await pool.CreateContainerAsync(createContainerParam, default);
@ -311,7 +311,7 @@ public class PoolSmokeTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.Zero);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -396,7 +396,7 @@ public class PoolSmokeTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.FromSeconds(20));
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams);
var container = await pool.CreateContainerAsync(createContainerParam, ctx);
@ -480,7 +480,7 @@ public class PoolSmokeTests : SmokeTestsBase
await Cleanup(pool);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await pool.CreateContainerAsync(createContainerParam, default);

View file

@ -81,7 +81,7 @@ public class SmokeClientTests : SmokeTestsBase
var token = await client.CreateSessionAsync(new PrmSessionCreate(int.MaxValue), default);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["key1", "value1"]);
@ -126,7 +126,7 @@ public class SmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await client.CreateContainerAsync(createContainerParam, default);
@ -219,7 +219,7 @@ public class SmokeClientTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.FromSeconds(20));
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -300,7 +300,7 @@ public class SmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -380,7 +380,7 @@ public class SmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -436,7 +436,7 @@ public class SmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams,
xheaders: ["testKey", "testValue"]);
@ -493,7 +493,7 @@ public class SmokeClientTests : SmokeTestsBase
var ctx = new CallContext(TimeSpan.FromSeconds(20));
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
PrmWait.DefaultParams);
var container = await client.CreateContainerAsync(createContainerParam, ctx);
@ -573,7 +573,7 @@ public class SmokeClientTests : SmokeTestsBase
await Cleanup(client);
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))),
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1))),
lightWait);
var containerId = await client.CreateContainerAsync(createContainerParam, default);

View file

@ -0,0 +1,100 @@
{
"name": "default CBF is 3",
"nodes": [
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "DE"
},
{
"key": "City",
"value": "Berlin"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "FR"
},
{
"key": "City",
"value": "Paris"
}
]
}
],
"tests": [
{
"name": "set default CBF",
"policy": {
"replicas": [
{
"count": 1,
"selector": "EU"
}
],
"containerBackupFactor": 0,
"selectors": [
{
"name": "EU",
"count": 1,
"clause": "SAME",
"attribute": "Location",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2
]
]
}
]
}

View file

@ -0,0 +1,101 @@
{
"name": "Real node count multiplier is in range [1, specified CBF]",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Country",
"value": "DE"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Country",
"value": "DE"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Country",
"value": "DE"
}
]
}
],
"tests": [
{
"name": "select 2, CBF is 2",
"policy": {
"replicas": [
{
"count": 1,
"selector": "X"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "X",
"count": 2,
"clause": "SAME",
"attribute": "Country",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2
]
]
},
{
"name": "select 3, CBF is 2",
"policy": {
"replicas": [
{
"count": 1,
"selector": "X"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "X",
"count": 3,
"clause": "SAME",
"attribute": "Country",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2
]
]
}
]
}

View file

@ -0,0 +1,156 @@
{
"name": "CBF requirements",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Attr",
"value": "Same"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Attr",
"value": "Same"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Attr",
"value": "Same"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "4"
},
{
"key": "Attr",
"value": "Same"
}
]
}
],
"tests": [
{
"name": "default CBF, no selector",
"policy": {
"replicas": [
{
"count": 2
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
2,
1,
3
]
]
},
{
"name": "explicit CBF, no selector",
"policy": {
"replicas": [
{
"count": 2
}
],
"containerBackupFactor": 3,
"selectors": [],
"filters": []
},
"result": [
[
0,
2,
1,
3
]
]
},
{
"name": "select distinct, weak CBF",
"policy": {
"replicas": [
{
"count": 2,
"selector": "X"
}
],
"containerBackupFactor": 3,
"selectors": [
{
"name": "X",
"count": 2,
"clause": "DISTINCT",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
2,
1,
3
]
]
},
{
"name": "select same, weak CBF",
"policy": {
"replicas": [
{
"count": 2,
"selector": "X"
}
],
"containerBackupFactor": 3,
"selectors": [
{
"name": "X",
"count": 2,
"clause": "SAME",
"attribute": "Attr",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2,
3
]
]
}
]
}

View file

@ -0,0 +1,345 @@
{
"name": "compound filter",
"nodes": [
{
"attributes": [
{
"key": "Storage",
"value": "SSD"
},
{
"key": "Rating",
"value": "10"
},
{
"key": "IntField",
"value": "100"
},
{
"key": "Param",
"value": "Value1"
}
]
}
],
"tests": [
{
"name": "good",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "SSD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
},
{
"name": "Main",
"op": "AND",
"filters": [
{
"name": "StorageSSD",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"op": "Unspecified",
"filters": []
},
{
"op": "OR",
"filters": [
{
"key": "Param",
"op": "EQ",
"value": "Value1",
"filters": []
},
{
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
},
"result": [
[
0
]
]
},
{
"name": "bad storage type",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "HDD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
},
{
"name": "Main",
"op": "AND",
"filters": [
{
"name": "StorageSSD",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"op": "OR",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value1",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
}
},
{
"name": "bad rating",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "SSD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "15",
"filters": []
},
{
"name": "Main",
"op": "AND",
"filters": [
{
"name": "StorageSSD",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"op": "OR",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value1",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
}
},
{
"name": "bad param",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "StorageSSD",
"key": "Storage",
"op": "EQ",
"value": "SSD",
"filters": []
},
{
"name": "GoodRating",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
},
{
"name": "Main",
"op": "AND",
"filters": [
{
"name": "StorageSSD",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"key": "IntField",
"op": "LT",
"value": "123",
"filters": []
},
{
"name": "GoodRating",
"op": "Unspecified",
"filters": []
},
{
"name": "",
"op": "OR",
"filters": [
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value0",
"filters": []
},
{
"name": "",
"key": "Param",
"op": "EQ",
"value": "Value2",
"filters": []
}
]
}
]
}
]
}
}
]
}

View file

@ -0,0 +1,81 @@
{
"name": "invalid integer field",
"nodes": [
{
"attributes": [
{
"key": "IntegerField",
"value": "true"
}
]
},
{
"attributes": [
{
"key": "IntegerField",
"value": "str"
}
]
}
],
"tests": [
{
"name": "empty string is not casted to 0",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "IntegerField",
"op": "LE",
"value": "8",
"filters": []
}
]
}
},
{
"name": "non-empty string is not casted to a number",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "IntegerField",
"op": "GE",
"value": "0",
"filters": []
}
]
}
}
]
}

View file

@ -0,0 +1,397 @@
{
"name": "single-op filters",
"nodes": [
{
"attributes": [
{
"key": "Rating",
"value": "4"
},
{
"key": "Country",
"value": "Germany"
}
]
}
],
"tests": [
{
"name": "GE true",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GE",
"value": "4",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "GE false",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GE",
"value": "5",
"filters": []
}
]
}
},
{
"name": "GT true",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GT",
"value": "3",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "GT false",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "GT",
"value": "4",
"filters": []
}
]
}
},
{
"name": "LE true",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LE",
"value": "4",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "LE false",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LE",
"value": "3",
"filters": []
}
]
}
},
{
"name": "LT true",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LT",
"value": "5",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "LT false",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Rating",
"op": "LT",
"value": "4",
"filters": []
}
]
}
},
{
"name": "EQ true",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "EQ",
"value": "Germany",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "EQ false",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "EQ",
"value": "China",
"filters": []
}
]
}
},
{
"name": "NE true",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "NE",
"value": "France",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "NE false",
"policy": {
"replicas": [
{
"count": 1,
"selector": "S"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "S",
"count": 1,
"clause": "DISTINCT",
"filter": "Main"
}
],
"filters": [
{
"name": "Main",
"key": "Country",
"op": "NE",
"value": "Germany",
"filters": []
}
]
}
}
]
}

View file

@ -0,0 +1,225 @@
{
"name": "HRW ordering",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Price",
"value": "2"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Price",
"value": "4"
},
{
"key": "Capacity",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Price",
"value": "3"
},
{
"key": "Capacity",
"value": "10"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Price",
"value": "2"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Price",
"value": "1"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Price",
"value": "100"
},
{
"key": "Capacity",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Price",
"value": "7"
},
{
"key": "Capacity",
"value": "10000"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Price",
"value": "2"
},
{
"key": "Capacity",
"value": "1"
}
]
}
],
"tests": [
{
"name":"select 3 nodes in 3 distinct countries, same placement",
"policy": {
"containerBackupFactor": 1,
"filters": [],
"replicas": [
{
"count": 1,
"selector": "Main"
}
],
"selectors": [
{
"attribute": "Country",
"clause": "DISTINCT",
"count": 3,
"filter": "*",
"name": "Main"
}
]
},
"pivot": "Y29udGFpbmVySUQ=",
"result": [[
5,
0,
7
]],
"placement": {
"pivot": "b2JqZWN0SUQ=",
"result": [[
5,
0,
7
]]
}
},
{
"name":"select 6 nodes in 3 distinct countries, different placement",
"policy": {
"containerBackupFactor": 2,
"filters": [
],
"replicas": [
{
"count": 1,
"selector": "Main"
}
],
"selectors": [
{
"attribute": "Country",
"clause": "DISTINCT",
"count": 3,
"filter": "*",
"name": "Main"
}
]
},
"pivot": "Y29udGFpbmVySUQ=",
"result": [[
5,
4,
0,
1,
7,
2]],
"placement": {
"pivot": "b2JqZWN0SUQ=",
"result": [[
5,
4,
0,
7,
2,
1]]
}
}
]
}

View file

@ -0,0 +1,107 @@
{
"name": "unnamed selector (nspcc-dev/neofs-api-go#213)",
"nodes": [
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Russia"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Russia"
},
{
"key": "City",
"value": "Saint-Petersburg"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Sweden"
},
{
"key": "City",
"value": "Stockholm"
}
]
},
{
"attributes": [
{
"key": "Location",
"value": "Europe"
},
{
"key": "Country",
"value": "Finalnd"
},
{
"key": "City",
"value": "Helsinki"
}
]
}
],
"tests": [
{
"name": "test",
"policy": {
"replicas": [
{
"count": 4
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "",
"count": 4,
"clause": "DISTINCT",
"filter": "LOC_EU"
}
],
"filters": [
{
"name": "LOC_EU",
"key": "Location",
"op": "EQ",
"value": "Europe",
"filters": []
}
]
},
"result": [
[
0,
1,
2,
3
]
]
}
]
}

View file

@ -0,0 +1,279 @@
{
"name": "single-op filters",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "1"
},
{
"key": "City",
"value": "SPB"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Rating",
"value": "5"
},
{
"key": "City",
"value": "Berlin"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "6"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Rating",
"value": "4"
},
{
"key": "City",
"value": "Paris"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "France"
},
{
"key": "Rating",
"value": "1"
},
{
"key": "City",
"value": "Lyon"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "5"
},
{
"key": "City",
"value": "SPB"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "7"
},
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Rating",
"value": "3"
},
{
"key": "City",
"value": "Darmstadt"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
},
{
"key": "Rating",
"value": "7"
},
{
"key": "City",
"value": "Frankfurt"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "9"
},
{
"key": "City",
"value": "SPB"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Russia"
},
{
"key": "Rating",
"value": "9"
},
{
"key": "City",
"value": "SPB"
}
]
}
],
"tests": [
{
"name": "Select",
"policy": {
"replicas": [
{
"count": 1,
"selector": "SameRU"
},
{
"count": 1,
"selector": "DistinctRU"
},
{
"count": 1,
"selector": "Good"
},
{
"count": 1,
"selector": "Main"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "SameRU",
"count": 2,
"clause": "SAME",
"attribute": "City",
"filter": "FromRU"
},
{
"name": "DistinctRU",
"count": 2,
"clause": "DISTINCT",
"attribute": "City",
"filter": "FromRU"
},
{
"name": "Good",
"count": 2,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "Good"
},
{
"name": "Main",
"count": 3,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "*"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia"
},
{
"name": "Good",
"key": "Rating",
"op": "GE",
"value": "4"
}
]
},
"result": [
[
0,
5,
9,
10
],
[
2,
6,
0,
5
],
[
1,
8,
2,
5
],
[
3,
4,
1,
7,
0,
2
]
]
}
]
}

View file

@ -0,0 +1,93 @@
{
"name": "multiple replicas (#215)",
"nodes": [
{
"attributes": [
{
"key": "City",
"value": "Saint-Petersburg"
}
]
},
{
"attributes": [
{
"key": "City",
"value": "Moscow"
}
]
},
{
"attributes": [
{
"key": "City",
"value": "Berlin"
}
]
},
{
"attributes": [
{
"key": "City",
"value": "Paris"
}
]
}
],
"tests": [
{
"name": "test",
"policy": {
"replicas": [
{
"count": 1,
"selector": "LOC_SPB_PLACE"
},
{
"count": 1,
"selector": "LOC_MSK_PLACE"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "LOC_SPB_PLACE",
"count": 1,
"clause": "UNSPECIFIED",
"filter": "LOC_SPB"
},
{
"name": "LOC_MSK_PLACE",
"count": 1,
"clause": "UNSPECIFIED",
"filter": "LOC_MSK"
}
],
"filters": [
{
"name": "LOC_SPB",
"key": "City",
"op": "EQ",
"value": "Saint-Petersburg",
"filters": []
},
{
"name": "LOC_MSK",
"key": "City",
"op": "EQ",
"value": "Moscow",
"filters": []
}
]
},
"result": [
[
0
],
[
1
]
]
}
]
}

View file

@ -0,0 +1,328 @@
{
"name": "multiple REP, asymmetric",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "0"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "4"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "5"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "6"
},
{
"key": "Continent",
"value": "NA"
},
{
"key": "City",
"value": "NewYork"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "7"
},
{
"key": "Continent",
"value": "AF"
},
{
"key": "City",
"value": "Cairo"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "8"
},
{
"key": "Continent",
"value": "AF"
},
{
"key": "City",
"value": "Cairo"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "9"
},
{
"key": "Continent",
"value": "SA"
},
{
"key": "City",
"value": "Lima"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "10"
},
{
"key": "Continent",
"value": "AF"
},
{
"key": "City",
"value": "Cairo"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "11"
},
{
"key": "Continent",
"value": "NA"
},
{
"key": "City",
"value": "NewYork"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "12"
},
{
"key": "Continent",
"value": "NA"
},
{
"key": "City",
"value": "LosAngeles"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "13"
},
{
"key": "Continent",
"value": "SA"
},
{
"key": "City",
"value": "Lima"
}
]
}
],
"tests": [
{
"name": "test",
"policy": {
"replicas": [
{
"count": 1,
"selector": "SPB"
},
{
"count": 2,
"selector": "Americas"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "SPB",
"count": 1,
"clause": "SAME",
"attribute": "City",
"filter": "SPBSSD"
},
{
"name": "Americas",
"count": 2,
"clause": "DISTINCT",
"attribute": "City",
"filter": "Americas"
}
],
"filters": [
{
"name": "SPBSSD",
"op": "AND",
"filters": [
{
"name": "",
"key": "Country",
"op": "EQ",
"value": "RU",
"filters": []
},
{
"name": "",
"key": "City",
"op": "EQ",
"value": "St.Petersburg",
"filters": []
},
{
"name": "",
"key": "SSD",
"op": "EQ",
"value": "1",
"filters": []
}
]
},
{
"name": "Americas",
"op": "OR",
"filters": [
{
"name": "",
"key": "Continent",
"op": "EQ",
"value": "NA",
"filters": []
},
{
"name": "",
"key": "Continent",
"op": "EQ",
"value": "SA",
"filters": []
}
]
}
]
},
"result": [
[
1,
4
],
[
8,
12,
5,
10
]
]
}
]
}

View file

@ -0,0 +1,97 @@
{
"name": "non-strict selections",
"comment": "These test specify loose selection behaviour, to allow fetching already PUT objects even when there is not enough nodes to select from.",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Russia"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
}
]
},
{
"attributes": []
}
],
"tests": [
{
"name": "not enough nodes (backup factor)",
"policy": {
"replicas": [
{
"count": 1,
"selector": "MyStore"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "MyStore",
"count": 2,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromRU"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia",
"filters": []
}
]
},
"result": [
[
0
]
]
},
{
"name": "not enough nodes (buckets)",
"policy": {
"replicas": [
{
"count": 1,
"selector": "MyStore"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "MyStore",
"count": 2,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromRU"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia",
"filters": []
}
]
},
"result": [
[
0
]
]
}
]
}

View file

@ -0,0 +1,113 @@
{
"name": "REP X",
"nodes": [
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Saint-Petersburg"
}
],
"state": "Unspecified"
},
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Moscow"
}
],
"state": "Unspecified"
},
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Berlin"
}
],
"state": "Unspecified"
},
{
"publicKey": "",
"addresses": [],
"attributes": [
{
"key": "City",
"value": "Paris"
}
],
"state": "Unspecified"
}
],
"tests": [
{
"name": "REP 1",
"policy": {
"replicas": [
{
"count": 1
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
1,
2
]
]
},
{
"name": "REP 3",
"policy": {
"replicas": [
{
"count": 3
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
3,
1,
2
]
]
},
{
"name": "REP 5",
"policy": {
"replicas": [
{
"count": 5
}
],
"containerBackupFactor": 0,
"selectors": [],
"filters": []
},
"result": [
[
0,
1,
2,
3
]
]
}
]
}

View file

@ -0,0 +1,116 @@
{
"name": "select with unspecified attribute",
"nodes": [
{
"attributes": [
{
"key": "ID",
"value": "1"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "0"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "2"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "St.Petersburg"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "3"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
},
{
"attributes": [
{
"key": "ID",
"value": "4"
},
{
"key": "Country",
"value": "RU"
},
{
"key": "City",
"value": "Moscow"
},
{
"key": "SSD",
"value": "1"
}
]
}
],
"tests": [
{
"name": "test",
"policy": {
"replicas": [
{
"count": 1,
"selector": "X"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "X",
"count": 4,
"clause": "DISTINCT",
"filter": "*"
}
],
"filters": []
},
"result": [
[
0,
1,
2,
3
]
]
}
]
}

View file

@ -0,0 +1,87 @@
{
"name": "invalid selections",
"nodes": [
{
"attributes": [
{
"key": "Country",
"value": "Russia"
}
]
},
{
"attributes": [
{
"key": "Country",
"value": "Germany"
}
]
},
{
"attributes": []
}
],
"tests": [
{
"name": "missing filter",
"policy": {
"replicas": [
{
"count": 1,
"selector": "MyStore"
}
],
"containerBackupFactor": 1,
"selectors": [
{
"name": "MyStore",
"count": 1,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromNL"
}
],
"filters": [
{
"name": "FromRU",
"key": "Country",
"op": "EQ",
"value": "Russia",
"filters": []
}
]
},
"error": "filter not found"
},
{
"name": "not enough nodes (filter results in empty set)",
"policy": {
"replicas": [
{
"count": 1,
"selector": "MyStore"
}
],
"containerBackupFactor": 2,
"selectors": [
{
"name": "MyStore",
"count": 2,
"clause": "DISTINCT",
"attribute": "Country",
"filter": "FromMoon"
}
],
"filters": [
{
"name": "FromMoon",
"key": "Country",
"op": "EQ",
"value": "Moon",
"filters": []
}
]
}
}
]
}

View file

@ -21,7 +21,7 @@ public abstract class ContainerTestsBase
Mocker = new ContainerMocker(key)
{
PlacementPolicy = new FrostFsPlacementPolicy(true, new FrostFsReplica(1)),
PlacementPolicy = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)),
Version = new FrostFsVersion(2, 13),
ContainerGuid = Guid.NewGuid()
};

View file

@ -30,7 +30,7 @@ public abstract class ObjectTestsBase
Mocker = new ObjectMocker(key)
{
PlacementPolicy = new FrostFsPlacementPolicy(true, new FrostFsReplica(1)),
PlacementPolicy = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)),
Version = new FrostFsVersion(2, 13),
ContainerGuid = Guid.NewGuid()
};

View file

@ -1,161 +1,275 @@
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
public class PlacementVectorTests(ITestOutputHelper testOutputHelper)
{
Dictionary<string, string>[] attribs;
public PlacementVectorTests()
private static readonly JsonSerializerOptions serializeOptions = new()
{
var attribs1 = new Dictionary<string, string>
{
{"Country", "Germany" },
{"Price", "2" },
{"Capacity", "10000"}
};
PropertyNameCaseInsensitive = true
};
var attribs2 = new Dictionary<string, string>
{
{"Country", "Germany" },
{"Price", "4" },
{"Capacity", "1"}
};
var attribs3 = new Dictionary<string, string>
{
{"Country", "France" },
{"Price", "3" },
{"Capacity", "10"}
};
var attribs4 = new Dictionary<string, string>
{
{"Country", "Russia" },
{"Price", "2" },
{"Capacity", "10000"}
};
var attribs5 = new Dictionary<string, string>
{
{"Country", "Russia" },
{"Price", "1" },
{"Capacity", "10000"}
};
var attribs6 = new Dictionary<string, string>
{
{"Country", "Russia" },
{"Capacity", "10000"}
};
var attribs7 = new Dictionary<string, string>
{
{"Country", "France" },
{"Price", "100" },
{"Capacity", "10000"}
};
var attribs8 = new Dictionary<string, string>
{
{"Country", "France" },
{"Price", "7" },
{"Capacity", "10000"}
};
var attribs9 = new Dictionary<string, string>
{
{"Country", "Russia" },
{"Price", "2" },
{"Capacity", "1"}
};
attribs = [attribs1, attribs2, attribs3, attribs4, attribs5, attribs6, attribs7, attribs8, attribs9];
}
private readonly ITestOutputHelper _testOutputHelper = testOutputHelper;
[Fact]
public void PlacementVectorTest()
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" };
var key1 = new byte[] { 1 };
var key2 = new byte[] { 2 };
var key3 = new byte[] { 3 };
var nodes = new List<FrostFsNodeInfo>
foreach (var file in files.Where(f => f.EndsWith(".json", StringComparison.OrdinalIgnoreCase)))
{
new(v, NodeState.Online, addresses.AsReadOnly(), attribs[5].AsReadOnly(), key1),
new(v, NodeState.Online, addresses.AsReadOnly(), attribs[0].AsReadOnly(), key2),
new(v, NodeState.Online, addresses.AsReadOnly(), attribs[8].AsReadOnly(), key3)
};
//if (!file.EndsWith("selector_invalid.json"))
// continue;
var netmap = new FrostFsNetmapSnapshot(100, nodes.AsReadOnly());
var fileName = file[(file.LastIndexOf("..\\") + 3)..];
_testOutputHelper.WriteLine($"Open file {fileName}");
var arg = new FrostFsNodeInfo[1][];
var pivot = "objectID"u8.ToArray();
var str = File.ReadAllText(file);
Assert.False(string.IsNullOrEmpty(str));
arg[0] = [.. nodes];
var result = netmap.PlacementVectors(arg, pivot);
var testCase = JsonSerializer.Deserialize<TestCase>(str, serializeOptions);
Assert.Single(result);
Assert.Equal(3, result[0].Length);
Assert.Equal(key1, result[0][0].PublicKey);
Assert.Equal(key2, result[0][1].PublicKey);
Assert.Equal(key3, result[0][2].PublicKey);
}
Assert.NotNull(testCase);
Assert.NotNull(testCase.Nodes);
Assert.True(testCase.Nodes.Length > 0);
[Fact]
public void TestPlacementPolicyUnique()
{
FrostFsVersion version = new(2, 13);
var p = new FrostFsPlacementPolicy(true, [new FrostFsReplica(1, "S"), new FrostFsReplica(1, "S")])
{
BackupFactor = 2
};
p.Selectors.Add(new FrostFsSelector("S")
{
Attribute = "City",
Count = 1,
Filter = "*",
Clause = (int)FrostFsClause.Same
});
_testOutputHelper.WriteLine($"Test case: \"{testCase.Name}\"");
List<FrostFsNodeInfo> nodes = [];
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 cities = new string[] { "Moscow", "Berlin", "Shenzhen" };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
var netmap = new FrostFsNetmapSnapshot(100, nodes);
Assert.NotNull(testCase.Tests);
foreach (var test in testCase.Tests)
{
var attr = new Dictionary<string, string> { { "City", cities[i] } };
var key = new byte[] { (byte)(i * 4 + j) };
var node = new FrostFsNodeInfo(version, NodeState.Online, [], attr, key);
_testOutputHelper.WriteLine($"Start test \"{test.Name}\"");
nodes.Add(node);
}
}
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() ?? []
);
var netMap = new FrostFsNetmapSnapshot(100, nodes.AsReadOnly());
var v = netMap.ContainerNodes(p, null);
Assert.Equal(2, v.Length);
Assert.Equal(2, v[0].Length);
Assert.Equal(2, v[1].Length);
for (int i = 0; i < v.Length; i++)
{
foreach (var ni in v[i])
{
for (int j = 0; j < i; j++)
try
{
foreach (var nj in v[j])
var result = netmap.ContainerNodes(policy, test.PivotBytes);
if (test.Result == null)
{
Assert.NotEqual(ni.Hash, nj.Hash);
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).ToArray() : []);
}
public class ReplicaDto
{
public int 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
}

View file

@ -30,7 +30,7 @@ public abstract class SessionTestsBase
Mocker = new SessionMocker(key)
{
PlacementPolicy = new FrostFsPlacementPolicy(true, new FrostFsReplica(1)),
PlacementPolicy = new FrostFsPlacementPolicy(true, 1, [], [], new FrostFsReplica(1)),
Version = new FrostFsVersion(2, 13)
};
}