Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 132 additions & 36 deletions fullstack-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,87 @@ GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

run_clean_output() {
FORCE_COLOR=1 \
CLICOLOR_FORCE=1 \
TERM=xterm-256color \
CARGO_TERM_COLOR=always \
"$@" 2>&1 | sed -u 's/\r/\n/g'
return ${PIPESTATUS[0]}
}

is_truthy() {
case "$1" in
[Yy]|[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
return 0
;;
*)
return 1
;;
esac
}

has_ancestor_flag() {
local needle=$1
local pid=$$
local ppid
local cmd
local depth=0

while [ -n "$pid" ] && [ "$pid" -ne 1 ] && [ "$depth" -lt 20 ]; do
cmd="$(ps -p "$pid" -o args= -ww 2>/dev/null | tr '\n' ' ')"
if echo "$cmd" | grep -q -- "$needle"; then
return 0
fi

ppid="$(ps -p "$pid" -o ppid= 2>/dev/null | tr -d ' ')"
if [ -z "$ppid" ] || [ "$ppid" = "$pid" ]; then
break
fi
pid="$ppid"
depth=$((depth + 1))
done

return 1
}

has_anchor_cli_skip_local_validator() {
if pgrep -af "anchor" 2>/dev/null | awk '($0 ~ /test/) && ($0 ~ /--skip-local-validator/) { found=1; exit } END { exit (found?0:1) }'; then

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit skip-local-validator auto-detection to this process tree

has_anchor_cli_skip_local_validator() scans all system processes with pgrep -af "anchor" and sets SKIP_LOCAL_VALIDATOR if any matching command includes test and --skip-local-validator, even if that command is unrelated to this run. In shared dev/CI hosts, an unrelated Anchor test can therefore force this script into skip mode, and the later skip-local-validator branch will fail if no validator is already on port 8899 instead of starting one.

Useful? React with 👍 / 👎.

return 0
fi
return 1
}

airdrop_upgrade_authority() {
local cluster_url=$1
local keypair=${2:-"${HOME}/.config/solana/id.json"}
local authority

if [ ! -f "$keypair" ]; then
echo -e "${RED}Error: Upgrade authority keypair not found at ${keypair}${NC}"
return 1
fi

authority="$(solana address -k "$keypair")"
echo -e "${YELLOW}Airdropping 100 SOL to upgrade authority (${authority}) on ${cluster_url}...${NC}"
run_clean_output solana airdrop 100 "$authority" --url "$cluster_url" --keypair "$keypair"
}

MB_VALIDATOR_STARTED_BY_US=false
EPHEMERAL_VALIDATOR_STARTED_BY_US=false

SKIP_LOCAL_VALIDATOR=false
for arg in "$@"; do
if [ "$arg" = "--skip-local-validator" ]; then
SKIP_LOCAL_VALIDATOR=true
break
fi
done

if is_truthy "${SKIP_LOCAL_VALIDATOR}" || is_truthy "${ANCHOR_SKIP_LOCAL_VALIDATOR}" || has_ancestor_flag "--skip-local-validator" || has_anchor_cli_skip_local_validator; then
SKIP_LOCAL_VALIDATOR=true
fi

