From 998da4288f59718e6e311bf86ce550452585a256 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sun, 24 May 2026 08:42:39 +0800 Subject: [PATCH] test: stabilize stress test runner --- Makefile | 6 +- README.md | 3 +- docs/CHANGELOG.md | 2 + docs/Development-Guide.md | 1 + docs/QUICKREF.md | 1 + tests/test_stress.sh | 148 ++++++++++++++++++++++++++++++-------- 6 files changed, 128 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 8848517..af14d9d 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ MANDIR ?= $(PREFIX)/share/man SYSTEMD_UNIT_DIR ?= $(PREFIX)/lib/systemd/system CI_TEST_PORT ?= $(if $(PORT),$(PORT),2222) -.PHONY: all clean install install-systemd uninstall uninstall-systemd debug release release-check release-check-strict asan valgrind check test test-advisory ci-test unit-test integration-test anonymous-access-test connection-limit-test security-test info +.PHONY: all clean install install-systemd uninstall uninstall-systemd debug release release-check release-check-strict asan valgrind check test test-advisory ci-test unit-test integration-test anonymous-access-test connection-limit-test security-test stress-test info all: $(TARGET) @@ -125,6 +125,10 @@ security-test: all @echo "Running security feature tests..." @cd tests && PORT=$${PORT:-13600} ./test_security_features.sh +stress-test: all + @echo "Running stress tests..." + @cd tests && PORT=$${PORT:-2222} ./test_stress.sh $${CLIENTS:-10} $${DURATION:-30} + ci-test: @$(MAKE) test PORT=$(CI_TEST_PORT) @$(MAKE) anonymous-access-test PORT=$$(($(CI_TEST_PORT) + 5)) diff --git a/README.md b/README.md index 002fe26..2db2d04 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,7 @@ make test-advisory # run integration tests as advisory checks make anonymous-access-test # verify default anonymous login behavior make connection-limit-test # verify per-IP concurrency and rate limits make security-test # run security feature checks +make stress-test # run configurable concurrent-client stress test make ci-test # run the same checks as GitHub Actions # Individual tests @@ -220,7 +221,7 @@ cd tests - Basic functionality: 3 tests - Anonymous access: 2 tests - Security features: 12 tests -- Stress test: concurrent connections +- Stress test: configurable concurrent clients (`CLIENTS=20 DURATION=60 make stress-test`) ### Dependencies diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d4379bf..a034c30 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -56,6 +56,8 @@ - Anonymous access checks now use isolated state, wait for real SSH health, avoid external `timeout` helpers, and run through `make anonymous-access-test` as part of `make ci-test`. +- Stress testing now uses isolated state, waits for real SSH health, avoids + external `timeout` helpers, and is available through `make stress-test`. - Refreshed README and quick-reference module maps to match the current `cli_text`, `help_text`, `support_text`, i18n, exec, and rate-limit modules. - NORMAL mode now opens at the latest visible messages instead of the oldest diff --git a/docs/Development-Guide.md b/docs/Development-Guide.md index bc01312..0b90d05 100644 --- a/docs/Development-Guide.md +++ b/docs/Development-Guide.md @@ -164,6 +164,7 @@ make test-advisory # Run integration tests as advisory checks make anonymous-access-test # Verify default anonymous login behavior make connection-limit-test # Verify per-IP concurrency and rate limits make security-test # Run security feature checks +make stress-test # Run configurable concurrent-client stress test make ci-test # Run the same checks as GitHub Actions # Individual tests diff --git a/docs/QUICKREF.md b/docs/QUICKREF.md index a593a85..4a821e1 100644 --- a/docs/QUICKREF.md +++ b/docs/QUICKREF.md @@ -14,6 +14,7 @@ TEST make anonymous-access-test default anonymous login checks make connection-limit-test per-IP concurrency/rate-limit checks make security-test security feature checks + make stress-test concurrent-client stress test make ci-test same checks as GitHub Actions DEBUG diff --git a/tests/test_stress.sh b/tests/test_stress.sh index 49ad5b9..6ccf4c3 100755 --- a/tests/test_stress.sh +++ b/tests/test_stress.sh @@ -1,56 +1,142 @@ #!/bin/sh -# Stress test for TNT server -# Usage: ./test_stress.sh [num_clients] +# Lightweight concurrent-client stress test for TNT. +# Usage: ./test_stress.sh [num_clients] [duration_seconds] PORT=${PORT:-2222} CLIENTS=${1:-10} DURATION=${2:-30} BIN="../tnt" +PASS=0 +FAIL=0 +STATE_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tnt-stress-test.XXXXXX") +SERVER_PID="" +CLIENT_PIDS="" + +cleanup() { + for pid in $CLIENT_PIDS; do + kill "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + done + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" 2>/dev/null || true + wait "$SERVER_PID" 2>/dev/null || true + fi + rm -rf "$STATE_DIR" +} + +trap cleanup EXIT if [ ! -f "$BIN" ]; then - echo "Error: Binary $BIN not found." + echo "Error: Binary $BIN not found. Run make first." exit 1 fi -# Detect timeout command -TIMEOUT_CMD="timeout" -if command -v gtimeout >/dev/null 2>&1; then - TIMEOUT_CMD="gtimeout" +case "$CLIENTS" in + ''|*[!0-9]*) + echo "Error: num_clients must be a positive integer" + exit 2 + ;; +esac + +case "$DURATION" in + ''|*[!0-9]*) + echo "Error: duration_seconds must be a positive integer" + exit 2 + ;; +esac + +if [ "$CLIENTS" -lt 1 ] || [ "$DURATION" -lt 1 ]; then + echo "Error: num_clients and duration_seconds must be positive" + exit 2 fi -echo "Starting TNT server on port $PORT..." -TNT_RATE_LIMIT=0 TNT_MAX_CONN_PER_IP=$CLIENTS $BIN -p $PORT & +SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes -p $PORT" + +wait_for_health() { + out="" + for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do + if [ -n "$SERVER_PID" ] && ! kill -0 "$SERVER_PID" 2>/dev/null; then + return 1 + fi + out=$(ssh -n $SSH_OPTS localhost health 2>/dev/null || true) + [ "$out" = "ok" ] && return 0 + sleep 1 + done + return 1 +} + +echo "=== TNT Stress Test ===" +echo "clients=$CLIENTS duration=${DURATION}s port=$PORT" + +MAX_CONN_PER_IP=$((CLIENTS + 5)) +TNT_LANG=zh TNT_RATE_LIMIT=0 TNT_MAX_CONN_PER_IP=$MAX_CONN_PER_IP \ + "$BIN" -p "$PORT" -d "$STATE_DIR" >"$STATE_DIR/server.log" 2>&1 & SERVER_PID=$! -sleep 2 -if ! kill -0 $SERVER_PID 2>/dev/null; then - echo "Server failed to start" +if wait_for_health; then + echo "✓ server started" + PASS=$((PASS + 1)) +else + echo "✗ server failed to start" + sed -n '1,120p' "$STATE_DIR/server.log" exit 1 fi -echo "Spawning $CLIENTS clients for ${DURATION}s..." +for i in $(seq 1 "$CLIENTS"); do + script="$STATE_DIR/client-$i.expect" + log="$STATE_DIR/client-$i.log" + ready="$STATE_DIR/client-$i.ready" -for i in $(seq 1 $CLIENTS); do - ( - sleep $((i % 5)) - echo "test user $i" | $TIMEOUT_CMD $DURATION ssh -o StrictHostKeyChecking=no \ - -o UserKnownHostsFile=/dev/null -p $PORT localhost \ - >/dev/null 2>&1 - ) & + cat >"$script" <"$log" 2>&1 & + CLIENT_PIDS="$CLIENT_PIDS $!" done -echo "Running stress test..." -sleep $DURATION +ready_count=0 +for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do + ready_count=$(find "$STATE_DIR" -name 'client-*.ready' -type f | wc -l | tr -d ' ') + [ "$ready_count" = "$CLIENTS" ] && break + if ! kill -0 "$SERVER_PID" 2>/dev/null; then + break + fi + sleep 1 +done -echo "Cleaning up..." -kill $SERVER_PID 2>/dev/null -wait - -echo "Stress test complete" -if kill -0 $SERVER_PID 2>/dev/null; then - echo "WARNING: tnt process still running" +if [ "$ready_count" = "$CLIENTS" ]; then + echo "✓ all clients reached chat" + PASS=$((PASS + 1)) else - echo "Server shutdown confirmed." + echo "✗ only $ready_count/$CLIENTS clients reached chat" + sed -n '1,160p' "$STATE_DIR/server.log" + FAIL=$((FAIL + 1)) fi -exit 0 +for pid in $CLIENT_PIDS; do + wait "$pid" 2>/dev/null || FAIL=$((FAIL + 1)) +done +CLIENT_PIDS="" + +if kill -0 "$SERVER_PID" 2>/dev/null; then + echo "✓ server survived concurrent clients" + PASS=$((PASS + 1)) +else + echo "✗ server exited during stress test" + sed -n '1,160p' "$STATE_DIR/server.log" + FAIL=$((FAIL + 1)) +fi + +echo "" +echo "PASSED: $PASS" +echo "FAILED: $FAIL" +[ "$FAIL" -eq 0 ] && echo "All tests passed" || echo "Some tests failed" +exit "$FAIL"