### Fixed

- Fixed flaky timing issues in P2P synchronisation.
- Fixed more binary file handling issues in CLI.

### Tests

- Rewrite P2P end-to-end tests to use the CLI as host.
This commit is contained in:
vorotamoroz
2026-03-16 00:48:22 +09:00
parent 89bf0488c3
commit 6c69547cef
14 changed files with 1039 additions and 20 deletions

View File

View File

@@ -0,0 +1,228 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
cd "$CLI_DIR"
source "$SCRIPT_DIR/test-helpers.sh"
display_test_info
RUN_BUILD="${RUN_BUILD:-1}"
KEEP_TEST_DATA="${KEEP_TEST_DATA:-1}"
VERBOSE_TEST_LOGGING="${VERBOSE_TEST_LOGGING:-0}"
RELAY="${RELAY:-ws://localhost:4000/}"
USE_INTERNAL_RELAY="${USE_INTERNAL_RELAY:-1}"
APP_ID="${APP_ID:-self-hosted-livesync-cli-tests}"
PEERS_TIMEOUT="${PEERS_TIMEOUT:-20}"
SYNC_TIMEOUT="${SYNC_TIMEOUT:-240}"
ROOM_ID="p2p-room-$(date +%s)-$RANDOM-$RANDOM"
PASSPHRASE="p2p-pass-$(date +%s)-$RANDOM-$RANDOM"
HOST_PEER_NAME="p2p-cli-host"
UPLOAD_PEER_NAME="p2p-cli-upload-$(date +%s)-$RANDOM"
DOWNLOAD_PEER_NAME="p2p-cli-download-$(date +%s)-$RANDOM"
cli_test_init_cli_cmd
if [[ "$RUN_BUILD" == "1" ]]; then
echo "[INFO] building CLI"
npm run build
fi
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-p2p-upload-download.XXXXXX")"
VAULT_HOST="$WORK_DIR/vault-host"
VAULT_UP="$WORK_DIR/vault-up"
VAULT_DOWN="$WORK_DIR/vault-down"
SETTINGS_HOST="$WORK_DIR/settings-host.json"
SETTINGS_UP="$WORK_DIR/settings-up.json"
SETTINGS_DOWN="$WORK_DIR/settings-down.json"
HOST_LOG="$WORK_DIR/p2p-host.log"
mkdir -p "$VAULT_HOST" "$VAULT_UP" "$VAULT_DOWN"
cleanup() {
local exit_code=$?
if [[ -n "${HOST_PID:-}" ]] && kill -0 "$HOST_PID" >/dev/null 2>&1; then
kill -TERM "$HOST_PID" >/dev/null 2>&1 || true
wait "$HOST_PID" >/dev/null 2>&1 || true
fi
if [[ "${P2P_RELAY_STARTED:-0}" == "1" ]]; then
cli_test_stop_p2p_relay
fi
if [[ "$KEEP_TEST_DATA" != "1" ]]; then
rm -rf "$WORK_DIR"
else
echo "[INFO] KEEP_TEST_DATA=1, preserving artefacts at $WORK_DIR"
fi
exit "$exit_code"
}
trap cleanup EXIT
if [[ "$USE_INTERNAL_RELAY" == "1" ]]; then
if cli_test_is_local_p2p_relay "$RELAY"; then
cli_test_start_p2p_relay
P2P_RELAY_STARTED=1
else
echo "[INFO] USE_INTERNAL_RELAY=1 but RELAY is not local ($RELAY), skipping local relay startup"
fi
fi
run_cli_host() {
run_cli "$VAULT_HOST" --settings "$SETTINGS_HOST" "$@"
}
run_cli_up() {
run_cli "$VAULT_UP" --settings "$SETTINGS_UP" "$@"
}
run_cli_down() {
run_cli "$VAULT_DOWN" --settings "$SETTINGS_DOWN" "$@"
}
apply_p2p_test_tweaks() {
local settings_file="$1"
local device_name="$2"
SETTINGS_FILE="$settings_file" DEVICE_NAME="$device_name" PASSPHRASE_VAL="$PASSPHRASE" node <<'NODE'
const fs = require("node:fs");
const settingsPath = process.env.SETTINGS_FILE;
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
data.remoteType = "ONLY_P2P";
data.encrypt = true;
data.passphrase = process.env.PASSPHRASE_VAL;
data.usePathObfuscation = true;
data.handleFilenameCaseSensitive = false;
data.customChunkSize = 50;
data.usePluginSyncV2 = true;
data.doNotUseFixedRevisionForChunks = false;
data.P2P_DevicePeerName = process.env.DEVICE_NAME;
data.isConfigured = true;
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
NODE
}
discover_peer_id() {
local side="$1"
local output
local peer_id
if [[ "$side" == "up" ]]; then
output="$(run_cli_up p2p-peers "$PEERS_TIMEOUT")"
else
output="$(run_cli_down p2p-peers "$PEERS_TIMEOUT")"
fi
peer_id="$(awk -F $'\t' 'NF>=3 && $1=="[peer]" {print $2; exit}' <<< "$output")"
if [[ -z "$peer_id" ]]; then
echo "[FAIL] ${side} could not discover any peer" >&2
echo "[FAIL] peers output:" >&2
echo "$output" >&2
return 1
fi
echo "$peer_id"
}
echo "[INFO] preparing settings"
echo "[INFO] relay=$RELAY room=$ROOM_ID app=$APP_ID"
cli_test_init_settings_file "$SETTINGS_HOST"
cli_test_init_settings_file "$SETTINGS_UP"
cli_test_init_settings_file "$SETTINGS_DOWN"
cli_test_apply_p2p_settings "$SETTINGS_HOST" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY" "~.*"
cli_test_apply_p2p_settings "$SETTINGS_UP" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY" "~.*"
cli_test_apply_p2p_settings "$SETTINGS_DOWN" "$ROOM_ID" "$PASSPHRASE" "$APP_ID" "$RELAY" "~.*"
apply_p2p_test_tweaks "$SETTINGS_HOST" "$HOST_PEER_NAME"
apply_p2p_test_tweaks "$SETTINGS_UP" "$UPLOAD_PEER_NAME"
apply_p2p_test_tweaks "$SETTINGS_DOWN" "$DOWNLOAD_PEER_NAME"
echo "[CASE] start p2p-host"
run_cli_host p2p-host >"$HOST_LOG" 2>&1 &
HOST_PID=$!
for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
if grep -Fq "P2P host is running" "$HOST_LOG"; then
break
fi
sleep 1
done
if ! grep -Fq "P2P host is running" "$HOST_LOG"; then
echo "[FAIL] p2p-host did not become ready" >&2
cat "$HOST_LOG" >&2
exit 1
fi
echo "[PASS] p2p-host started"
echo "[CASE] upload peer discovers host"
HOST_PEER_ID_FOR_UP="$(discover_peer_id up)"
echo "[PASS] upload peer discovered host: $HOST_PEER_ID_FOR_UP"
echo "[CASE] upload phase writes source files"
STORE_TEXT="$WORK_DIR/store-file.md"
DIFF_A_TEXT="$WORK_DIR/test-diff-1.md"
DIFF_B_TEXT="$WORK_DIR/test-diff-2.md"
DIFF_C_TEXT="$WORK_DIR/test-diff-3.md"
printf 'Hello, World!\n' > "$STORE_TEXT"
printf 'Content A\n' > "$DIFF_A_TEXT"
printf 'Content B\n' > "$DIFF_B_TEXT"
printf 'Content C\n' > "$DIFF_C_TEXT"
run_cli_up push "$STORE_TEXT" p2p/store-file.md >/dev/null
run_cli_up push "$DIFF_A_TEXT" p2p/test-diff-1.md >/dev/null
run_cli_up push "$DIFF_B_TEXT" p2p/test-diff-2.md >/dev/null
run_cli_up push "$DIFF_C_TEXT" p2p/test-diff-3.md >/dev/null
LARGE_TXT_100K="$WORK_DIR/large-100k.txt"
LARGE_TXT_1M="$WORK_DIR/large-1m.txt"
head -c 100000 /dev/zero | tr '\0' 'a' > "$LARGE_TXT_100K"
head -c 1000000 /dev/zero | tr '\0' 'b' > "$LARGE_TXT_1M"
run_cli_up push "$LARGE_TXT_100K" p2p/large-100000.md >/dev/null
run_cli_up push "$LARGE_TXT_1M" p2p/large-1000000.md >/dev/null
BINARY_100K="$WORK_DIR/binary-100k.bin"
BINARY_5M="$WORK_DIR/binary-5m.bin"
head -c 100000 /dev/urandom > "$BINARY_100K"
head -c 5000000 /dev/urandom > "$BINARY_5M"
run_cli_up push "$BINARY_100K" p2p/binary-100000.bin >/dev/null
run_cli_up push "$BINARY_5M" p2p/binary-5000000.bin >/dev/null
echo "[PASS] upload source files prepared"
echo "[CASE] upload phase syncs to host"
run_cli_up p2p-sync "$HOST_PEER_ID_FOR_UP" "$SYNC_TIMEOUT" >/dev/null
run_cli_up p2p-sync "$HOST_PEER_ID_FOR_UP" "$SYNC_TIMEOUT" >/dev/null
echo "[PASS] upload phase synced"
echo "[CASE] download peer discovers host"
HOST_PEER_ID_FOR_DOWN="$(discover_peer_id down)"
echo "[PASS] download peer discovered host: $HOST_PEER_ID_FOR_DOWN"
echo "[CASE] download phase syncs from host"
run_cli_down p2p-sync "$HOST_PEER_ID_FOR_DOWN" "$SYNC_TIMEOUT" >/dev/null
run_cli_down p2p-sync "$HOST_PEER_ID_FOR_DOWN" "$SYNC_TIMEOUT" >/dev/null
echo "[PASS] download phase synced"
echo "[CASE] verify text files on download peer"
DOWN_STORE_TEXT="$WORK_DIR/down-store-file.md"
DOWN_DIFF_A_TEXT="$WORK_DIR/down-test-diff-1.md"
DOWN_DIFF_B_TEXT="$WORK_DIR/down-test-diff-2.md"
DOWN_DIFF_C_TEXT="$WORK_DIR/down-test-diff-3.md"
run_cli_down pull p2p/store-file.md "$DOWN_STORE_TEXT" >/dev/null
run_cli_down pull p2p/test-diff-1.md "$DOWN_DIFF_A_TEXT" >/dev/null
run_cli_down pull p2p/test-diff-2.md "$DOWN_DIFF_B_TEXT" >/dev/null
run_cli_down pull p2p/test-diff-3.md "$DOWN_DIFF_C_TEXT" >/dev/null
cmp -s "$STORE_TEXT" "$DOWN_STORE_TEXT" || { echo "[FAIL] store-file mismatch" >&2; exit 1; }
cmp -s "$DIFF_A_TEXT" "$DOWN_DIFF_A_TEXT" || { echo "[FAIL] test-diff-1 mismatch" >&2; exit 1; }
cmp -s "$DIFF_B_TEXT" "$DOWN_DIFF_B_TEXT" || { echo "[FAIL] test-diff-2 mismatch" >&2; exit 1; }
cmp -s "$DIFF_C_TEXT" "$DOWN_DIFF_C_TEXT" || { echo "[FAIL] test-diff-3 mismatch" >&2; exit 1; }
echo "[CASE] verify pushed files on download peer"
DOWN_LARGE_100K="$WORK_DIR/down-large-100k.txt"
DOWN_LARGE_1M="$WORK_DIR/down-large-1m.txt"
DOWN_BINARY_100K="$WORK_DIR/down-binary-100k.bin"
DOWN_BINARY_5M="$WORK_DIR/down-binary-5m.bin"
run_cli_down pull p2p/large-100000.md "$DOWN_LARGE_100K" >/dev/null
run_cli_down pull p2p/large-1000000.md "$DOWN_LARGE_1M" >/dev/null
run_cli_down pull p2p/binary-100000.bin "$DOWN_BINARY_100K" >/dev/null
run_cli_down pull p2p/binary-5000000.bin "$DOWN_BINARY_5M" >/dev/null
cmp -s "$LARGE_TXT_100K" "$DOWN_LARGE_100K" || { echo "[FAIL] large-100000 mismatch" >&2; exit 1; }
cmp -s "$LARGE_TXT_1M" "$DOWN_LARGE_1M" || { echo "[FAIL] large-1000000 mismatch" >&2; exit 1; }
cmp -s "$BINARY_100K" "$DOWN_BINARY_100K" || { echo "[FAIL] binary-100000 mismatch" >&2; exit 1; }
cmp -s "$BINARY_5M" "$DOWN_BINARY_5M" || { echo "[FAIL] binary-5000000 mismatch" >&2; exit 1; }
echo "[PASS] CLI P2P upload/download reproduction scenario completed"