mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-04-18 23:18:36 +00:00
362 lines
11 KiB
Bash
362 lines
11 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# ─── local init hook ────────────────────────────────────────────────────────
|
|
# If test-init.local.sh exists alongside this file, source it before anything
|
|
# else. Use it to set up your local environment (e.g. activate nvm, set
|
|
# DOCKER_IMAGE, ...). The file is git-ignored so it is safe to put personal
|
|
# or machine-specific configuration there.
|
|
_TEST_HELPERS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=/dev/null
|
|
[[ -f "$_TEST_HELPERS_DIR/test-init.local.sh" ]] && source "$_TEST_HELPERS_DIR/test-init.local.sh"
|
|
unset _TEST_HELPERS_DIR
|
|
|
|
cli_test_init_cli_cmd() {
|
|
if [[ "${VERBOSE_TEST_LOGGING:-0}" == "1" ]]; then
|
|
CLI_CMD=(npm --silent run cli -- -v)
|
|
else
|
|
CLI_CMD=(npm --silent run cli --)
|
|
fi
|
|
}
|
|
|
|
run_cli() {
|
|
"${CLI_CMD[@]}" "$@"
|
|
}
|
|
|
|
cli_test_require_env() {
|
|
local var_name="$1"
|
|
local env_file="${2:-${TEST_ENV_FILE:-environment}}"
|
|
if [[ -z "${!var_name:-}" ]]; then
|
|
echo "[ERROR] required variable '$var_name' is missing in $env_file" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cli_test_assert_contains() {
|
|
local haystack="$1"
|
|
local needle="$2"
|
|
local message="$3"
|
|
if ! grep -Fq "$needle" <<< "$haystack"; then
|
|
echo "[FAIL] $message" >&2
|
|
echo "[FAIL] expected to find: $needle" >&2
|
|
echo "[FAIL] actual output:" >&2
|
|
echo "$haystack" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cli_test_assert_equal() {
|
|
local expected="$1"
|
|
local actual="$2"
|
|
local message="$3"
|
|
if [[ "$expected" != "$actual" ]]; then
|
|
echo "[FAIL] $message" >&2
|
|
echo "[FAIL] expected: $expected" >&2
|
|
echo "[FAIL] actual: $actual" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cli_test_assert_command_fails() {
|
|
local message="$1"
|
|
local log_file="$2"
|
|
shift 2
|
|
set +e
|
|
"$@" >"$log_file" 2>&1
|
|
local exit_code=$?
|
|
set -e
|
|
if [[ "$exit_code" -eq 0 ]]; then
|
|
echo "[FAIL] $message" >&2
|
|
cat "$log_file" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cli_test_assert_files_equal() {
|
|
local expected_file="$1"
|
|
local actual_file="$2"
|
|
local message="$3"
|
|
if ! cmp -s "$expected_file" "$actual_file"; then
|
|
echo "[FAIL] $message" >&2
|
|
echo "[FAIL] expected sha256: $(sha256sum "$expected_file" | awk '{print $1}')" >&2
|
|
echo "[FAIL] actual sha256: $(sha256sum "$actual_file" | awk '{print $1}')" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
cli_test_sanitise_cat_stdout() {
|
|
sed '/^\[CLIWatchAdapter\] File watching is not enabled in CLI version$/d'
|
|
}
|
|
|
|
cli_test_json_string_field_from_stdin() {
|
|
local field_name="$1"
|
|
node -e '
|
|
const fs = require("node:fs");
|
|
const fieldName = process.argv[1];
|
|
const data = JSON.parse(fs.readFileSync(0, "utf-8"));
|
|
const value = data[fieldName];
|
|
if (typeof value === "string") {
|
|
process.stdout.write(value);
|
|
}
|
|
' "$field_name"
|
|
}
|
|
|
|
cli_test_json_string_field_from_file() {
|
|
local json_file="$1"
|
|
local field_name="$2"
|
|
node -e '
|
|
const fs = require("node:fs");
|
|
const jsonFile = process.argv[1];
|
|
const fieldName = process.argv[2];
|
|
const data = JSON.parse(fs.readFileSync(jsonFile, "utf-8"));
|
|
const value = data[fieldName];
|
|
if (typeof value === "string") {
|
|
process.stdout.write(value);
|
|
}
|
|
' "$json_file" "$field_name"
|
|
}
|
|
|
|
cli_test_json_field_is_na() {
|
|
local json_file="$1"
|
|
local field_name="$2"
|
|
[[ "$(cli_test_json_string_field_from_file "$json_file" "$field_name")" == "N/A" ]]
|
|
}
|
|
|
|
cli_test_curl_json() {
|
|
curl -4 -sS --fail --connect-timeout 3 --max-time 15 "$@"
|
|
}
|
|
|
|
cli_test_init_settings_file() {
|
|
local settings_file="$1"
|
|
run_cli init-settings --force "$settings_file" >/dev/null
|
|
}
|
|
|
|
cli_test_mark_settings_configured() {
|
|
local settings_file="$1"
|
|
SETTINGS_FILE="$settings_file" node <<'NODE'
|
|
const fs = require("node:fs");
|
|
const settingsPath = process.env.SETTINGS_FILE;
|
|
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
data.isConfigured = true;
|
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
|
NODE
|
|
}
|
|
|
|
cli_test_apply_couchdb_settings() {
|
|
local settings_file="$1"
|
|
local couchdb_uri="$2"
|
|
local couchdb_user="$3"
|
|
local couchdb_password="$4"
|
|
local couchdb_dbname="$5"
|
|
local live_sync="${6:-0}"
|
|
SETTINGS_FILE="$settings_file" \
|
|
COUCHDB_URI="$couchdb_uri" \
|
|
COUCHDB_USER="$couchdb_user" \
|
|
COUCHDB_PASSWORD="$couchdb_password" \
|
|
COUCHDB_DBNAME="$couchdb_dbname" \
|
|
LIVE_SYNC="$live_sync" \
|
|
node <<'NODE'
|
|
const fs = require("node:fs");
|
|
const settingsPath = process.env.SETTINGS_FILE;
|
|
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
data.couchDB_URI = process.env.COUCHDB_URI;
|
|
data.couchDB_USER = process.env.COUCHDB_USER;
|
|
data.couchDB_PASSWORD = process.env.COUCHDB_PASSWORD;
|
|
data.couchDB_DBNAME = process.env.COUCHDB_DBNAME;
|
|
if (process.env.LIVE_SYNC === "1") {
|
|
data.liveSync = true;
|
|
data.syncOnStart = false;
|
|
data.syncOnSave = false;
|
|
data.usePluginSync = false;
|
|
}
|
|
data.isConfigured = true;
|
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
|
NODE
|
|
}
|
|
|
|
cli_test_apply_remote_sync_settings() {
|
|
local settings_file="$1"
|
|
SETTINGS_FILE="$settings_file" \
|
|
REMOTE_TYPE="$REMOTE_TYPE" \
|
|
COUCHDB_URI="$COUCHDB_URI" \
|
|
COUCHDB_USER="${COUCHDB_USER:-}" \
|
|
COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-}" \
|
|
COUCHDB_DBNAME="$COUCHDB_DBNAME" \
|
|
MINIO_ENDPOINT="${MINIO_ENDPOINT:-}" \
|
|
MINIO_BUCKET="$MINIO_BUCKET" \
|
|
MINIO_ACCESS_KEY="${MINIO_ACCESS_KEY:-}" \
|
|
MINIO_SECRET_KEY="${MINIO_SECRET_KEY:-}" \
|
|
ENCRYPT="${ENCRYPT:-0}" \
|
|
E2E_PASSPHRASE="${E2E_PASSPHRASE:-}" \
|
|
node <<'NODE'
|
|
const fs = require("node:fs");
|
|
const settingsPath = process.env.SETTINGS_FILE;
|
|
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
|
|
const remoteType = process.env.REMOTE_TYPE;
|
|
if (remoteType === "COUCHDB") {
|
|
data.remoteType = "";
|
|
data.couchDB_URI = process.env.COUCHDB_URI;
|
|
data.couchDB_USER = process.env.COUCHDB_USER;
|
|
data.couchDB_PASSWORD = process.env.COUCHDB_PASSWORD;
|
|
data.couchDB_DBNAME = process.env.COUCHDB_DBNAME;
|
|
} else if (remoteType === "MINIO") {
|
|
data.remoteType = "MINIO";
|
|
data.bucket = process.env.MINIO_BUCKET;
|
|
data.endpoint = process.env.MINIO_ENDPOINT;
|
|
data.accessKey = process.env.MINIO_ACCESS_KEY;
|
|
data.secretKey = process.env.MINIO_SECRET_KEY;
|
|
data.region = "auto";
|
|
data.forcePathStyle = true;
|
|
}
|
|
|
|
data.liveSync = true;
|
|
data.syncOnStart = false;
|
|
data.syncOnSave = false;
|
|
data.usePluginSync = false;
|
|
data.encrypt = process.env.ENCRYPT === "1";
|
|
data.passphrase = data.encrypt ? process.env.E2E_PASSPHRASE : "";
|
|
data.isConfigured = true;
|
|
|
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
|
NODE
|
|
}
|
|
|
|
cli_test_apply_p2p_settings() {
|
|
local settings_file="$1"
|
|
local room_id="$2"
|
|
local passphrase="$3"
|
|
local app_id="${4:-self-hosted-livesync-cli-tests}"
|
|
local relays="${5:-ws://localhost:4000/}"
|
|
local auto_accept="${6:-~.*}"
|
|
SETTINGS_FILE="$settings_file" \
|
|
P2P_ROOM_ID="$room_id" \
|
|
P2P_PASSPHRASE="$passphrase" \
|
|
P2P_APP_ID="$app_id" \
|
|
P2P_RELAYS="$relays" \
|
|
P2P_AUTO_ACCEPT="$auto_accept" \
|
|
node <<'NODE'
|
|
const fs = require("node:fs");
|
|
const settingsPath = process.env.SETTINGS_FILE;
|
|
const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
|
|
data.P2P_Enabled = true;
|
|
data.P2P_AutoStart = false;
|
|
data.P2P_AutoBroadcast = false;
|
|
data.P2P_AppID = process.env.P2P_APP_ID;
|
|
data.P2P_roomID = process.env.P2P_ROOM_ID;
|
|
data.P2P_passphrase = process.env.P2P_PASSPHRASE;
|
|
data.P2P_relays = process.env.P2P_RELAYS;
|
|
data.P2P_AutoAcceptingPeers = process.env.P2P_AUTO_ACCEPT;
|
|
data.P2P_AutoDenyingPeers = "";
|
|
data.P2P_IsHeadless = true;
|
|
data.isConfigured = true;
|
|
|
|
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8");
|
|
NODE
|
|
}
|
|
|
|
cli_test_is_local_p2p_relay() {
|
|
local relay_url="$1"
|
|
[[ "$relay_url" == "ws://localhost:4000" || "$relay_url" == "ws://localhost:4000/" ]]
|
|
}
|
|
|
|
cli_test_stop_p2p_relay() {
|
|
bash "$CLI_DIR/util/p2p-stop.sh" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
cli_test_start_p2p_relay() {
|
|
echo "[INFO] stopping leftover P2P relay container if present"
|
|
cli_test_stop_p2p_relay
|
|
|
|
echo "[INFO] starting local P2P relay container"
|
|
bash "$CLI_DIR/util/p2p-start.sh"
|
|
}
|
|
|
|
cli_test_stop_couchdb() {
|
|
bash "$CLI_DIR/util/couchdb-stop.sh" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
cli_test_start_couchdb() {
|
|
local couchdb_uri="$1"
|
|
local couchdb_user="$2"
|
|
local couchdb_password="$3"
|
|
local couchdb_dbname="$4"
|
|
echo "[INFO] stopping leftover CouchDB container if present"
|
|
cli_test_stop_couchdb
|
|
|
|
echo "[INFO] starting CouchDB test container"
|
|
bash "$CLI_DIR/util/couchdb-start.sh"
|
|
|
|
echo "[INFO] initialising CouchDB test container"
|
|
bash "$CLI_DIR/util/couchdb-init.sh"
|
|
|
|
echo "[INFO] CouchDB create test database: $couchdb_dbname"
|
|
until (cli_test_curl_json -X PUT --user "${couchdb_user}:${couchdb_password}" "${couchdb_uri}/${couchdb_dbname}"); do sleep 5; done
|
|
}
|
|
|
|
cli_test_stop_minio() {
|
|
bash "$CLI_DIR/util/minio-stop.sh" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
cli_test_wait_for_minio_bucket() {
|
|
local minio_endpoint="$1"
|
|
local minio_access_key="$2"
|
|
local minio_secret_key="$3"
|
|
local minio_bucket="$4"
|
|
local retries=30
|
|
local delay_sec=2
|
|
local i
|
|
for ((i = 1; i <= retries; i++)); do
|
|
if docker run --rm --network host --entrypoint=/bin/sh minio/mc -c "mc alias set myminio $minio_endpoint $minio_access_key $minio_secret_key >/dev/null 2>&1 && mc ls myminio/$minio_bucket >/dev/null 2>&1"; then
|
|
return 0
|
|
fi
|
|
bucketName="$minio_bucket" bash "$CLI_DIR/util/minio-init.sh" >/dev/null 2>&1 || true
|
|
sleep "$delay_sec"
|
|
done
|
|
return 1
|
|
}
|
|
|
|
cli_test_start_minio() {
|
|
local minio_endpoint="$1"
|
|
local minio_access_key="$2"
|
|
local minio_secret_key="$3"
|
|
local minio_bucket="$4"
|
|
local minio_init_ok=0
|
|
|
|
echo "[INFO] stopping leftover MinIO container if present"
|
|
cli_test_stop_minio
|
|
|
|
echo "[INFO] starting MinIO test container"
|
|
bucketName="$minio_bucket" bash "$CLI_DIR/util/minio-start.sh"
|
|
|
|
echo "[INFO] initialising MinIO test bucket: $minio_bucket"
|
|
for _ in 1 2 3 4 5; do
|
|
if bucketName="$minio_bucket" bash "$CLI_DIR/util/minio-init.sh"; then
|
|
minio_init_ok=1
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
if [[ "$minio_init_ok" != "1" ]]; then
|
|
echo "[FAIL] could not initialise MinIO bucket after retries: $minio_bucket" >&2
|
|
exit 1
|
|
fi
|
|
if ! cli_test_wait_for_minio_bucket "$minio_endpoint" "$minio_access_key" "$minio_secret_key" "$minio_bucket"; then
|
|
echo "[FAIL] MinIO bucket not ready: $minio_bucket" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
display_test_info(){
|
|
echo "======================"
|
|
echo "Script: ${BASH_SOURCE[1]:-$0}"
|
|
echo "Date: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
echo "Git commit: $(git -C "$SCRIPT_DIR/.." rev-parse --short HEAD 2>/dev/null || echo "N/A")"
|
|
echo "======================"
|
|
}
|
|
|
|
# Docker-mode hook — source overrides when LIVESYNC_TEST_DOCKER=1.
|
|
if [[ "${LIVESYNC_TEST_DOCKER:-0}" == "1" ]]; then
|
|
# shellcheck source=/dev/null
|
|
source "$(dirname "${BASH_SOURCE[0]}")/test-helpers-docker.sh"
|
|
fi |