mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-03 12:16:12 +01:00
Nutshell wallet (#695)
* chore: Add nutshell wallet integration test script feat: Add MINT_URL configuration for docker container in nutshell wallet integration test script chore: Make nutshell_wallet_itest.sh executable fix: Update MINT_URL to use host.docker.internal for Docker container access feat: Add Docker container startup for Cashu daemon in wallet integration test script chore: Update Docker image to use Docker Hub repository feat: Add cleanup trap to stop Docker container and unset variables feat: Add initial test stub for nutshell wallet mint functionality test: Add Cashu wallet mint integration test feat: Add just command for nutshell wallet integration test refactor: Organize imports and improve code formatting in nutshell wallet test fix: Update Cashu Docker image and test URL for correct container access fix: Update Docker container name and image for Cashu wallet test script fix: Handle Docker container name conflict in nutshell wallet integration test fix: Update Docker image to cashubtc/nutshell:latest in wallet integration test script feat: Add support for running separate Nutshell mint and wallet containers feat: Update Nutshell mint and wallet container configurations for integration testing fix: Update wallet port and container configuration in integration test script chore: Export MINT_URL and WALLET_URL as environment variables fix: Update invoice creation and state checking in nutshell wallet test fix: Update MINT_URL to use host.docker.internal for container access fix: Update nutshell wallet mint test to handle invoice payment state refactor: Improve Nutshell wallet test with better error handling and robustness refactor: Improve code formatting and logging in nutshell wallet test refactor: Replace anyhow with expect for error handling in Nutshell wallet test refactor: Simplify error handling in Nutshell wallet mint test refactor: Replace `?` with `expect()` in Nutshell wallet test refactor: Simplify nutshell wallet test by removing unused code and improving error handling refactor: Extract minting and balance helper functions in nutshell wallet test feat: Add test for Nutshell wallet token swap functionality fix: Trim quotes from token in nutshell wallet swap test refactor: Remove debug print statements and improve code readability refactor: Improve test_nutshell_wallet_melt with payment state checking and balance verification refactor: Update Nutshell wallet integration test script configuration feat: Extract common mint startup function into shared script refactor: Simplify nutshell wallet integration test script and improve startup process feat: Add mintd process termination and test status capture in integration test script refactor: Capitalize env vars and ensure comprehensive cleanup in trap feat: nutshell wallet test * ci: Add test step for Nutshell wallet integration tests * ci: Split Nutshell integration tests into separate jobs * feat: nutshell wallet test * ci: Add Docker setup and increase timeout for Nutshell wallet integration tests * chore: Improve Nutshell wallet integration test script robustness * fix: Remove -i flag from Nix develop and improve Docker accessibility check * fix: payment processor * fix: wallet tests * feat: Add integration shell with Docker and Rust stable for testing * ci: Simplify Nutshell wallet integration test workflow and script * fix: Improve mintd endpoint detection and error handling in integration test script * fix: wallet tests
This commit is contained in:
26
.github/workflows/nutshell_itest.yml
vendored
26
.github/workflows/nutshell_itest.yml
vendored
@@ -3,7 +3,8 @@ name: Nutshell integration
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
nutshell-integration-tests:
|
||||
name: Nutshell Mint Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Pull and start mint
|
||||
@@ -19,8 +20,29 @@ jobs:
|
||||
uses: DeterminateSystems/magic-nix-cache-action@v6
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Test
|
||||
- name: Test Nutshell
|
||||
run: nix develop -i -L .#stable --command just test-nutshell
|
||||
- name: Show logs if tests fail
|
||||
if: failure()
|
||||
run: docker logs nutshell
|
||||
|
||||
nutshell-wallet-integration-tests:
|
||||
name: Nutshell Wallet Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Pull Nutshell Docker image
|
||||
run: docker pull cashubtc/nutshell:latest
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@v11
|
||||
- name: Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@v6
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Test Nutshell Wallet
|
||||
run: |
|
||||
nix develop -i -L .#integration --command just nutshell-wallet-itest
|
||||
- name: Show Docker logs if tests fail
|
||||
if: failure()
|
||||
run: docker logs nutshell-wallet || true
|
||||
|
||||
251
crates/cdk-integration-tests/tests/nutshell_wallet.rs
Normal file
251
crates/cdk-integration-tests/tests/nutshell_wallet.rs
Normal file
@@ -0,0 +1,251 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use cdk_fake_wallet::create_fake_invoice;
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tokio::time::sleep;
|
||||
|
||||
/// Response from the invoice creation endpoint
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct InvoiceResponse {
|
||||
payment_request: String,
|
||||
checking_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Maximum number of attempts to check invoice payment status
|
||||
const MAX_PAYMENT_CHECK_ATTEMPTS: u8 = 20;
|
||||
/// Delay between payment status checks in milliseconds
|
||||
const PAYMENT_CHECK_DELAY_MS: u64 = 500;
|
||||
/// Default test amount in satoshis
|
||||
const DEFAULT_TEST_AMOUNT: u64 = 10000;
|
||||
|
||||
/// Helper function to mint tokens via Lightning invoice
|
||||
async fn mint_tokens(base_url: &str, amount: u64) -> String {
|
||||
let client = Client::new();
|
||||
|
||||
// Create an invoice for the specified amount
|
||||
let invoice_url = format!("{}/lightning/create_invoice?amount={}", base_url, amount);
|
||||
|
||||
let invoice_response = client
|
||||
.post(&invoice_url)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send invoice creation request")
|
||||
.json::<InvoiceResponse>()
|
||||
.await
|
||||
.expect("Failed to parse invoice response");
|
||||
|
||||
println!("Created invoice: {}", invoice_response.payment_request);
|
||||
|
||||
invoice_response.payment_request
|
||||
}
|
||||
|
||||
/// Helper function to wait for payment confirmation
|
||||
async fn wait_for_payment_confirmation(base_url: &str, payment_request: &str) {
|
||||
let client = Client::new();
|
||||
let check_url = format!(
|
||||
"{}/lightning/invoice_state?payment_request={}",
|
||||
base_url, payment_request
|
||||
);
|
||||
|
||||
let mut payment_confirmed = false;
|
||||
|
||||
for attempt in 1..=MAX_PAYMENT_CHECK_ATTEMPTS {
|
||||
println!(
|
||||
"Checking invoice state (attempt {}/{})...",
|
||||
attempt, MAX_PAYMENT_CHECK_ATTEMPTS
|
||||
);
|
||||
|
||||
let response = client
|
||||
.get(&check_url)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send payment check request");
|
||||
|
||||
if response.status().is_success() {
|
||||
let state: Value = response
|
||||
.json()
|
||||
.await
|
||||
.expect("Failed to parse payment state response");
|
||||
println!("Payment state: {:?}", state);
|
||||
|
||||
if let Some(result) = state.get("result") {
|
||||
if result == 1 {
|
||||
payment_confirmed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Failed to check payment state: {}", response.status());
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(PAYMENT_CHECK_DELAY_MS)).await;
|
||||
}
|
||||
|
||||
if !payment_confirmed {
|
||||
panic!("Payment not confirmed after maximum attempts");
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to get the current wallet balance
|
||||
async fn get_wallet_balance(base_url: &str) -> u64 {
|
||||
let client = Client::new();
|
||||
let balance_url = format!("{}/balance", base_url);
|
||||
|
||||
let balance_response = client
|
||||
.get(&balance_url)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send balance request");
|
||||
|
||||
let balance: Value = balance_response
|
||||
.json()
|
||||
.await
|
||||
.expect("Failed to parse balance response");
|
||||
|
||||
println!("Wallet balance: {:?}", balance);
|
||||
|
||||
balance["balance"]
|
||||
.as_u64()
|
||||
.expect("Could not parse balance as u64")
|
||||
}
|
||||
|
||||
/// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
||||
async fn test_nutshell_wallet_mint() {
|
||||
// Get the wallet URL from environment variable
|
||||
let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
|
||||
|
||||
// Step 1: Create an invoice and mint tokens
|
||||
let amount = DEFAULT_TEST_AMOUNT;
|
||||
let payment_request = mint_tokens(&base_url, amount).await;
|
||||
|
||||
// Step 2: Wait for the invoice to be paid
|
||||
wait_for_payment_confirmation(&base_url, &payment_request).await;
|
||||
|
||||
// Step 3: Check the wallet balance
|
||||
let available_balance = get_wallet_balance(&base_url).await;
|
||||
|
||||
// Verify the balance is at least the amount we minted
|
||||
assert!(
|
||||
available_balance >= amount,
|
||||
"Balance should be at least {} but was {}",
|
||||
amount,
|
||||
available_balance
|
||||
);
|
||||
}
|
||||
|
||||
/// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
||||
async fn test_nutshell_wallet_swap() {
|
||||
// Get the wallet URL from environment variable
|
||||
let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
|
||||
|
||||
// Step 1: Create an invoice and mint tokens
|
||||
let amount = DEFAULT_TEST_AMOUNT;
|
||||
let payment_request = mint_tokens(&base_url, amount).await;
|
||||
|
||||
// Step 2: Wait for the invoice to be paid
|
||||
wait_for_payment_confirmation(&base_url, &payment_request).await;
|
||||
|
||||
let send_amount = 100;
|
||||
let send_url = format!("{}/send?amount={}", base_url, send_amount);
|
||||
let client = Client::new();
|
||||
|
||||
let response: Value = client
|
||||
.post(&send_url)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send payment check request")
|
||||
.json()
|
||||
.await
|
||||
.expect("Valid json");
|
||||
|
||||
// Extract the token and remove the surrounding quotes
|
||||
let token_with_quotes = response
|
||||
.get("token")
|
||||
.expect("Missing token")
|
||||
.as_str()
|
||||
.expect("Token is not a string");
|
||||
let token = token_with_quotes.trim_matches('"');
|
||||
|
||||
let receive_url = format!("{}/receive?token={}", base_url, token);
|
||||
|
||||
let response: Value = client
|
||||
.post(&receive_url)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to receive request")
|
||||
.json()
|
||||
.await
|
||||
.expect("Valid json");
|
||||
|
||||
let balance = response
|
||||
.get("balance")
|
||||
.expect("Bal in response")
|
||||
.as_u64()
|
||||
.expect("Valid num");
|
||||
let initial_balance = response
|
||||
.get("initial_balance")
|
||||
.expect("Bal in response")
|
||||
.as_u64()
|
||||
.expect("Valid num");
|
||||
|
||||
let token_received = balance - initial_balance;
|
||||
|
||||
assert_eq!(token_received, send_amount);
|
||||
}
|
||||
|
||||
/// Test the Nutshell wallet's ability to melt tokens to pay a Lightning invoice
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
||||
async fn test_nutshell_wallet_melt() {
|
||||
// Get the wallet URL from environment variable
|
||||
let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
|
||||
|
||||
// Step 1: Create an invoice and mint tokens
|
||||
let amount = DEFAULT_TEST_AMOUNT;
|
||||
let payment_request = mint_tokens(&base_url, amount).await;
|
||||
|
||||
// Step 2: Wait for the invoice to be paid
|
||||
wait_for_payment_confirmation(&base_url, &payment_request).await;
|
||||
|
||||
// Get initial balance
|
||||
let initial_balance = get_wallet_balance(&base_url).await;
|
||||
println!("Initial balance: {}", initial_balance);
|
||||
|
||||
// Step 3: Create a fake invoice to pay
|
||||
let payment_amount = 1000; // 1000 sats
|
||||
let fake_invoice = create_fake_invoice(payment_amount, "Test payment".to_string());
|
||||
let pay_url = format!("{}/lightning/pay_invoice?bolt11={}", base_url, fake_invoice);
|
||||
let client = Client::new();
|
||||
|
||||
// Step 4: Pay the invoice
|
||||
let _response: Value = client
|
||||
.post(&pay_url)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send pay request")
|
||||
.json()
|
||||
.await
|
||||
.expect("Failed to parse pay response");
|
||||
|
||||
let final_balance = get_wallet_balance(&base_url).await;
|
||||
println!("Final balance: {}", final_balance);
|
||||
|
||||
assert!(
|
||||
initial_balance > final_balance,
|
||||
"Balance should decrease after payment"
|
||||
);
|
||||
|
||||
let balance_difference = initial_balance - final_balance;
|
||||
println!("Balance decreased by: {}", balance_difference);
|
||||
|
||||
// The balance difference should be at least the payment amount
|
||||
assert!(
|
||||
balance_difference >= (payment_amount / 1000),
|
||||
"Balance should decrease by at least the payment amount ({}) but decreased by {}",
|
||||
payment_amount,
|
||||
balance_difference
|
||||
);
|
||||
}
|
||||
22
flake.nix
22
flake.nix
@@ -265,9 +265,29 @@
|
||||
inherit nativeBuildInputs;
|
||||
} // envVars);
|
||||
|
||||
# Shell with Docker for integration tests
|
||||
integration = pkgs.mkShell ({
|
||||
shellHook = ''
|
||||
${_shellHook}
|
||||
# Ensure Docker is available
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo "Docker is not installed or not in PATH"
|
||||
echo "Please install Docker to run integration tests"
|
||||
exit 1
|
||||
fi
|
||||
echo "Docker is available at $(which docker)"
|
||||
echo "Docker version: $(docker --version)"
|
||||
'';
|
||||
buildInputs = buildInputs ++ [
|
||||
stable_toolchain
|
||||
pkgs.docker-client
|
||||
];
|
||||
inherit nativeBuildInputs;
|
||||
} // envVars);
|
||||
|
||||
in
|
||||
{
|
||||
inherit msrv stable nightly;
|
||||
inherit msrv stable nightly integration;
|
||||
default = stable;
|
||||
};
|
||||
}
|
||||
|
||||
4
justfile
4
justfile
@@ -109,6 +109,10 @@ fake-auth-mint-itest db openid_discovery:
|
||||
#!/usr/bin/env bash
|
||||
./misc/fake_auth_itests.sh "{{db}}" "{{openid_discovery}}"
|
||||
|
||||
nutshell-wallet-itest:
|
||||
#!/usr/bin/env bash
|
||||
./misc/nutshell_wallet_itest.sh
|
||||
|
||||
run-examples:
|
||||
cargo r --example p2pk
|
||||
cargo r --example mint-token
|
||||
|
||||
161
misc/nutshell_wallet_itest.sh
Executable file
161
misc/nutshell_wallet_itest.sh
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
MINT_PORT=8085
|
||||
WALLET_PORT=4448
|
||||
MINT_CONTAINER_NAME="nutshell-mint"
|
||||
WALLET_CONTAINER_NAME="nutshell-wallet"
|
||||
# Use host.docker.internal for the mint URL so containers can access it
|
||||
MINT_URL="http://0.0.0.0:${MINT_PORT}"
|
||||
WALLET_URL="http://localhost:${WALLET_PORT}"
|
||||
CDK_MINTD_PID=""
|
||||
|
||||
# Function to clean up resources
|
||||
cleanup() {
|
||||
echo "Cleaning up resources..."
|
||||
|
||||
if docker ps -a | grep -q ${WALLET_CONTAINER_NAME}; then
|
||||
echo "Stopping and removing Docker container '${WALLET_CONTAINER_NAME}'..."
|
||||
docker stop ${WALLET_CONTAINER_NAME} >/dev/null 2>&1
|
||||
docker rm ${WALLET_CONTAINER_NAME} >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ -n "$CDK_MINTD_PID" ]; then
|
||||
echo "Stopping mintd process (PID: $CDK_MINTD_PID)..."
|
||||
kill -TERM $CDK_MINTD_PID >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Unset variables
|
||||
unset MINT_URL WALLET_URL MINT_PORT WALLET_PORT MINT_CONTAINER_NAME WALLET_CONTAINER_NAME
|
||||
unset CDK_MINTD_PID CDK_MINTD_URL CDK_MINTD_WORK_DIR CDK_MINTD_LISTEN_HOST CDK_MINTD_LISTEN_PORT
|
||||
unset CDK_MINTD_LN_BACKEND CDK_MINTD_FAKE_WALLET_SUPPORTED_UNITS CDK_MINTD_MNEMONIC
|
||||
unset CDK_MINTD_FAKE_WALLET_FEE_PERCENT CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN CDK_MINTD_DATABASE
|
||||
unset TEST_STATUS
|
||||
echo "Cleanup complete."
|
||||
}
|
||||
|
||||
# Set up trap to call cleanup function on script exit
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
|
||||
|
||||
# Create a temporary directory for mintd
|
||||
CDK_ITESTS=$(mktemp -d)
|
||||
echo "Created temporary directory for mintd: $CDK_ITESTS"
|
||||
|
||||
export CDK_MINTD_URL="$MINT_URL"
|
||||
export CDK_MINTD_WORK_DIR="$CDK_ITESTS"
|
||||
export CDK_MINTD_LISTEN_HOST="127.0.0.1"
|
||||
export CDK_MINTD_LISTEN_PORT="8085"
|
||||
export CDK_MINTD_LN_BACKEND="fakewallet"
|
||||
export CDK_MINTD_FAKE_WALLET_SUPPORTED_UNITS="sat,usd"
|
||||
export CDK_MINTD_MNEMONIC="eye survey guilt napkin crystal cup whisper salt luggage manage unveil loyal"
|
||||
export CDK_MINTD_FAKE_WALLET_FEE_PERCENT="0"
|
||||
export CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN="1"
|
||||
export CDK_MINTD_DATABASE="redb"
|
||||
|
||||
|
||||
echo "Starting fake mintd"
|
||||
cargo run --bin cdk-mintd --features "redb" &
|
||||
CDK_MINTD_PID=$!
|
||||
|
||||
# Wait for the mint to be ready
|
||||
echo "Waiting for mintd to start..."
|
||||
TIMEOUT=300
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# Try different URLs since the endpoint might vary
|
||||
URLS=("http://localhost:${MINT_PORT}/v1/info" "http://127.0.0.1:${MINT_PORT}/v1/info" "http://0.0.0.0:${MINT_PORT}/v1/info")
|
||||
|
||||
# Loop until one of the endpoints returns a 200 OK status or timeout is reached
|
||||
while true; do
|
||||
# Get the current time
|
||||
CURRENT_TIME=$(date +%s)
|
||||
|
||||
# Calculate the elapsed time
|
||||
ELAPSED_TIME=$((CURRENT_TIME - START_TIME))
|
||||
|
||||
# Check if the elapsed time exceeds the timeout
|
||||
if [ $ELAPSED_TIME -ge $TIMEOUT ]; then
|
||||
echo "Timeout of $TIMEOUT seconds reached. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try each URL
|
||||
for URL in "${URLS[@]}"; do
|
||||
# Make a request to the endpoint and capture the HTTP status code
|
||||
HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" "$URL" || echo "000")
|
||||
|
||||
# Check if the HTTP status is 200 OK
|
||||
if [ "$HTTP_STATUS" -eq 200 ]; then
|
||||
echo "Received 200 OK from $URL"
|
||||
MINT_URL=$(echo "$URL" | sed 's|/v1/info||')
|
||||
echo "Setting MINT_URL to $MINT_URL"
|
||||
export MINT_URL
|
||||
break 2 # Break out of both loops
|
||||
else
|
||||
echo "Waiting for 200 OK response from $URL, current status: $HTTP_STATUS"
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait before retrying
|
||||
sleep 5
|
||||
done
|
||||
|
||||
|
||||
|
||||
|
||||
# Check if Docker is available and accessible
|
||||
if docker info > /dev/null 2>&1; then
|
||||
echo "Docker is available, starting Nutshell wallet container"
|
||||
# Use the MINT_URL which is already set to host.docker.internal
|
||||
docker run -d --name ${WALLET_CONTAINER_NAME} \
|
||||
--network=host \
|
||||
-p ${WALLET_PORT}:${WALLET_PORT} \
|
||||
-e MINT_URL=${MINT_URL} \
|
||||
cashubtc/nutshell:latest \
|
||||
poetry run cashu -d
|
||||
else
|
||||
echo "Docker is not accessible, skipping Nutshell wallet container setup"
|
||||
# Set a flag to indicate we're not using Docker
|
||||
export NO_DOCKER=true
|
||||
fi
|
||||
|
||||
# Wait for the mint to be ready
|
||||
echo "Waiting for Nutshell Mint to start..."
|
||||
sleep 5
|
||||
|
||||
# Check if the Mint API is responding (use localhost for local curl check)
|
||||
echo "Checking if Nutshell Mint API is available..."
|
||||
if curl -s "http://localhost:${MINT_PORT}/v1/info" > /dev/null; then
|
||||
echo "Nutshell Mint is running and accessible at ${MINT_URL}"
|
||||
else
|
||||
echo "Warning: Nutshell Mint API is not responding. It might not be ready yet."
|
||||
fi
|
||||
|
||||
# Only check wallet if Docker is available
|
||||
if [ -z "$NO_DOCKER" ]; then
|
||||
# Check if the Wallet API is responding
|
||||
echo "Checking if Nutshell Wallet API is available..."
|
||||
if curl -s "${WALLET_URL}/info" > /dev/null; then
|
||||
echo "Nutshell Wallet is running in container '${WALLET_CONTAINER_NAME}'"
|
||||
echo "You can access it at ${WALLET_URL}"
|
||||
else
|
||||
echo "Warning: Nutshell Wallet API is not responding. The container might not be ready yet."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Export URLs as environment variables
|
||||
export MINT_URL=${MINT_URL}
|
||||
export WALLET_URL=${WALLET_URL}
|
||||
|
||||
# Run the integration test
|
||||
echo "Running integration test..."
|
||||
cargo test -p cdk-integration-tests --tests nutshell_wallet
|
||||
TEST_STATUS=$?
|
||||
|
||||
# Exit with the test status
|
||||
echo "Integration test completed with status: $TEST_STATUS"
|
||||
exit $TEST_STATUS
|
||||
Reference in New Issue
Block a user