Files
NeoTachyon/tests/subscription_update_reload.sh

182 lines
5.5 KiB
Bash

#!/usr/bin/env bash
set -eo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
UPDATES_UC="$ROOT_DIR/tachyon/files/usr/lib/components/updates.uc"
REAL_LIB="$ROOT_DIR/tachyon/files/usr/lib"
WORK_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}
write_stub() {
local path="$1"
local body="$2"
mkdir -p "$(dirname "$path")"
printf '%s\n' "$body" >"$path"
}
stub_header='#!/usr/bin/env ucode
let fs = require("fs");
function as_string(value) {
return value == null ? "" : "" + value;
}
function shell_quote(value) {
return "'"'"'" + replace(as_string(value), /'"'"'/g, "'"'"'\\'"'"''"'"'") + "'"'"'";
}
function record(line) {
let path = getenv("FAKE_CALL_LOG") || "";
if (path != "")
system("printf '"'"'%s\\n'"'"' " + shell_quote(line) + " >> " + shell_quote(path));
}
'
FAKE_LIB="$WORK_DIR/lib"
write_stub "$FAKE_LIB/subscription/cache.uc" "$stub_header"'
let mode = as_string(ARGV[0]);
record("subscription/cache:" + mode);
if (mode == "ensure-runtime-dirs")
exit(0);
if (mode == "update-request") {
print(getenv("FAKE_SUBSCRIPTION_UPDATE_SUMMARY") || "1 0 0 0", "\n");
exit(0);
}
exit(64);
'
write_stub "$FAKE_LIB/service/state.uc" "$stub_header"'
let mode = as_string(ARGV[0]);
record("service/state:" + mode);
if (mode == "sing-box-service-runtime-pid") {
print("3285\n");
exit(0);
}
if (mode == "acquire-runtime-dir-lock" ||
mode == "acquire-runtime-dir-lock-wait" ||
mode == "release-runtime-dir-lock" ||
mode == "reload-sing-box-runtime" ||
mode == "write-current-reload-state-clean" ||
mode == "run-pending-reload-if-requested")
exit(0);
exit(64);
'
write_stub "$FAKE_LIB/server/service.uc" "$stub_header"'
record("server/service:" + as_string(ARGV[0]));
exit(ARGV[0] == "prepare-all-defaults" ? 0 : 64);
'
write_stub "$FAKE_LIB/config/validator.uc" "$stub_header"'
record("config/validator:" + as_string(ARGV[0]));
exit(ARGV[0] == "validate-runtime" ? 0 : 64);
'
write_stub "$FAKE_LIB/singbox/runtime.uc" "$stub_header"'
let mode = as_string(ARGV[0]);
if (mode == "init-config")
record("singbox/runtime:init-config:" + as_string(ARGV[1]) + ":" + as_string(ARGV[2]) + ":" + as_string(ARGV[3]));
else
record("singbox/runtime:" + mode);
if (mode == "configure-service" || mode == "init-config")
exit(0);
exit(64);
'
write_stub "$FAKE_LIB/singbox/priority.uc" "$stub_header"'
let mode = as_string(ARGV[0]);
record("singbox/priority:" + mode);
if (mode == "stop-runtime" || mode == "start-runtime")
exit(0);
exit(64);
'
write_stub "$FAKE_LIB/singbox/dns_failover.uc" "$stub_header"'
let mode = as_string(ARGV[0]);
record("singbox/dns_failover:" + mode);
if (mode == "stop-runtime" || mode == "start-runtime")
exit(0);
exit(64);
'
run_update() {
local summary="$1"
local log="$2"
: >"$log"
env \
TACHYON_LIB="$FAKE_LIB" \
TACHYON_RUNTIME_STATE_DIR="$WORK_DIR/run" \
TACHYON_SUBSCRIPTION_UPDATE_LOCK_DIR="$WORK_DIR/run/subscription-update.lock" \
TACHYON_RELOAD_LOCK_DIR="$WORK_DIR/run/reload.lock" \
TACHYON_SUBSCRIPTION_UPDATE_STATE_DIR="$WORK_DIR/run/subscription-update" \
TACHYON_SUBSCRIPTION_UPDATE_JOB_DIR="$WORK_DIR/run/subscription-update-jobs" \
TACHYON_SUBSCRIPTION_LINKS_DIR="$WORK_DIR/run/subscription-links" \
TACHYON_SUBSCRIPTION_METADATA_DIR="$WORK_DIR/run/subscription-metadata" \
TACHYON_OUTBOUND_METADATA_DIR="$WORK_DIR/run/outbound-metadata" \
TACHYON_SECTION_CACHE_DIR="$WORK_DIR/run/section-cache" \
TACHYON_RUNTIME_CACHE_FORMAT_FILE="$WORK_DIR/run/cache-format" \
TACHYON_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent/subscription-cache" \
TACHYON_PERSISTENT_SUBSCRIPTION_CACHE_FORMAT_FILE="$WORK_DIR/persistent/subscription-cache/cache-format" \
TACHYON_PENDING_RELOAD_FILE="$WORK_DIR/run/reload.pending" \
TACHYON_RELOAD_STATE_FILE="$WORK_DIR/run/reload-state" \
TACHYON_RULE_CONDITION_CACHE_DIR="$WORK_DIR/run/rule-condition-cache" \
FAKE_CALL_LOG="$log" \
FAKE_SUBSCRIPTION_UPDATE_SUMMARY="$summary" \
ucode -L "$REAL_LIB" "$UPDATES_UC" subscription-update-if-due
}
updated_log="$WORK_DIR/updated.log"
run_update "1 0 0 0" "$updated_log"
node - "$updated_log" <<'JS'
const fs = require("fs");
const calls = fs.readFileSync(process.argv[2], "utf8").trim().split(/\n+/);
const expected = [
"subscription/cache:update-request",
"server/service:prepare-all-defaults",
"config/validator:validate-runtime",
"singbox/runtime:configure-service",
"service/state:sing-box-service-runtime-pid",
"singbox/dns_failover:stop-runtime",
"singbox/runtime:init-config:0:1:1",
"singbox/priority:stop-runtime",
"service/state:reload-sing-box-runtime",
"singbox/priority:start-runtime",
"singbox/dns_failover:start-runtime",
"service/state:write-current-reload-state-clean",
"service/state:run-pending-reload-if-requested"
];
let position = -1;
for (const item of expected) {
const next = calls.indexOf(item, position + 1);
if (next === -1) {
console.error(`missing or out-of-order call: ${item}`);
console.error(calls.join("\n"));
process.exit(1);
}
position = next;
}
JS
unchanged_log="$WORK_DIR/unchanged.log"
run_update "0 0 1 0" "$unchanged_log"
if grep -Eq 'server/service|config/validator|singbox/runtime|singbox/priority|singbox/dns_failover|reload-sing-box-runtime|write-current-reload-state-clean' "$unchanged_log"; then
fail "unchanged subscription update must not rebuild or reload sing-box"
fi
printf 'subscription update reload checks passed\n'