- Fix #52: Watchdog proxy restart on single URL failure: * Validate failure against secondary independent probe URL before declaring proxy down * Skip restart when direct WAN is also degraded or down (!direct_ok) * Implement 120s cooldown between automated proxy restarts * Fix Telegram notification to accurately state 'Перезапуск sing-box' instead of 'Перезапуск роутера' - Fix #51: Telegram bot spamming WAN up/down notifications: * Filter hotplug events strictly by badwan_monitored_interfaces (defaults to wan) * Add atomic directory lock with debounce delay and real ubus state query * Prevent repeated notifications when interface state has not transitioned - Feat: 2-VPN deadlock elimination & self-download subscription proxy: * Isolate unbootstrapped dependent subscription sections on initial boot (deferred_sections) * Allow self-download proxy target for subscriptions with explicit UI hint '(текущее правило при активности)' * Enable automatic local proxy fallback for subscription refresh when direct WAN download fails while sing-box is active * Add regression test in tests/subscription_bootstrap_deadlock.sh - Fix: Parental control quota enforcement: * Correct route rule filtering when daily quota minutes are exhausted without a fixed time interval - i18n & Version bump: * Bump fe-app-tachyon to 1.3.26 and update Russian PO/POT translations
182 lines
5.8 KiB
Bash
182 lines
5.8 KiB
Bash
#!/usr/bin/env bash
|
|
set -eo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)"
|
|
MONITOR_SCRIPT="$ROOT_DIR/tachyon/files/etc/hotplug.d/iface/99-tachyon-wan-monitor"
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
[ -f "$MONITOR_SCRIPT" ] || fail "99-tachyon-wan-monitor not found"
|
|
|
|
TMP_DIR="$(mktemp -d /tmp/tachyon_wan_monitor_test.XXXXXX)"
|
|
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
|
|
BIN_DIR="$TMP_DIR/bin"
|
|
VAR_RUN="$TMP_DIR/var_run"
|
|
mkdir -p "$BIN_DIR" "$VAR_RUN"
|
|
|
|
# Mock uci
|
|
cat > "$BIN_DIR/uci" << 'EOF'
|
|
#!/bin/sh
|
|
cmd="$1"
|
|
shift
|
|
case "$cmd" in
|
|
-q)
|
|
subcmd="$1"
|
|
shift
|
|
if [ "$subcmd" = "get" ]; then
|
|
key="$1"
|
|
case "$key" in
|
|
tachyon.settings.enable_badwan_interface_monitoring)
|
|
cat "${TEST_UCI_DIR}/enable_badwan" 2>/dev/null || echo "0"
|
|
;;
|
|
tachyon.telegram.enabled)
|
|
cat "${TEST_UCI_DIR}/telegram_enabled" 2>/dev/null || echo "0"
|
|
;;
|
|
tachyon.settings.badwan_monitored_interfaces)
|
|
cat "${TEST_UCI_DIR}/monitored_ifaces" 2>/dev/null || echo ""
|
|
;;
|
|
tachyon.settings.badwan_reload_delay)
|
|
cat "${TEST_UCI_DIR}/reload_delay" 2>/dev/null || echo "50"
|
|
;;
|
|
*)
|
|
echo ""
|
|
;;
|
|
esac
|
|
fi
|
|
;;
|
|
esac
|
|
EOF
|
|
chmod +x "$BIN_DIR/uci"
|
|
|
|
# Mock ubus
|
|
cat > "$BIN_DIR/ubus" << 'EOF'
|
|
#!/bin/sh
|
|
# Check if call network.interface.<iface> status
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
network.interface.*)
|
|
iface="${arg#network.interface.}"
|
|
state="$(cat "${TEST_UCI_DIR}/ubus_state_${iface}" 2>/dev/null || echo "up")"
|
|
if [ "$state" = "up" ]; then
|
|
echo '{"up": true, "pending": false, "available": true}'
|
|
else
|
|
echo '{"up": false, "pending": false, "available": false}'
|
|
fi
|
|
exit 0
|
|
;;
|
|
esac
|
|
done
|
|
echo '{}'
|
|
EOF
|
|
chmod +x "$BIN_DIR/ubus"
|
|
|
|
# Mock logger
|
|
cat > "$BIN_DIR/logger" << 'EOF'
|
|
#!/bin/sh
|
|
echo "$@" >> "${TEST_LOG_FILE}"
|
|
EOF
|
|
chmod +x "$BIN_DIR/logger"
|
|
|
|
# Mock tachyon init.d script
|
|
cat > "$BIN_DIR/initd_tachyon" << 'EOF'
|
|
#!/bin/sh
|
|
exit 0
|
|
EOF
|
|
chmod +x "$BIN_DIR/initd_tachyon"
|
|
|
|
# Mock tachyon cli (for send_telegram_notification)
|
|
cat > "$BIN_DIR/tachyon" << 'EOF'
|
|
#!/bin/sh
|
|
if [ "$1" = "telegram" ]; then
|
|
echo "$@" >> "${TEST_TELEGRAM_LOG}"
|
|
fi
|
|
exit 0
|
|
EOF
|
|
chmod +x "$BIN_DIR/tachyon"
|
|
|
|
export PATH="$BIN_DIR:$PATH"
|
|
export TEST_UCI_DIR="$TMP_DIR/uci"
|
|
export TEST_LOG_FILE="$TMP_DIR/logger.log"
|
|
export TEST_TELEGRAM_LOG="$TMP_DIR/telegram.log"
|
|
mkdir -p "$TEST_UCI_DIR"
|
|
|
|
# Patch monitor script temporarily to use our isolated /var/run/tachyon dir and fast debounce (sleep 0 instead of 2s)
|
|
TEST_MONITOR="$TMP_DIR/monitor_under_test.sh"
|
|
# shellcheck disable=SC2016
|
|
sed -e "s|/var/run/tachyon|${VAR_RUN}|g" \
|
|
-e "s|/usr/bin/tachyon|${BIN_DIR}/tachyon|g" \
|
|
-e "s|/etc/init.d/tachyon|${BIN_DIR}/initd_tachyon|g" \
|
|
-e 's|sleep "\$DELAY_SEC"|sleep 0|g' \
|
|
-e 's|sleep 2|sleep 0|g' \
|
|
"$MONITOR_SCRIPT" > "$TEST_MONITOR"
|
|
chmod +x "$TEST_MONITOR"
|
|
|
|
# Test 1: Monitoring disabled (enable_badwan = 0)
|
|
echo "0" > "$TEST_UCI_DIR/enable_badwan"
|
|
echo "1" > "$TEST_UCI_DIR/telegram_enabled"
|
|
echo "wan" > "$TEST_UCI_DIR/monitored_ifaces"
|
|
echo "down" > "$TEST_UCI_DIR/ubus_state_wan"
|
|
|
|
ACTION="ifdown" INTERFACE="wan" sh "$TEST_MONITOR"
|
|
[ ! -f "$TEST_TELEGRAM_LOG" ] || fail "Test 1 failed: notification sent when enable_badwan=0"
|
|
|
|
# Test 2: Telegram disabled (telegram_enabled = 0)
|
|
echo "1" > "$TEST_UCI_DIR/enable_badwan"
|
|
echo "0" > "$TEST_UCI_DIR/telegram_enabled"
|
|
ACTION="ifdown" INTERFACE="wan" sh "$TEST_MONITOR"
|
|
[ ! -f "$TEST_TELEGRAM_LOG" ] || fail "Test 2 failed: notification sent when telegram.enabled=0"
|
|
|
|
# Test 3: Unmonitored interface (wan6 triggered, only wan is monitored)
|
|
echo "1" > "$TEST_UCI_DIR/enable_badwan"
|
|
echo "1" > "$TEST_UCI_DIR/telegram_enabled"
|
|
echo "wan" > "$TEST_UCI_DIR/monitored_ifaces"
|
|
echo "down" > "$TEST_UCI_DIR/ubus_state_wan6"
|
|
ACTION="ifdown" INTERFACE="wan6" sh "$TEST_MONITOR"
|
|
[ ! -f "$TEST_TELEGRAM_LOG" ] || fail "Test 3 failed: notification sent for unmonitored interface wan6"
|
|
|
|
# Test 4: Monitored interface drops (wan ifdown, initial up -> down transition)
|
|
echo "1" > "$TEST_UCI_DIR/enable_badwan"
|
|
echo "1" > "$TEST_UCI_DIR/telegram_enabled"
|
|
echo "wan" > "$TEST_UCI_DIR/monitored_ifaces"
|
|
echo "up" > "$VAR_RUN/wan_state_wan" # interface was previously UP
|
|
echo "down" > "$TEST_UCI_DIR/ubus_state_wan"
|
|
|
|
ACTION="ifdown" INTERFACE="wan" sh "$TEST_MONITOR"
|
|
# Wait for background verification
|
|
sleep 1
|
|
|
|
[ -f "$TEST_TELEGRAM_LOG" ] || fail "Test 4 failed: no notification sent on UP -> DOWN transition"
|
|
grep -Fq "Интерфейс *wan* упал!" "$TEST_TELEGRAM_LOG" || fail "Test 4 failed: expected drop message not found in log"
|
|
[ "$(cat "$VAR_RUN/wan_state_wan")" = "down" ] || fail "Test 4 failed: state file not set to down"
|
|
|
|
# Test 5: Duplicate hotplug ifdown while state is already down (NO repeated spam)
|
|
rm -f "$TEST_TELEGRAM_LOG"
|
|
ACTION="ifdown" INTERFACE="wan" sh "$TEST_MONITOR"
|
|
sleep 1
|
|
|
|
[ ! -f "$TEST_TELEGRAM_LOG" ] || fail "Test 5 failed: duplicate notification sent on repeated ifdown without state change"
|
|
|
|
# Test 6: Interface recovers (wan ifup, transition down -> up)
|
|
rm -f "$TEST_TELEGRAM_LOG"
|
|
echo "up" > "$TEST_UCI_DIR/ubus_state_wan"
|
|
|
|
ACTION="ifup" INTERFACE="wan" sh "$TEST_MONITOR"
|
|
sleep 1
|
|
|
|
[ -f "$TEST_TELEGRAM_LOG" ] || fail "Test 6 failed: no notification sent on DOWN -> UP recovery"
|
|
grep -Fq "Интерфейс *wan* поднялся!" "$TEST_TELEGRAM_LOG" || fail "Test 6 failed: expected recovery message not found in log"
|
|
[ "$(cat "$VAR_RUN/wan_state_wan")" = "up" ] || fail "Test 6 failed: state file not set to up"
|
|
|
|
# Test 7: Duplicate hotplug ifup while state is already up (NO duplicate message)
|
|
rm -f "$TEST_TELEGRAM_LOG"
|
|
ACTION="ifup" INTERFACE="wan" sh "$TEST_MONITOR"
|
|
sleep 1
|
|
|
|
[ ! -f "$TEST_TELEGRAM_LOG" ] || fail "Test 7 failed: duplicate notification sent on repeated ifup without state change"
|
|
|
|
printf 'All 7 WAN monitor hotplug tests PASSED!\n'
|