Skip to content

Commit

Permalink
test: Add test code for evidence query
Browse files Browse the repository at this point in the history
  • Loading branch information
s2quake committed Dec 26, 2024
1 parent adfc252 commit c655e2e
Show file tree
Hide file tree
Showing 3 changed files with 277 additions and 7 deletions.
37 changes: 30 additions & 7 deletions test/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Libplanet.Types.Tx;
using Libplanet.Store;
using Libplanet.Store.Trie;
using Libplanet.Tests.Blockchain.Evidence;

namespace Libplanet.Explorer.Tests;

Expand All @@ -29,6 +30,8 @@ public class GeneratedBlockChainFixture

public int MaxTxCount { get; }

public int MaxEvidenceCount { get; }

public ImmutableDictionary<Address, ImmutableArray<Block>>
MinedBlocks { get; private set; }

Expand All @@ -43,7 +46,8 @@ public GeneratedBlockChainFixture(
int maxTxCount = 20,
int privateKeyCount = 10,
ImmutableArray<ImmutableArray<ImmutableArray<SimpleAction>>>?
txActionsForSuffixBlocks = null)
txActionsForSuffixBlocks = null,
int maxEvidenceCount = 2)
{
txActionsForSuffixBlocks ??=
ImmutableArray<ImmutableArray<ImmutableArray<SimpleAction>>>.Empty;
Expand All @@ -65,6 +69,7 @@ public GeneratedBlockChainFixture(
.ToImmutableDictionary(
key => key.Address,
key => ImmutableArray<Transaction>.Empty);
MaxEvidenceCount = maxEvidenceCount;

var privateKey = new PrivateKey();
var policy = new BlockPolicy(
Expand Down Expand Up @@ -105,22 +110,24 @@ public GeneratedBlockChainFixture(

while (Chain.Count < blockCount)
{
AddBlock(GetRandomTransactions());
AddBlock(GetRandomTransactions(), GetRandomEvidence(height: Chain.Count - 1));
}

if (txActionsForSuffixBlocks is { } txActionsForSuffixBlocksVal)
{
foreach (var actionsForTransactions in txActionsForSuffixBlocksVal)
{
var pk = PrivateKeys[Random.Next(PrivateKeys.Length)];
AddBlock(actionsForTransactions
var txs = actionsForTransactions
.Select(actions =>
Transaction.Create(
nonce: Chain.GetNextTxNonce(pk.Address),
privateKey: pk,
genesisHash: Chain.Genesis.Hash,
actions: actions.ToPlainValues()))
.ToImmutableArray());
.ToImmutableArray();
var evs = ImmutableArray<EvidenceBase>.Empty;
AddBlock(txs, evs);
}
}
}
Expand Down Expand Up @@ -159,6 +166,21 @@ private Transaction GetRandomTransaction(PrivateKey pk, long nonce)
gasLimit: null);
}

private ImmutableArray<EvidenceBase> GetRandomEvidence(long height)
{
return Enumerable
.Range(0, Random.Next(MaxEvidenceCount))
.Select<int, EvidenceBase>(_ =>
{
return new TestEvidence(
height: height,
validatorAddress: new PrivateKey().Address,
timestamp: DateTimeOffset.UtcNow);
})
.OrderBy(ev => ev.Id)
.ToImmutableArray();
}

private ImmutableArray<SimpleAction> GetRandomActions()
{
return Enumerable
Expand All @@ -167,7 +189,8 @@ private ImmutableArray<SimpleAction> GetRandomActions()
.ToImmutableArray();
}

private void AddBlock(ImmutableArray<Transaction> transactions)
private void AddBlock(
ImmutableArray<Transaction> transactions, ImmutableArray<EvidenceBase> evidence)
{
var proposer = PrivateKeys[Random.Next(PrivateKeys.Length)];
var block = Chain.EvaluateAndSign(
Expand All @@ -179,9 +202,9 @@ private void AddBlock(ImmutableArray<Transaction> transactions)
Chain.Tip.Hash,
BlockContent.DeriveTxHash(transactions),
Chain.Store.GetChainBlockCommit(Chain.Store.GetCanonicalChainId()!.Value),
evidenceHash: null),
evidenceHash: BlockContent.DeriveEvidenceHash(evidence)),
transactions,
evidence: Array.Empty<EvidenceBase>()).Propose(),
evidence: evidence).Propose(),
proposer);
Chain.Append(
block,
Expand Down
58 changes: 58 additions & 0 deletions test/Libplanet.Explorer.Tests/GraphTypes/EvidenceIdTypeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using GraphQL.Language.AST;
using Libplanet.Common;
using Libplanet.Explorer.GraphTypes;
using Libplanet.Types.Evidence;
using Xunit;

namespace Libplanet.Explorer.Tests.GraphTypes
{
public class EvidenceIdTypeTest : ScalarGraphTypeTestBase<EvidenceIdType>
{
[Fact]
public void ParseLiteral()
{
Assert.Null(_type.ParseLiteral(new NullValue()));

var bytes = TestUtils.GetRandomBytes(EvidenceId.Size);
var evidenceId = new EvidenceId(bytes);
var hex = ByteUtil.Hex(bytes);
Assert.Equal(
evidenceId,
Assert.IsType<EvidenceId>(_type.ParseLiteral(new StringValue(hex))));

Assert.Throws<InvalidOperationException>(
() => _type.ParseLiteral(new LongValue(1234)));
Assert.Throws<InvalidOperationException>(
() => _type.ParseValue(new StringValue("evidenceId")));
}

[Fact]
public void ParseValue()
{
Assert.Null(_type.ParseValue(null));

var bytes = TestUtils.GetRandomBytes(EvidenceId.Size);
var evidenceId = new EvidenceId(bytes);
var hex = ByteUtil.Hex(bytes);
Assert.Equal(evidenceId, _type.ParseValue(hex));

Assert.Throws<InvalidOperationException>(() => _type.ParseValue(0));
Assert.Throws<InvalidOperationException>(() => _type.ParseValue(new EvidenceId()));
Assert.Throws<InvalidOperationException>(() => _type.ParseValue(new object()));
}

[Fact]
public void Serialize()
{
var bytes = TestUtils.GetRandomBytes(EvidenceId.Size);
var evidenceId = new EvidenceId(bytes);
var hex = ByteUtil.Hex(bytes);
Assert.Equal(hex, _type.Serialize(evidenceId));

Assert.Throws<InvalidOperationException>(() => _type.Serialize(0));
Assert.Throws<InvalidOperationException>(() => _type.Serialize(""));
Assert.Throws<InvalidOperationException>(() => _type.Serialize(new object()));
}
}
}
189 changes: 189 additions & 0 deletions test/Libplanet.Explorer.Tests/Queries/EvidenceQueryTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GraphQL.Execution;
using Libplanet.Explorer.Queries;
using Xunit;
using static Libplanet.Explorer.Tests.GraphQLTestUtils;
using Libplanet.Types.Blocks;
using System;
using Libplanet.Tests.Blockchain.Evidence;
using Libplanet.Crypto;
using Libplanet.Types.Evidence;

namespace Libplanet.Explorer.Tests.Queries;

public class EvidenceQueryTest
{
private readonly GeneratedBlockChainFixture _fixture;
private readonly MockBlockChainContext _source;
private readonly EvidenceQuery _queryGraph;

public EvidenceQueryTest()
{
_fixture = new GeneratedBlockChainFixture(seed: 0);
_source = new MockBlockChainContext(_fixture.Chain);
_queryGraph = new EvidenceQuery();
var _ = new ExplorerQuery(_source);
}

[Fact]
public async Task ExecuteCommittedEvidenceByHeightAsync()
{
var blocks = GetBlocks().ToArray();
var block = blocks[Random.Shared.Next(blocks.Length)];

var result = await ExecuteQueryAsync(@$"{{
committedEvidence(
index: {block.Index}) {{
id
type
height
targetAddress
timestamp
}}
}}", _queryGraph, source: _source);
Assert.Null(result.Errors);
var resultData = Assert.IsAssignableFrom<ExecutionNode>(result.Data);
var resultDict =
Assert.IsAssignableFrom<IDictionary<string, object>>(resultData.ToValue());
var resultEvidence = Assert.IsAssignableFrom<object[]>(resultDict["committedEvidence"]);

for (var i = 0; i < block.Evidence.Count; i++)
{
var evidence = block.Evidence[i];
var resultEvidenceDict
= Assert.IsAssignableFrom<IDictionary<string, object>>(resultEvidence[i]);
Assert.Equal(evidence.Id.ToString(), resultEvidenceDict["id"]);
Assert.Equal(evidence.GetType().FullName, resultEvidenceDict["type"]);
Assert.Equal(evidence.Height, resultEvidenceDict["height"]);
Assert.Equal(evidence.TargetAddress.ToString(), resultEvidenceDict["targetAddress"]);
Assert.Equal(
evidence.Timestamp, DateTimeOffset.Parse($"{resultEvidenceDict["timestamp"]}"));
}
}

[Fact]
public async Task ExecuteCommittedEvidenceByHashAsync()
{
var blocks = GetBlocks().ToArray();
var block = blocks[Random.Shared.Next(blocks.Length)];

var result = await ExecuteQueryAsync(@$"{{
committedEvidence(
hash: ""{block.Hash}"") {{
id
type
height
targetAddress
timestamp
}}
}}", _queryGraph, source: _source);
Assert.Null(result.Errors);
var resultData = Assert.IsAssignableFrom<ExecutionNode>(result.Data);
var resultDict =
Assert.IsAssignableFrom<IDictionary<string, object>>(resultData.ToValue());
var resultEvidence = Assert.IsAssignableFrom<object[]>(resultDict["committedEvidence"]);

for (var i = 0; i < block.Evidence.Count; i++)
{
var evidence = block.Evidence[i];
var resultEvidenceDict
= Assert.IsAssignableFrom<IDictionary<string, object>>(resultEvidence[i]);
Assert.Equal(evidence.Id.ToString(), resultEvidenceDict["id"]);
Assert.Equal(evidence.GetType().FullName, resultEvidenceDict["type"]);
Assert.Equal(evidence.Height, resultEvidenceDict["height"]);
Assert.Equal(evidence.TargetAddress.ToString(), resultEvidenceDict["targetAddress"]);
Assert.Equal(
evidence.Timestamp, DateTimeOffset.Parse($"{resultEvidenceDict["timestamp"]}"));
}
}

