diff --git a/Monero.Lws.IntegrationTests/Monero.Lws.IntegrationTests.csproj b/Monero.Lws.IntegrationTests/Monero.Lws.IntegrationTests.csproj
index fc2560c..66cefb3 100644
--- a/Monero.Lws.IntegrationTests/Monero.Lws.IntegrationTests.csproj
+++ b/Monero.Lws.IntegrationTests/Monero.Lws.IntegrationTests.csproj
@@ -27,6 +27,10 @@
.dockerignore
+
+
+ PreserveNewest
+
diff --git a/Monero.Lws.IntegrationTests/MoneroLwsServiceIntegrationTest.cs b/Monero.Lws.IntegrationTests/MoneroLwsServiceIntegrationTest.cs
index a6db976..74624e1 100644
--- a/Monero.Lws.IntegrationTests/MoneroLwsServiceIntegrationTest.cs
+++ b/Monero.Lws.IntegrationTests/MoneroLwsServiceIntegrationTest.cs
@@ -7,8 +7,7 @@ namespace Monero.Lws.IntegrationTests;
public class MoneroLwsServiceIntegrationTest
{
- private static readonly string Address = TestUtils.Address;
- private static readonly string ViewKey = TestUtils.PrivateViewKey;
+ private static readonly List Wallets = TestUtils.Config.Wallets;
private static readonly MoneroLwsService Lws = TestUtils.GetLwsService();
[Fact]
@@ -43,18 +42,21 @@ public async Task GetDaemonStatus()
[Fact]
public async Task TestLogin()
{
- var response = await Lws.Login(Address, ViewKey, true, true);
- if (response.NewAddress)
+ foreach (var wallet in Wallets)
{
- Assert.True(response.GeneratedLocally);
- if (response.StartHeight != null)
+ var response = await Lws.Login(wallet.PrimaryAddress, wallet.PrivateViewKey, true, true);
+ if (response.NewAddress)
{
- Assert.True(response.StartHeight > 0);
+ Assert.True(response.GeneratedLocally);
+ if (response.StartHeight != null)
+ {
+ Assert.True(response.StartHeight > 0);
+ }
+ }
+ else
+ {
+ Assert.Null(response.StartHeight);
}
- }
- else
- {
- Assert.Null(response.StartHeight);
}
}
@@ -88,43 +90,49 @@ public async Task TestAcceptRequests()
[Fact]
public async Task TestGetAddressInfo()
{
- var response = await Lws.GetAddressInfo(Address, ViewKey);
- Assert.True(response.StartHeight >= 0);
- Assert.True(response.BlockchainHeight > 0);
- Assert.True(response.ScannedBlockHeight > 0);
- Assert.True(response.ScannedHeight > 0);
- Assert.True(response.TransactionHeight > 0);
- Assert.False(string.IsNullOrEmpty(response.LockedFunds));
- Assert.False(string.IsNullOrEmpty(response.TotalReceived));
- Assert.False(string.IsNullOrEmpty(response.TotalSent));
-
- if (response.TotalSent != "0")
- {
- Assert.NotNull(response.SpentOutputs);
- Assert.NotEmpty(response.SpentOutputs);
- TestSpends(response.SpentOutputs);
- }
+ foreach (var wallet in Wallets)
+ {
+ var response = await Lws.GetAddressInfo(wallet.PrimaryAddress, wallet.PrivateViewKey);
+ Assert.True(response.StartHeight >= 0);
+ Assert.True(response.BlockchainHeight > 0);
+ Assert.True(response.ScannedBlockHeight > 0);
+ Assert.True(response.ScannedHeight > 0);
+ Assert.True(response.TransactionHeight > 0);
+ Assert.False(string.IsNullOrEmpty(response.LockedFunds));
+ Assert.False(string.IsNullOrEmpty(response.TotalReceived));
+ Assert.False(string.IsNullOrEmpty(response.TotalSent));
+
+ if (response.TotalSent != "0")
+ {
+ Assert.NotNull(response.SpentOutputs);
+ Assert.NotEmpty(response.SpentOutputs);
+ TestSpends(response.SpentOutputs);
+ }
- Assert.Null(response.Rates);
+ Assert.Null(response.Rates);
+ }
}
[Fact]
public async Task TestGetAddressTxs()
{
- var response = await Lws.GetAddressTxs(Address, ViewKey);
- Assert.NotNull(response.TotalReceived);
- Assert.NotEmpty(response.TotalReceived);
- Assert.True(response.ScannedHeight > 0);
- Assert.True(response.ScannedBlockHeight > 0);
- Assert.True(response.StartHeight > 0);
- Assert.True(response.BlockchainHeight > 0);
- if (!response.TotalReceived.Equals("0"))
+ foreach (var wallet in Wallets)
{
- Assert.NotNull(response.Transactions);
- Assert.NotEmpty(response.Transactions);
- foreach (var tx in response.Transactions)
+ var response = await Lws.GetAddressTxs(wallet.PrimaryAddress, wallet.PrivateViewKey);
+ Assert.NotNull(response.TotalReceived);
+ Assert.NotEmpty(response.TotalReceived);
+ Assert.True(response.ScannedHeight > 0);
+ Assert.True(response.ScannedBlockHeight > 0);
+ Assert.True(response.StartHeight >= 0);
+ Assert.True(response.BlockchainHeight > 0);
+ if (!response.TotalReceived.Equals("0"))
{
- TestTransaction(tx);
+ Assert.NotNull(response.Transactions);
+ Assert.NotEmpty(response.Transactions);
+ foreach (var tx in response.Transactions)
+ {
+ TestTransaction(tx);
+ }
}
}
}
@@ -139,51 +147,57 @@ public async Task TestGetRandomOuts()
[Fact]
public async Task TestGetUnspentOuts()
{
- var response = await Lws.GetUnspentOuts(Address, ViewKey, "0", 0, true);
- Assert.True(response.PerByteFee > 0);
- Assert.True(response.FeeMask > 0);
- Assert.False(string.IsNullOrEmpty(response.Amount));
- Assert.Equal(4, response.Fees.Count);
- long lastFee = 0;
- foreach (var fee in response.Fees)
+ foreach (var wallet in Wallets)
{
- Assert.True(fee > lastFee);
- lastFee = fee;
- }
+ var response = await Lws.GetUnspentOuts(wallet.PrimaryAddress, wallet.PrivateViewKey, "0", 0, true);
+ Assert.True(response.PerByteFee > 0);
+ Assert.True(response.FeeMask > 0);
+ Assert.False(string.IsNullOrEmpty(response.Amount));
+ Assert.Equal(4, response.Fees.Count);
+ long lastFee = 0;
+ foreach (var fee in response.Fees)
+ {
+ Assert.True(fee > lastFee);
+ lastFee = fee;
+ }
- TestOutputs(response.Outputs);
+ TestOutputs(response.Outputs);
+ }
}
[Fact]
public async Task TestImportWallet()
{
- var response = await Lws.ImportWallet(Address, ViewKey, 0);
- if (string.IsNullOrEmpty(response.ImportFee) || response.ImportFee.Equals("0"))
+ foreach (var wallet in Wallets)
{
- Assert.True(string.IsNullOrEmpty(response.PaymentAddress));
- Assert.True(string.IsNullOrEmpty(response.PaymentId));
- }
- else
- {
- Assert.False(string.IsNullOrEmpty(response.PaymentAddress));
- Assert.False(string.IsNullOrEmpty(response.PaymentId));
- }
+ var response = await Lws.ImportWallet(wallet.PrimaryAddress, wallet.PrivateViewKey, 0);
+ if (string.IsNullOrEmpty(response.ImportFee) || response.ImportFee.Equals("0"))
+ {
+ Assert.True(string.IsNullOrEmpty(response.PaymentAddress));
+ Assert.True(string.IsNullOrEmpty(response.PaymentId));
+ }
+ else
+ {
+ Assert.False(string.IsNullOrEmpty(response.PaymentAddress));
+ Assert.False(string.IsNullOrEmpty(response.PaymentId));
+ }
- Assert.NotNull(response.Status);
+ Assert.NotNull(response.Status);
- if (response.NewRequest)
- {
- Assert.False(response.RequestFulfilled);
- Assert.Equal("Accepted, waiting for approval", response.Status);
- }
+ if (response.NewRequest)
+ {
+ Assert.False(response.RequestFulfilled);
+ Assert.Equal("Accepted, waiting for approval", response.Status);
+ }
- if (response.RequestFulfilled)
- {
- Assert.NotNull(response.Status);
- }
- else if (!response.NewRequest)
- {
- Assert.Equal("Waiting for Approval", response.Status);
+ if (response.RequestFulfilled)
+ {
+ Assert.NotNull(response.Status);
+ }
+ else if (!response.NewRequest)
+ {
+ Assert.Equal("Waiting for Approval", response.Status);
+ }
}
}
@@ -197,51 +211,70 @@ public async Task TestUpsertSubaddrs()
};
entry.Ranges.Add([2, 10]);
subaddrs.Add(entry);
- var response = await Lws.UpsertSubaddrs(Address, ViewKey, subaddrs, true);
- TestSubaddrs(response, true, true);
+ foreach (var wallet in Wallets)
+ {
+ var response = await Lws.UpsertSubaddrs(wallet.PrimaryAddress, wallet.PrivateViewKey, subaddrs, true);
+ TestSubaddrs(response, true, true);
+ }
}
[Fact]
public async Task TestProvisionSubaddrs()
{
- var response = await Lws.ProvisionSubaddrs(Address, ViewKey, 0, 20, 1, 1, true);
- TestSubaddrs(response, true, true);
+ foreach (var wallet in Wallets)
+ {
+ var response = await Lws.ProvisionSubaddrs(wallet.PrimaryAddress, wallet.PrivateViewKey, 0, 20, 1, 1, true);
+ TestSubaddrs(response, true, true);
+ }
}
[Fact]
public async Task TestGetSubaddrs()
{
- var response = await Lws.GetSubaddrs(Address, ViewKey);
- Assert.Empty(response.NewSubaddrs);
- Assert.NotNull(response.AllSubaddrs);
- Assert.NotEmpty(response.AllSubaddrs);
- foreach (var entry in response.AllSubaddrs)
+ foreach (var wallet in Wallets)
{
- TestSubaddrsEntry(entry);
+ var response = await Lws.GetSubaddrs(wallet.PrimaryAddress, wallet.PrivateViewKey);
+ Assert.Empty(response.NewSubaddrs);
+ Assert.NotNull(response.AllSubaddrs);
+ Assert.NotEmpty(response.AllSubaddrs);
+ foreach (var entry in response.AllSubaddrs)
+ {
+ TestSubaddrsEntry(entry);
+ }
}
}
[Fact]
public async Task TestRescan()
{
- var response = await Lws.Rescan(0, [Address]);
+ List addresses = [];
+ foreach (var wallet in Wallets)
+ {
+ addresses.Add(wallet.PrimaryAddress);
+ }
+
+ var response = await Lws.Rescan(0, addresses);
Assert.NotEmpty(response.UpdatedAddresses);
- Assert.Single(response.UpdatedAddresses);
- Assert.Equal(Address, response.UpdatedAddresses.First());
+ Assert.Equal(response.UpdatedAddresses.Count, addresses.Count);
+ TestAddressesEqual(addresses, response.UpdatedAddresses);
}
[Fact]
public async Task TestValidate()
{
- var response = await Lws.Validate(TestUtils.PublicViewKey, TestUtils.PublicSpendKey, TestUtils.PrivateViewKey);
-
- if (response.Error != null)
+ foreach (var wallet in Wallets)
{
- Assert.Fail($"{response.Error.Field}: {response.Error.Details}");
- }
+ var response = await Lws.Validate(wallet.PublicViewKey, wallet.PublicSpendKey,
+ wallet.PrivateViewKey);
+
+ if (response.Error != null)
+ {
+ Assert.Fail($"{response.Error.Field}: {response.Error.Details}");
+ }
- Assert.False(string.IsNullOrEmpty(response.Address));
- Assert.Equal(TestUtils.Address, response.Address);
+ Assert.False(string.IsNullOrEmpty(response.Address));
+ Assert.Equal(wallet.PrimaryAddress, response.Address);
+ }
}
[Fact]
@@ -284,20 +317,24 @@ public async Task TestAddAccount()
[Fact]
public async Task TestModifyAccountStatus()
{
+ List addresses = [];
+ foreach (var wallet in Wallets)
+ {
+ addresses.Add(wallet.PrimaryAddress);
+ }
+
// deactivate account
- var response = await Lws.ModifyAccountStatus("inactive", [TestUtils.Address]);
- Assert.NotEmpty(response.UpdatedAddresses);
- Assert.Single(response.UpdatedAddresses);
- Assert.Equal(TestUtils.Address, response.UpdatedAddresses.First());
+ var response = await Lws.ModifyAccountStatus("inactive", addresses);
+ TestAddressesEqual(addresses, response.UpdatedAddresses);
// wait for lws to catch up
Thread.Sleep(5000);
// reactivate account
- response = await Lws.ModifyAccountStatus("active", [TestUtils.Address]);
- Assert.NotEmpty(response.UpdatedAddresses);
- Assert.Single(response.UpdatedAddresses);
- Assert.Equal(TestUtils.Address, response.UpdatedAddresses.First());
+ response = await Lws.ModifyAccountStatus("active", addresses);
+ TestAddressesEqual(addresses, response.UpdatedAddresses);
}
+ #region Test Utils
+
private static void TestTransaction(MoneroLwsTransaction? tx)
{
Assert.NotNull(tx);
@@ -500,4 +537,19 @@ private static void TestAccounts(List? accounts)
{
TestAccounts(accounts, false);
}
+
+ private static void TestAddressesEqual(List? addresses, List? others)
+ {
+ Assert.NotNull(addresses);
+ Assert.NotNull(others);
+ Assert.Equal(addresses.Count, others.Count);
+ for (int i = 0; i < others.Count; i++)
+ {
+ var address = addresses[i];
+ var other = others[i];
+ Assert.Equal(address, other);
+ }
+ }
+
+ #endregion
}
\ No newline at end of file
diff --git a/Monero.Lws.IntegrationTests/Utils/TestConfig.cs b/Monero.Lws.IntegrationTests/Utils/TestConfig.cs
new file mode 100644
index 0000000..6456428
--- /dev/null
+++ b/Monero.Lws.IntegrationTests/Utils/TestConfig.cs
@@ -0,0 +1,69 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Monero.Lws.IntegrationTests.Utils;
+
+internal class TestConfig
+{
+ [JsonPropertyName("wallets")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List Wallets { get; set; } = [];
+
+ private void Validate()
+ {
+ if (Wallets.Count == 0)
+ {
+ throw new Exception("No wallets found in configuration");
+ }
+
+ int i = 0;
+ foreach (var wallet in Wallets)
+ {
+ ValidateWallet(wallet, i);
+ i++;
+ }
+ }
+
+ public static TestConfig Load()
+ {
+ var json = File.ReadAllText("settings.json");
+ var config = JsonSerializer.Deserialize(json);
+
+ if (config == null)
+ {
+ throw new Exception("config is null");
+ }
+
+ config.Validate();
+
+ return config;
+ }
+
+ private static void ValidateWallet(WalletInfo? info, int index)
+ {
+ if (info == null)
+ {
+ throw new Exception($"Found null configuration at wallet index {index}");
+ }
+
+ if (string.IsNullOrEmpty(info.PrimaryAddress))
+ {
+ throw new Exception($"Invalid primary address found at wallet index {index}");
+ }
+
+ if (string.IsNullOrEmpty(info.PublicViewKey))
+ {
+ throw new Exception($"Invalid public view key found at wallet index {index}");
+ }
+
+ if (string.IsNullOrEmpty(info.PublicSpendKey))
+ {
+ throw new Exception($"Invalid public spend key found at wallet index {index}");
+ }
+
+ if (string.IsNullOrEmpty(info.PrivateViewKey))
+ {
+ throw new Exception($"Invalid private view key found at wallet {index}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Monero.Lws.IntegrationTests/Utils/TestUtils.cs b/Monero.Lws.IntegrationTests/Utils/TestUtils.cs
index 29f78d6..85fec24 100644
--- a/Monero.Lws.IntegrationTests/Utils/TestUtils.cs
+++ b/Monero.Lws.IntegrationTests/Utils/TestUtils.cs
@@ -6,13 +6,7 @@ internal static class TestUtils
public static readonly Uri LwsServiceUri = new(GetDefaultEnv("XMR_LWS_URI", "http://127.0.0.1:8443"));
public const string Username = "";
public const string Password = "";
-
- public const string Address =
- "42EhKmBx6pAPYhX4QCHKBPRw8dgc3VVVdA7g2dxr5wz21crqvPUkwPTde64Xac5uawQeFbh6K7PD4YLqiX1VTP5jUH7gZez";
-
- public const string PublicViewKey = "b244f89be70e16db0d8905628480708d590ffd4e820303bb61c96cf2395bfaf1";
- public const string PublicSpendKey = "1030f7c992ec9b86cc0055d2c44632951104e67b05d28c367ecd8382c0931303";
- public const string PrivateViewKey = "41f55a92b942681e35bf7bb64f71142729039bd8e606a4f4218c543065c15c05";
+ public static readonly TestConfig Config = TestConfig.Load();
private static MoneroLwsService? _lwsService = null;
diff --git a/Monero.Lws.IntegrationTests/Utils/WalletInfo.cs b/Monero.Lws.IntegrationTests/Utils/WalletInfo.cs
new file mode 100644
index 0000000..7cb9511
--- /dev/null
+++ b/Monero.Lws.IntegrationTests/Utils/WalletInfo.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+
+namespace Monero.Lws.IntegrationTests.Utils;
+
+internal class WalletInfo
+{
+ [JsonPropertyName("primaryAddress")] public string PrimaryAddress { get; set; } = "";
+ [JsonPropertyName("publicViewKey")] public string PublicViewKey { get; set; } = "";
+ [JsonPropertyName("publicSpendKey")] public string PublicSpendKey { get; set; } = "";
+ [JsonPropertyName("privateViewKey")] public string PrivateViewKey { get; set; } = "";
+}
\ No newline at end of file
diff --git a/Monero.Lws.IntegrationTests/docker-compose.yml b/Monero.Lws.IntegrationTests/docker-compose.yml
index 7e66ec4..59c6622 100644
--- a/Monero.Lws.IntegrationTests/docker-compose.yml
+++ b/Monero.Lws.IntegrationTests/docker-compose.yml
@@ -42,7 +42,7 @@ services:
"--no-zmq",
"--max-connections-per-ip=100",
"--rpc-max-connections-per-private-ip=100",
- "--start-mining=42U9v3qs5CjZEePHBZHwuSckQXebuZu299NSmVEmQ41YJZQhKcPyujyMSzpDH4VMMVSBo3U3b54JaNvQLwAjqDhKS3rvM3L",
+ "--start-mining=4Ak1faX5RFdPbvZ8TSDvGaj8KYhPBMPAAjkS2RVfnDSa17y4MvuGnF2VpeBWHDzRHm4J8aj1MgvJWbxjGDwvWyEZNYX3B84",
"--mining-threads=1",
"--non-interactive"
]
diff --git a/Monero.Lws.IntegrationTests/settings.json b/Monero.Lws.IntegrationTests/settings.json
new file mode 100644
index 0000000..cd16f20
--- /dev/null
+++ b/Monero.Lws.IntegrationTests/settings.json
@@ -0,0 +1,34 @@
+{
+ "wallets": [
+ {
+ "primaryAddress": "42EhKmBx6pAPYhX4QCHKBPRw8dgc3VVVdA7g2dxr5wz21crqvPUkwPTde64Xac5uawQeFbh6K7PD4YLqiX1VTP5jUH7gZez",
+ "publicViewKey": "b244f89be70e16db0d8905628480708d590ffd4e820303bb61c96cf2395bfaf1",
+ "publicSpendKey": "1030f7c992ec9b86cc0055d2c44632951104e67b05d28c367ecd8382c0931303",
+ "privateViewKey": "41f55a92b942681e35bf7bb64f71142729039bd8e606a4f4218c543065c15c05"
+ },
+ {
+ "primaryAddress": "48vqoFpnN5ERGo9nfBcdCb48XLhPWbTHZ3bNdHkiC5n67wYVgRiB2Ti1kCPpbqNFwVDa6TVv9fS3hKfNZiqf45TsLYfJpFV",
+ "publicViewKey": "7e7196620e24110473e1ed38a5f1984b2796099f37fcb06f92000a594930bead",
+ "publicSpendKey": "c0c963f3903121911d66150f9048c812b6181437388ac80f80173d96fbc6ef29",
+ "privateViewKey": "4ddf79ccfc707b81681f5ddbab1cd2fa7fb001bb92768376396e002c12d9790b"
+ },
+ {
+ "primaryAddress": "43RKRsiBN9nPaXkMwAL5r1aGgrSEnqUPrje4voU7BVerBYUi5UigN3C7w82qeK2sqCjNQViB9JyCMRkoxpRZvvxSUru5QSz",
+ "publicViewKey": "07e297d3d5a07b2973505783c652dbfd4eda4d4701271a9400a42e979dab8bf6",
+ "publicSpendKey": "2f699dbcc9975186fc552764528672c6e93fb190d4a429feec29e531ee8d533f",
+ "privateViewKey": "eb45d8c771c08e8702c3e17e85d053696a3e41782aea96d619052c0d57184301"
+ },
+ {
+ "primaryAddress": "49QVG5DJXiUVyKBwfaCyXxd3Ftf6LVn6wfzUpQkoCmE1Zmvew6Tu3qPRzEdrTdVrVV9bRfxt6KdY8CPSB5HCSmUkHuY9MCK",
+ "publicViewKey": "f244240bba181e9562ea53959bb27833609ee4492279dd4413c2c9106b031195",
+ "publicSpendKey": "cd5d621ad33a45ad34b1d1170297ebd7766a5b6cdb085ce91cb427c74d59dac3",
+ "privateViewKey": "bf5e19cd9a32e7fae300419a430b2fae3345dc2ce71056ac9790fa30eefdd600"
+ },
+ {
+ "primaryAddress": "4Ak1faX5RFdPbvZ8TSDvGaj8KYhPBMPAAjkS2RVfnDSa17y4MvuGnF2VpeBWHDzRHm4J8aj1MgvJWbxjGDwvWyEZNYX3B84",
+ "publicViewKey": "b7d5b7b44f4cb9ac4fd72c2f1d021413b39f8a31c20bc3d10467341b528e0abe",
+ "publicSpendKey": "f0a2d6c96d4750872118e2c48acb6bfbdb2a1b8cfc3c2bff94179b8cad89e300",
+ "privateViewKey": "f144c669767005078546c18092167ce4cb2d6df539fe2c5b8feec9b2d8c75007"
+ }
+ ]
+}