Skip to content

Commit

Permalink
Added a new set of test actions
Browse files Browse the repository at this point in the history
  • Loading branch information
greymistcube committed Aug 9, 2024
1 parent 30d224a commit 8214808
Show file tree
Hide file tree
Showing 8 changed files with 343 additions and 0 deletions.
46 changes: 46 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Actions/AvatarAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;
using Libplanet.SDK.Action.Tests.SimpleRPG.Models;

namespace Libplanet.SDK.Action.Tests.SimpleRPG.Actions
{
[ActionType("Avatar")]
public class AvatarAction : ActionBase
{
// This has no IAccount associated with its domain.
public override Address StorageAddress => default;

[Executable]
public void Create(IValue args)
{
string name = (Text)args;
Call<InfoAction, Info>("Create", new object?[] { name });
Call<InventoryAction, Inventory>("Create");
}

[Callable]
public Avatar GetAvatar(Address address)
{
Info info = Call<InfoAction, Info>(
"GetInfo",
new object?[] { address });
Inventory inventory = Call<InventoryAction, Inventory>(
"GetInventory",
new object?[] { address });
return new Avatar(info, inventory);
}

[Callable]
public void SetAvatar(Address address, Avatar avatar)
{
Call<InfoAction>(
"SetInfo",
new object?[] { address, avatar.Info });
Call<InventoryAction>(
"SetInventory",
new object?[] { address, avatar.Inventory });
}
}
}
30 changes: 30 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Actions/FarmAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;
using Libplanet.SDK.Action.Tests.SimpleRPG.Models;

namespace Libplanet.SDK.Action.Tests.SimpleRPG.Actions
{
[ActionType("Farm")]
public class FarmAction : ActionBase
{
public const int ExpPerFarm = 10;
public const int GoldPerFarm = 20;

// This has no IAccount associated with its domain.
public override Address StorageAddress => default;

[Executable]
public void Farm(IValue args)
{
// Simple type checking.
_ = (Null)args;

Avatar avatar = Call<AvatarAction, Avatar>("GetAvatar", new object?[] { Signer });
avatar.Info.AddExp(ExpPerFarm);
avatar.Inventory.AddGold(GoldPerFarm);
Call<AvatarAction>("SetAvatar", new object?[] { Signer, avatar });
}
}
}
33 changes: 33 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Actions/InfoAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;
using Libplanet.SDK.Action.Tests.SimpleRPG.Models;

namespace Libplanet.SDK.Action.Tests.SimpleRPG.Actions
{
public class InfoAction : ActionBase
{
public override Address StorageAddress =>
new Address("0x1000000000000000000000000000000000000001");

[Callable]
public Info Create(string name)
{
if (GetState(Signer) is { } value)
{
throw new InvalidOperationException("Info already exists.");
}

Info info = new Info(name, 0);
SetInfo(Signer, info);
return info;
}

[Callable]
public Info GetInfo(Address address) =>
new Info(GetState(address) ?? throw new NullReferenceException());

[Callable]
public void SetInfo(Address address, Info info) =>
SetState(address, info.Serialized);
}
}
33 changes: 33 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Actions/InventoryAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;
using Libplanet.SDK.Action.Tests.SimpleRPG.Models;

namespace Libplanet.SDK.Action.Tests.SimpleRPG.Actions
{
public class InventoryAction : ActionBase
{
public override Address StorageAddress =>
new Address("0x1000000000000000000000000000000000000002");

[Callable]
public Inventory Create()
{
if (GetState(Signer) is { })
{
throw new InvalidOperationException("Inventory already exists.");
}

Inventory inventory = new Inventory();
SetInventory(Signer, inventory);
return inventory;
}

[Callable]
public Inventory GetInventory(Address address) =>
new Inventory(GetState(address) ?? throw new NullReferenceException());

[Callable]
public void SetInventory(Address address, Inventory inventory) =>
SetState(address, inventory.Serialized);
}
}
14 changes: 14 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Models/Avatar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Libplanet.SDK.Action.Tests.SimpleRPG.Models
{
public class Avatar
{
public Info Info { get; }
public Inventory Inventory { get; }

Check warning on line 6 in Libplanet.SDK.Action.Tests/SimpleRPG/Models/Avatar.cs

View workflow job for this annotation

GitHub Actions / docs

Elements should be separated by blank line

public Avatar(Info info, Inventory inventory)

Check warning on line 8 in Libplanet.SDK.Action.Tests/SimpleRPG/Models/Avatar.cs

View workflow job for this annotation

GitHub Actions / docs

A constructor should not follow a property
{
Info = info;
Inventory = inventory;
}
}
}
38 changes: 38 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Models/Info.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Bencodex.Types;

namespace Libplanet.SDK.Action.Tests.SimpleRPG.Models
{
public class Info
{
public string Name { get; }

public int Exp { get; private set; }

public int Level => (Exp / 100);

Check warning on line 11 in Libplanet.SDK.Action.Tests/SimpleRPG/Models/Info.cs

View workflow job for this annotation

GitHub Actions / docs

Statement should not use unnecessary parenthesis

public Info(string name)

Check warning on line 13 in Libplanet.SDK.Action.Tests/SimpleRPG/Models/Info.cs

View workflow job for this annotation

GitHub Actions / docs

A constructor should not follow a property
: this(name, 0)
{
}

public Info(IValue value)
: this((Text)((List)value)[0], (Integer)((List)value)[1])
{
}

public Info(string name, int exp)
{
Name = name;
Exp = exp;
}

public IValue Serialized => List.Empty
.Add(Name)
.Add(Exp);

public void AddExp(int exp)
{
Exp = Exp + exp;
}
}
}
31 changes: 31 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/Models/Inventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Bencodex.Types;