[Fact]
public async Task ExecutePendingEvidenceAsync()
{
var evidenceList = new List<EvidenceBase>
{
new TestEvidence(
height: _fixture.Chain.Count - 3,
validatorAddress: new PrivateKey().Address,
timestamp: DateTimeOffset.UtcNow)
};

foreach (var evidence in evidenceList)
{
_fixture.Chain.AddEvidence(evidence);
}

var result = await ExecuteQueryAsync(@$"{{
pendingEvidence {{
id
type
height
targetAddress
timestamp
}}
}}", _queryGraph, source: _source);
Assert.Null(result.Errors);
var resultData = Assert.IsAssignableFrom<ExecutionNode>(result.Data);
var resultDict =
Assert.IsAssignableFrom<IDictionary<string, object>>(resultData.ToValue());
var resultEvidence = Assert.IsAssignableFrom<object[]>(resultDict["pendingEvidence"]);

for (var i = 0; i < evidenceList.Count; i++)
{
var evidence = evidenceList[i];
var resultEvidenceDict
= Assert.IsAssignableFrom<IDictionary<string, object>>(resultEvidence[i]);
Assert.Equal(evidence.Id.ToString(), resultEvidenceDict["id"]);
Assert.Equal(evidence.GetType().FullName, resultEvidenceDict["type"]);
Assert.Equal(evidence.Height, resultEvidenceDict["height"]);
Assert.Equal(evidence.TargetAddress.ToString(), resultEvidenceDict["targetAddress"]);
Assert.Equal(
evidence.Timestamp, DateTimeOffset.Parse($"{resultEvidenceDict["timestamp"]}"));
}
}

