btcpay-plugin/BTCPayServer.Plugins.IntegrationTests/Lethean/IntegrationTestUtils.cs
Claude a3869db496
rebrand(lethean): update branding, ports, and config for Lethean blockchain
- Coin: Zano → Lethean, ticker: ZAN/ZANO → LTHN
- Ports: 11211 → 36941 (mainnet RPC), 46941 (testnet RPC)
- Wallet: 11212 → 36944/46944
- Address prefix: iTHN
- URLs: zano.org → lethean.io
- Explorer links: explorer.lthn.io

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 22:24:13 +01:00

227 lines
No EOL
7 KiB
C#

using System.Diagnostics;
using BTCPayServer.Tests;
using Microsoft.Extensions.Logging;
using Mono.Unix.Native;
using Npgsql;
using static Mono.Unix.Native.Syscall;
namespace BTCPayServer.Plugins.IntegrationTests.Lethean;
public static class IntegrationTestUtils
{
private static readonly ILogger Logger = LoggerFactory
.Create(builder => builder.AddConsole())
.CreateLogger("IntegrationTestUtils");
private static readonly string ContainerWalletDir =
Environment.GetEnvironmentVariable("BTCPAY_LTHN_WALLET_DAEMON_WALLETDIR") ?? "/wallet";
public static async Task CleanUpAsync(PlaywrightTester playwrightTester)
{
if (playwrightTester.Server.PayTester.InContainer)
{
DeleteWalletInContainer();
await DropDatabaseAsync(
"btcpayserver",
"Host=postgres;Port=5432;Username=postgres;Database=postgres");
}
else
{
await RemoveWalletFromLocalDocker();
await DropDatabaseAsync(
"btcpayserver",
"Host=localhost;Port=39372;Username=postgres;Database=postgres");
}
}
private static async Task DropDatabaseAsync(string dbName, string connectionString)
{
try
{
await using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync();
await new NpgsqlCommand($"""
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = '{dbName}'
AND pid <> pg_backend_pid();
""", conn).ExecuteNonQueryAsync();
var cmd = new NpgsqlCommand($"DROP DATABASE IF EXISTS {dbName};", conn);
await cmd.ExecuteNonQueryAsync();
Logger.LogInformation("Database {DbName} dropped successfully.", dbName);
}
catch (Exception ex)
{
Logger.LogError("Failed to drop database {DbName}: {ExMessage}", dbName, ex.Message);
}
}
public static async Task CopyWalletFilesToLetheanRpcDirAsync(PlaywrightTester playwrightTester, String walletDir)
{
Logger.LogInformation("Starting to copy wallet files");
if (playwrightTester.Server.PayTester.InContainer)
{
CopyWalletFilesInContainer(walletDir);
}
else
{
await CopyWalletFilesToLocalDocker(walletDir);
}
}
private static void CopyWalletFilesInContainer(String walletDir)
{
try
{
CopyWalletFile("wallet", walletDir);
CopyWalletFile("wallet.keys", walletDir);
CopyWalletFile("password", walletDir);
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to copy wallet files to the Lethean directory.");
}
}
private static void CopyWalletFile(string name, string walletDir)
{
var resourceWalletDir = Path.Combine(AppContext.BaseDirectory, "Resources", walletDir);
var src = Path.Combine(resourceWalletDir, name);
var dst = Path.Combine(ContainerWalletDir, name);
if (!File.Exists(src))
{
return;
}
File.Copy(src, dst, overwrite: true);
// lethean ownership
if (chown(dst, 980, 980) == 0)
{
return;
}
Logger.LogError("chown failed for {File}. errno={Errno}", dst, Stdlib.GetLastError());
}
private static async Task CopyWalletFilesToLocalDocker(String walletDir)
{
try
{
var fullWalletDir = Path.Combine(AppContext.BaseDirectory, "Resources", walletDir);
await RunProcessAsync("docker",
$"cp \"{Path.Combine(fullWalletDir, "wallet")}\" lethean_wallet:/wallet/wallet");
await RunProcessAsync("docker",
$"cp \"{Path.Combine(fullWalletDir, "wallet.keys")}\" lethean_wallet:/wallet/wallet.keys");
await RunProcessAsync("docker",
$"cp \"{Path.Combine(fullWalletDir, "password")}\" lethean_wallet:/wallet/password");
await RunProcessAsync("docker",
"exec lethean_wallet chown lethean:lethean /wallet/wallet /wallet/wallet.keys /wallet/password");
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to copy wallet files to the Lethean directory.");
}
}
static async Task RunProcessAsync(string fileName, string args)
{
var psi = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using var process = Process.Start(psi)!;
await process.WaitForExitAsync();
if (process.ExitCode != 0)
{
throw new Exception(await process.StandardError.ReadToEndAsync());
}
}
private static async Task RemoveWalletFromLocalDocker()
{
try
{
var removeWalletFromDocker = new ProcessStartInfo
{
FileName = "docker",
Arguments = "exec lethean_wallet sh -c \"rm -rf /wallet/*\"",
RedirectStandardOutput = true,
RedirectStandardError = true
};
using var process = Process.Start(removeWalletFromDocker);
if (process is null)
{
Logger.LogWarning("Failed to start docker process for wallet cleanup.");
return;
}
var stdout = await process.StandardOutput.ReadToEndAsync();
var stderr = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
if (!string.IsNullOrWhiteSpace(stdout))
{
Logger.LogInformation("Docker wallet cleanup output: {Output}", stdout);
}
if (!string.IsNullOrWhiteSpace(stderr))
{
Logger.LogWarning("Docker wallet cleanup error output: {Error}", stderr);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Wallet cleanup via Docker failed.");
}
}
private static void DeleteWalletInContainer()
{
try
{
var walletFile = Path.Combine(ContainerWalletDir, "wallet");
var keysFile = walletFile + ".keys";
var passwordFile = Path.Combine(ContainerWalletDir, "password");
if (File.Exists(walletFile))
{
File.Delete(walletFile);
}
if (File.Exists(keysFile))
{
File.Delete(keysFile);
}
if (File.Exists(passwordFile))
{
File.Delete(passwordFile);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Failed to delete wallet files in directory {Dir}", ContainerWalletDir);
}
}
}