namespace Libplanet.SDK.Action.Tests.SimpleRPG.Models
{
public class Inventory
{
public int Gold { get; private set; }

public Inventory()

Check warning on line 9 in Libplanet.SDK.Action.Tests/SimpleRPG/Models/Inventory.cs

View workflow job for this annotation

GitHub Actions / docs

A constructor should not follow a property
: this(0)
{
}

public Inventory(IValue value)
: this((int)(Integer)value)
{
}

public Inventory(int gold)
{
Gold = gold;
}

public IValue Serialized => new Integer(Gold);

public void AddGold(int gold)
{
Gold = Gold + gold;
}
}
}
118 changes: 118 additions & 0 deletions Libplanet.SDK.Action.Tests/SimpleRPG/SimpleRPGActionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System.Collections.Immutable;
using System.Reflection;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Action.Loader;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Libplanet.SDK.Action.Tests.SimpleRPG.Actions;
using Libplanet.SDK.Action.Tests.SimpleRPG.Models;
using Libplanet.Store;
using Libplanet.Store.Trie;
using Libplanet.Types.Blocks;
using Xunit;

namespace Libplanet.SDK.Action.Tests.Sample
{
public class SimpleRPGActionsTest
{
private TypedActionLoader _loader;
private IStateStore _stateStore;
private IWorld _world;

public SimpleRPGActionsTest()
{
_loader = new TypedActionLoader(
ImmutableDictionary<IValue, Type>.Empty
.Add(new Text("Avatar"), typeof(AvatarAction))
.Add(new Text("Farm"), typeof(FarmAction)));

_stateStore = new TrieStateStore(new MemoryKeyValueStore());

ITrie trie = _stateStore.GetStateRoot(null);
trie = trie.SetMetadata(new TrieMetadata(Block.CurrentProtocolVersion));
trie = _stateStore.Commit(trie);
_world = new World(new WorldBaseState(trie, _stateStore));
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void Scenario(bool commit)
{
IValue plainValue = Dictionary.Empty
.Add("type_id", "Avatar")
.Add("call", "Create")
.Add("args", "Hero");
IAction action = Assert.IsType<AvatarAction>(_loader.LoadAction(0, plainValue));
Address signer = new PrivateKey().Address;
IWorld world = _world;

world = action.Execute(new MockActionContext(signer, signer, world));
world = commit ? _stateStore.CommitWorld(world) : world;
Assert.Equal(
new Info("Hero").Serialized,
world
.GetAccountState(new Address("0x1000000000000000000000000000000000000001"))
.GetState(signer));
Assert.Equal(
new Inventory().Serialized,
world
.GetAccountState(new Address("0x1000000000000000000000000000000000000002"))
.GetState(signer));

const int repeat = 3;
foreach (var _ in Enumerable.Range(0, repeat))

Check warning on line 65 in Libplanet.SDK.Action.Tests/SimpleRPG/SimpleRPGActionsTest.cs

View workflow job for this annotation

GitHub Actions / docs

Variable '_' should begin with lower-case letter
{
plainValue = Dictionary.Empty
.Add("type_id", "Farm")
.Add("call", "Farm")
.Add("args", Null.Value);
action = Assert.IsType<FarmAction>(_loader.LoadAction(0, plainValue));
world = action.Execute(new MockActionContext(signer, signer, world));
world = commit ? _stateStore.CommitWorld(world) : world;
}

Assert.Equal(
new Info("Hero", FarmAction.ExpPerFarm * repeat).Serialized,
world
.GetAccountState(new Address("0x1000000000000000000000000000000000000001"))
.GetState(signer));
Assert.Equal(
new Inventory(FarmAction.GoldPerFarm * repeat).Serialized,
world
.GetAccountState(new Address("0x1000000000000000000000000000000000000002"))
.GetState(signer));
}

[Fact]
public void CannotCreateTwice()
{
IValue plainValue = Dictionary.Empty
.Add("type_id", "Avatar")
.Add("call", "Create")
.Add("args", "Hero");
IAction action = Assert.IsType<AvatarAction>(_loader.LoadAction(0, plainValue));
Address signer = new PrivateKey().Address;
IWorld world = _world;

world = action.Execute(new MockActionContext(signer, signer, world));
world = _stateStore.CommitWorld(world);

plainValue = Dictionary.Empty
.Add("type_id", "Avatar")
.Add("call", "Create")
.Add("args", "Princess");
action = Assert.IsType<AvatarAction>(_loader.LoadAction(0, plainValue));
Assert.Contains(
"Info already exists",
Assert.IsType<InvalidOperationException>(

Check warning on line 109 in Libplanet.SDK.Action.Tests/SimpleRPG/SimpleRPGActionsTest.cs

View workflow job for this annotation

GitHub Actions / docs

The parameter spans multiple lines
Assert.IsType<TargetInvocationException>(
Assert.Throws<TargetInvocationException>(() =>
action.Execute(new MockActionContext(signer, signer, world)))
.InnerException)
.InnerException)
.Message);
}
}
}

0 comments on commit 8214808

Please sign in to comment.