[Fact]
public async Task ExecuteEvidenceByIdAsync()
{
var blocks = GetBlocks().ToArray();
var block = blocks[Random.Shared.Next(blocks.Length)];
var evidence = block.Evidence[Random.Shared.Next(block.Evidence.Count)];

var result = await ExecuteQueryAsync(@$"{{
evidence(id: ""{evidence.Id}"") {{
id
type
height
targetAddress
timestamp
}}
}}", _queryGraph, source: _source);
Assert.Null(result.Errors);
var resultData = Assert.IsAssignableFrom<ExecutionNode>(result.Data);
var resultDict =
Assert.IsAssignableFrom<IDictionary<string, object>>(resultData.ToValue());

var resultEvidenceDict
= Assert.IsAssignableFrom<IDictionary<string, object>>(resultDict["evidence"]);
Assert.Equal(evidence.Id.ToString(), resultEvidenceDict["id"]);
Assert.Equal(evidence.GetType().FullName, resultEvidenceDict["type"]);
Assert.Equal(evidence.Height, resultEvidenceDict["height"]);
Assert.Equal(evidence.TargetAddress.ToString(), resultEvidenceDict["targetAddress"]);
Assert.Equal(
evidence.Timestamp, DateTimeOffset.Parse($"{resultEvidenceDict["timestamp"]}"));
}

private IEnumerable<Block> GetBlocks()
{
for (var i = 0; i < _fixture.Chain.Count; i++)
{
var block = _fixture.Chain[i];
if (block.Evidence.Count > 0)
{
yield return block;
}
}
}
}

0 comments on commit c655e2e

Please sign in to comment.