# Check if a port is in use
check_port() {
local port=$1
Expand Down Expand Up @@ -40,31 +118,35 @@ cleanup() {
if [ "$CLUSTER" = "localnet" ]; then
# Only cleanup validators if we started them
if [ "$MB_VALIDATOR_STARTED_BY_US" = true ] || [ "$EPHEMERAL_VALIDATOR_STARTED_BY_US" = true ]; then
echo -ne "\n[SETUP] ${YELLOW}Cleaning up validators...${NC}\r"
if [ "$MB_VALIDATOR_STARTED_BY_US" = true ] && [ ! -z "$MB_VALIDATOR_PID" ]; then
kill $MB_VALIDATOR_PID 2>/dev/null || true
fi
if [ "$EPHEMERAL_VALIDATOR_STARTED_BY_US" = true ] && [ ! -z "$EPHEMERAL_VALIDATOR_PID" ]; then
kill $EPHEMERAL_VALIDATOR_PID 2>/dev/null || true
fi
# Kill any remaining validator processes (only if we started them)
if [ "$MB_VALIDATOR_STARTED_BY_US" = true ]; then
pkill -f "solana-test-validator" 2>/dev/null || true
pkill -f "mb-test-validator" 2>/dev/null || true
fi
if [ "$EPHEMERAL_VALIDATOR_STARTED_BY_US" = true ]; then
pkill -f "ephemeral-validator" 2>/dev/null || true
echo -e "\n[SETUP] ${YELLOW}Cleaning up validators...${NC}"
if ! is_truthy "$SKIP_LOCAL_VALIDATOR"; then

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clean up validators started by this script in skip mode

When SKIP_LOCAL_VALIDATOR is true, cleanup() skips all process termination under if ! is_truthy "$SKIP_LOCAL_VALIDATOR", but the script can still start ephemeral-validator itself earlier (when port 7799 is free). In that flow, the process is left running after tests, so later runs may attach to stale state on port 7799 instead of a fresh reset, which can corrupt test reliability and cause hard-to-reproduce failures.

Useful? React with 👍 / 👎.

if [ "$MB_VALIDATOR_STARTED_BY_US" = true ] && [ ! -z "$MB_VALIDATOR_PID" ]; then
kill $MB_VALIDATOR_PID 2>/dev/null || true
fi
if [ "$EPHEMERAL_VALIDATOR_STARTED_BY_US" = true ] && [ ! -z "$EPHEMERAL_VALIDATOR_PID" ]; then
kill $EPHEMERAL_VALIDATOR_PID 2>/dev/null || true
fi
# Kill any remaining validator processes (only if we started them)
if [ "$MB_VALIDATOR_STARTED_BY_US" = true ]; then
pkill -f "solana-test-validator" 2>/dev/null || true
pkill -f "mb-test-validator" 2>/dev/null || true
fi
if [ "$EPHEMERAL_VALIDATOR_STARTED_BY_US" = true ]; then
pkill -f "ephemeral-validator" 2>/dev/null || true
fi
else
echo -e "\n[SETUP] ${GREEN}Skip-local-validator mode: leaving validator processes untouched...${NC}"
fi
# Clean up test ledger directories (only if we started the validators)
if [ "$MB_VALIDATOR_STARTED_BY_US" = true ]; then
echo -ne "${YELLOW}Cleaning up test ledger directories...${NC}\r"
if [ "$MB_VALIDATOR_STARTED_BY_US" = true ] && ! is_truthy "$SKIP_LOCAL_VALIDATOR"; then
echo -e "${YELLOW}Cleaning up test ledger directories...${NC}"
rm -rf test-ledger 2>/dev/null || true
rm -rf test-ledger-magicblock 2>/dev/null || true
fi
if [ "$EPHEMERAL_VALIDATOR_STARTED_BY_US" = true ]; then
rm -rf magicblock-test-storage 2>/dev/null || true
fi
echo -e "\033[2K${GREEN}Cleanup complete${NC}"
echo -e "${GREEN}Cleanup complete${NC}"
else
echo -e "\n[SETUP] ${GREEN}Validators were already running, leaving them running...${NC}"
fi
Expand All @@ -76,11 +158,13 @@ trap cleanup EXIT INT TERM

if [ "$CLUSTER" = "localnet" ]; then
# Check if anchor started its own validator (port 8899 occupied but not by mb-test-validator)
if check_port 8899 && ! pgrep -f "mb-test-validator" > /dev/null 2>&1; then
echo -e "${YELLOW}Non-MagicBlock validator detected on port 8899, killing it...${NC}"
echo -e "${YELLOW}Tip: run with 'anchor test --skip-local-validator --skip-build --skip-deploy' to avoid this${NC}"
lsof -ti :8899 | xargs kill 2>/dev/null
sleep 1
if ! is_truthy "$SKIP_LOCAL_VALIDATOR"; then
if check_port 8899 && ! pgrep -f "mb-test-validator" > /dev/null 2>&1; then
echo -e "${YELLOW}Non-MagicBlock validator detected on port 8899, killing it...${NC}"
echo -e "${YELLOW}Tip: run with 'anchor test --skip-local-validator --skip-build --skip-deploy' to avoid this${NC}"
lsof -ti :8899 | xargs kill 2>/dev/null
sleep 1
fi
fi

# Check if ephemeral-validator is installed
Expand All @@ -94,26 +178,35 @@ if [ "$CLUSTER" = "localnet" ]; then
solana config set --url localhost 2>/dev/null

# Check if mb-test-validator (Solana validator) is already running on port 8899
if check_port 8899; then
if is_truthy "$SKIP_LOCAL_VALIDATOR"; then
if check_port 8899; then
echo -e "[SETUP] ${GREEN}skip-local-validator enabled, using existing validator on port 8899...${NC}"
MB_VALIDATOR_PID=$(lsof -ti :8899 | head -1)
else
echo -e "[SETUP] ${YELLOW}skip-local-validator is enabled, but no validator detected on port 8899...${NC}"
echo -e "${RED}Unable to run with --skip-local-validator: no validator available on port 8899.${NC}"
exit 1
fi
elif check_port 8899; then
echo -e "[SETUP] ${GREEN}Solana validator is already running on port 8899, skipping startup...${NC}"
MB_VALIDATOR_STARTED_BY_US=false
# Try to get the PID of the running validator
MB_VALIDATOR_PID=$(lsof -ti :8899 | head -1)
else
echo -ne "[SETUP] ${GREEN}Starting mb-test-validator...${NC}\r"
echo -e "[SETUP] ${GREEN}Starting mb-test-validator...${NC}"
mb-test-validator --reset > /tmp/mb-test-validator.log 2>&1 &
MB_VALIDATOR_PID=$!
MB_VALIDATOR_STARTED_BY_US=true

# Wait for solana-test-validator to be ready
echo -ne "${YELLOW}Waiting for solana-test-validator to be ready...${NC}\r"
echo -e "${YELLOW}Waiting for solana-test-validator to be ready...${NC}"
for i in {1..60}; do
if curl -s http://127.0.0.1:8899/health > /dev/null 2>&1; then
echo -e "\033[2K${GREEN}solana-test-validator is ready${NC}"
echo -e "${GREEN}solana-test-validator is ready${NC}"
break
fi
if [ $i -eq 60 ]; then
echo -e "\033[2K${RED}Error: solana-test-validator failed to start${NC}"
echo -e "${RED}Error: solana-test-validator failed to start${NC}"
echo "Check logs at /tmp/mb-test-validator.log"
exit 1
fi
Expand All @@ -129,7 +222,7 @@ if [ "$CLUSTER" = "localnet" ]; then
EPHEMERAL_VALIDATOR_PID=$(lsof -ti :7799 | head -1)
else
# Start ephemeral-validator
echo -ne "[SETUP] ${GREEN}Starting ephemeral-validator...${NC}\r"
echo -e "[SETUP] ${GREEN}Starting ephemeral-validator...${NC}"
RUST_LOG=info ephemeral-validator \
--remotes "http://127.0.0.1:8899" \
--remotes "ws://127.0.0.1:8900" \
Expand All @@ -140,14 +233,14 @@ if [ "$CLUSTER" = "localnet" ]; then
EPHEMERAL_VALIDATOR_STARTED_BY_US=true

# Wait for ephemeral-validator to be ready
echo -ne "${YELLOW}Waiting for ephemeral-validator to be ready...${NC}\r"
echo -e "${YELLOW}Waiting for ephemeral-validator to be ready...${NC}"
for i in {1..60}; do
if curl -s http://127.0.0.1:7799/health > /dev/null 2>&1; then
echo -e "\033[2K${GREEN}ephemeral-validator is ready${NC}"
echo -e "${GREEN}ephemeral-validator is ready${NC}"
break
fi
if [ $i -eq 60 ]; then
echo -e "\033[2K${RED}Error: ephemeral-validator failed to start${NC}"
echo -e "${RED}Error: ephemeral-validator failed to start${NC}"
echo "Check logs at /tmp/ephemeral-validator.log"
exit 1
fi
Expand All @@ -159,10 +252,14 @@ if [ "$CLUSTER" = "localnet" ]; then
export EPHEMERAL_WS_ENDPOINT=ws://localhost:7800
export ANCHOR_WALLET="${HOME}/.config/solana/id.json"
export ANCHOR_PROVIDER_URL="http://127.0.0.1:8899"

airdrop_upgrade_authority "$ANCHOR_PROVIDER_URL" "$ANCHOR_WALLET"

echo -e "${GREEN}Running anchor test...${NC}"
anchor build && anchor deploy --provider.cluster localnet

yarn ts-mocha -p ./tsconfig.json -t 1000000 --exit tests/**/*.ts --provider.cluster localnet --skip-local-validator --skip-build --skip-deploy
run_clean_output anchor build
run_clean_output anchor deploy --provider.cluster localnet

run_clean_output yarn ts-mocha --colors -p ./tsconfig.json -t 1000000 --exit tests/**/*.ts --provider.cluster localnet --skip-local-validator --skip-build --skip-deploy
TEST_EXIT_CODE=$?

if [ $TEST_EXIT_CODE -ne 0 ]; then
Expand Down Expand Up @@ -192,7 +289,7 @@ else
;;
esac

yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts
run_clean_output yarn run ts-mocha --colors -p ./tsconfig.json -t 1000000 tests/**/*.ts
TEST_EXIT_CODE=$?

if [ $TEST_EXIT_CODE -ne 0 ]; then
Expand All @@ -201,4 +298,3 @@ else
fi
echo -e "${GREEN}Tests completed successfully${NC}"
fi

Loading