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" + } + ] +}