feat(cli): drop --data-dir; tests isolate via $HOME override
There is no installed base to protect, so the self-contained
`--data-dir P` escape hatch goes away entirely. amy now has exactly
one layout — the production multi-account one — and tests exercise
that same code path. Drops one global flag, one DataDir construction
mode, and the "tests use a different code path than users" footgun.
Layout (unchanged from the previous commits — just the only mode now):
~/.amy/
├── current # `amy use NAME` marker
├── shared/
│ └── events-store/ # FsEventStore, one per machine
├── alice/
│ ├── identity.json
│ ├── state.json
│ ├── aliases.json # {"alice": "<own npub>"} after init
│ └── marmot/
└── bob/ ...
`DataDir.DEFAULT_ROOT` reads `$HOME` directly (falling back to
`user.home`) because JDK 21 resolves `user.home` via getpwuid and
ignores `$HOME` — which would have broken the standard CLI test
isolation pattern of `HOME=/tmp/foo amy …` (the same convention
git, gpg, npm follow).
Test sweep:
- `cli/tests/headless/helpers.sh`, `cli/tests/dm/setup.sh`,
`cli/tests/cache/cache-headless.sh` wrappers all switch to
`HOME=$STATE_DIR amy --name X …`.
- `ensure_identity_for` drops its `dir` parameter; the function and
every harness call site go through `--name` only.
- `A_DIR`/`B_DIR`/`D_DIR` get repointed at `$STATE_DIR/.amy/X`. The
one consumer (`cache-headless.sh`'s T6 `relays.json` check) still
works since it's just a path probe.
- `cli/tests/dm/tests-dm.sh` ghost identities get their own
short-lived `$HOME` so they don't pollute the main test root.
Cache-test T4 inverts: pre-shared-store, "B has not seen A → first
profile show is a relay miss, second is a cache hit" tested
per-account caching. With one shared events-store, B's first lookup
of A is already a cache hit because A wrote kind:0 there during
bootstrap. T4 now asserts that — drops the stale "second lookup hits
cache" half. T7 (no-identity maintenance verbs) gets a fresh fake
`$HOME` plus a throwaway `--name` so the empty store has no inherited
events.
Docs (README + DEVELOPMENT) rewritten to match — quick-start now uses
`amy --name alice create`, the on-disk-layout section shows the new
tree, and the global-flags table replaces `--data-dir` with `--name`
plus the new `amy use` verb.
This commit is contained in:
@@ -15,8 +15,10 @@ REPO_ROOT="$(cd -- "$SCRIPT_DIR/../../.." && pwd)"
|
||||
TESTS_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||
STATE_DIR="$SCRIPT_DIR/state-dm-headless"
|
||||
LOG_DIR="$STATE_DIR/logs"
|
||||
A_DIR="$STATE_DIR/A"
|
||||
D_DIR="$STATE_DIR/D"
|
||||
# Per-account dirs under the same fake $HOME=$STATE_DIR so amy treats
|
||||
# this as one user with two accounts (production layout).
|
||||
A_DIR="$STATE_DIR/.amy/A"
|
||||
D_DIR="$STATE_DIR/.amy/D"
|
||||
|
||||
RUN_TS="$(date +%Y%m%d-%H%M%S)"
|
||||
LOG_FILE="$LOG_DIR/run-$RUN_TS.log"
|
||||
@@ -57,7 +59,7 @@ while [[ $# -gt 0 ]]; do
|
||||
shift
|
||||
done
|
||||
|
||||
mkdir -p "$STATE_DIR" "$LOG_DIR" "$A_DIR" "$D_DIR"
|
||||
mkdir -p "$STATE_DIR" "$LOG_DIR"
|
||||
: >"$LOG_FILE"
|
||||
: >"$RESULTS_FILE"
|
||||
|
||||
@@ -94,8 +96,8 @@ trap 'exit 129' HUP
|
||||
banner "Amethyst NIP-17 DM headless interop ($RUN_TS)"
|
||||
preflight_dm
|
||||
start_local_relay
|
||||
ensure_identity_for A "$A_DIR"
|
||||
ensure_identity_for D "$D_DIR"
|
||||
ensure_identity_for A
|
||||
ensure_identity_for D
|
||||
configure_relays_dm
|
||||
|
||||
test_01_dm_text_round_trip
|
||||
|
||||
+12
-9
@@ -69,21 +69,24 @@ preflight_dm() {
|
||||
}
|
||||
|
||||
# --- amy identity wrappers ---------------------------------------------------
|
||||
# Two identities: A (sender) and D (recipient). We reuse A_DIR for parity
|
||||
# with the existing harness files; D_DIR is new.
|
||||
# Two identities: A (sender) and D (recipient), both inside the same
|
||||
# $STATE_DIR/.amy tree. They share $STATE_DIR/.amy/shared/events-store/
|
||||
# — the same code path real users hit with multiple accounts. The
|
||||
# enclosing harness sets STATE_DIR to a fresh tempdir per run, so amy
|
||||
# sees a virgin home each time.
|
||||
#
|
||||
# `--secret-backend=plaintext` keeps these throwaway interop runs headless —
|
||||
# the default `auto` would try the OS keychain (not available in CI) and then
|
||||
# ask for a NIP-49 passphrase. Plaintext still writes 0600-owner-only.
|
||||
amy_a() { "$AMY_BIN" --data-dir "$A_DIR" --secret-backend plaintext --json "$@"; }
|
||||
amy_d() { "$AMY_BIN" --data-dir "$D_DIR" --secret-backend plaintext --json "$@"; }
|
||||
amy_a() { HOME="$STATE_DIR" "$AMY_BIN" --name A --secret-backend plaintext --json "$@"; }
|
||||
amy_d() { HOME="$STATE_DIR" "$AMY_BIN" --name D --secret-backend plaintext --json "$@"; }
|
||||
|
||||
# --- identity bootstrap ------------------------------------------------------
|
||||
ensure_identity_for() {
|
||||
local who="$1" dir="$2"
|
||||
step "initialising Identity $who (amy at $dir)"
|
||||
local who="$1"
|
||||
step "initialising Identity $who (amy at \$HOME=$STATE_DIR --name $who)"
|
||||
local out
|
||||
out=$("$AMY_BIN" --data-dir "$dir" --secret-backend plaintext --json init) || {
|
||||
out=$(HOME="$STATE_DIR" "$AMY_BIN" --name "$who" --secret-backend plaintext --json init) || {
|
||||
fail_msg "amy init failed for $who: $out"; exit 1
|
||||
}
|
||||
local npub hex
|
||||
@@ -103,8 +106,8 @@ ensure_identity_for() {
|
||||
# so the DM strict-relay routing has something to resolve to.
|
||||
configure_relays_dm() {
|
||||
banner "Configuring relays → $RELAY_URL"
|
||||
"$AMY_BIN" --data-dir "$A_DIR" relay add "$RELAY_URL" --type all >/dev/null
|
||||
"$AMY_BIN" --data-dir "$D_DIR" relay add "$RELAY_URL" --type all >/dev/null
|
||||
amy_a relay add "$RELAY_URL" --type all >/dev/null
|
||||
amy_d relay add "$RELAY_URL" --type all >/dev/null
|
||||
|
||||
step "publishing A's NIP-65 + kind:10050 lists"
|
||||
amy_a relay publish-lists >>"$LOG_FILE" 2>&1 \
|
||||
|
||||
+20
-17
@@ -12,22 +12,23 @@
|
||||
# dm-05 file message reference mode round-trip (kind:15)
|
||||
# dm-06 cursor advance (subsequent no-flag `dm list` is empty)
|
||||
|
||||
# Wrap amy_json around either data-dir so the per-test code stays tight.
|
||||
# `--json` opts into amy's machine-readable contract; assertions below
|
||||
# parse with jq.
|
||||
# Wrap amy_json around either account so the per-test code stays tight.
|
||||
# Both share $HOME=$STATE_DIR (set by the harness); --name picks the
|
||||
# account inside it. `--json` opts into amy's machine-readable contract;
|
||||
# assertions below parse with jq.
|
||||
amy_json_for() {
|
||||
local dir="$1"; shift
|
||||
local account="$1"; shift
|
||||
local out
|
||||
if ! out=$("$AMY_BIN" --data-dir "$dir" --json "$@" 2>>"$LOG_FILE"); then
|
||||
fail_msg "amy --data-dir $dir $*: exit $? (see $LOG_FILE)"
|
||||
if ! out=$(HOME="$STATE_DIR" "$AMY_BIN" --name "$account" --secret-backend plaintext --json "$@" 2>>"$LOG_FILE"); then
|
||||
fail_msg "amy --name $account $*: exit $? (see $LOG_FILE)"
|
||||
printf '%s\n' "$out" >>"$LOG_FILE"
|
||||
return 1
|
||||
fi
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
amy_json_a() { amy_json_for "$A_DIR" "$@"; }
|
||||
amy_json_d() { amy_json_for "$D_DIR" "$@"; }
|
||||
amy_json_a() { amy_json_for A "$@"; }
|
||||
amy_json_d() { amy_json_for D "$@"; }
|
||||
|
||||
test_01_dm_text_round_trip() {
|
||||
banner "DM-01 — text round-trip A↔D (kind:14)"
|
||||
@@ -91,19 +92,21 @@ test_03_dm_send_rejects_no_inbox() {
|
||||
local id="dm-03 strict no_dm_relays"
|
||||
|
||||
# Generate a throwaway identity but do NOT publish its kind:10050.
|
||||
local tmpdir; tmpdir=$(mktemp -d "${STATE_DIR}/ghost.XXXXXX")
|
||||
# The ghost lives in its own fake $HOME so it doesn't pollute the
|
||||
# test's main STATE_DIR with a third account.
|
||||
local ghost_home; ghost_home=$(mktemp -d "${STATE_DIR}/ghost-home.XXXXXX")
|
||||
local ghost_out ghost_npub
|
||||
ghost_out=$("$AMY_BIN" --data-dir "$tmpdir" --secret-backend plaintext --json init) || {
|
||||
record_result "$id" fail "ghost init failed"; rm -rf "$tmpdir"; return
|
||||
ghost_out=$(HOME="$ghost_home" "$AMY_BIN" --name ghost --secret-backend plaintext --json init) || {
|
||||
record_result "$id" fail "ghost init failed"; rm -rf "$ghost_home"; return
|
||||
}
|
||||
ghost_npub=$(printf '%s' "$ghost_out" | jq -r '.npub')
|
||||
info "ghost: $ghost_npub (no relays advertised)"
|
||||
|
||||
# A sends without --allow-fallback; amy should refuse.
|
||||
local raw rc
|
||||
raw=$("$AMY_BIN" --data-dir "$A_DIR" --json dm send "$ghost_npub" "should be rejected" 2>&1)
|
||||
raw=$(HOME="$STATE_DIR" "$AMY_BIN" --name A --secret-backend plaintext --json dm send "$ghost_npub" "should be rejected" 2>&1)
|
||||
rc=$?
|
||||
rm -rf "$tmpdir"
|
||||
rm -rf "$ghost_home"
|
||||
if [[ "$rc" -ne 0 ]] && printf '%s' "$raw" | grep -q '"error":"no_dm_relays"'; then
|
||||
info "amy refused with no_dm_relays as expected (exit $rc)"
|
||||
record_result "$id" pass
|
||||
@@ -121,13 +124,13 @@ test_04_dm_send_allow_fallback() {
|
||||
# fall through kind:10050 → NIP-65 read → bootstrap. Our bootstrap set
|
||||
# always includes the loopback relay via the shared RelayConfig, so the
|
||||
# publish should succeed even though the ghost has no 10050.
|
||||
local tmpdir; tmpdir=$(mktemp -d "${STATE_DIR}/ghost.XXXXXX")
|
||||
local ghost_home; ghost_home=$(mktemp -d "${STATE_DIR}/ghost-home.XXXXXX")
|
||||
local ghost_out ghost_npub
|
||||
ghost_out=$("$AMY_BIN" --data-dir "$tmpdir" --secret-backend plaintext --json init) || {
|
||||
record_result "$id" fail "ghost init failed"; rm -rf "$tmpdir"; return
|
||||
ghost_out=$(HOME="$ghost_home" "$AMY_BIN" --name ghost --secret-backend plaintext --json init) || {
|
||||
record_result "$id" fail "ghost init failed"; rm -rf "$ghost_home"; return
|
||||
}
|
||||
ghost_npub=$(printf '%s' "$ghost_out" | jq -r '.npub')
|
||||
rm -rf "$tmpdir"
|
||||
rm -rf "$ghost_home"
|
||||
|
||||
local out source
|
||||
out=$(amy_json_a dm send "$ghost_npub" "hi via fallback" --allow-fallback) || {
|
||||
|
||||
Reference in New Issue
Block a user