Files
obsidian-livesync/src/apps/cli/test/test-helpers.sh
vorotamoroz cda27fb7f8 - Update trystero to v0.23.0
- Add dockerfile for CLI
- Change relay image for testing on arm64
2026-03-31 07:17:51 +00:00

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