diff --git a/skills/slack/tests/hold.test.sh b/skills/slack/tests/hold.test.sh index 4537b68a6b..0d493e6df4 100755 --- a/skills/slack/tests/hold.test.sh +++ b/skills/slack/tests/hold.test.sh @@ -44,10 +44,11 @@ row() { sk_run -- listen --status --root "$1"; LINE="$(printf '%s' "$OUT" | sed refuse_next_post() { sk_ctl /_test/fault '{"method": "chat.postMessage", "error": "not_in_channel", "times": 1}' >/dev/null; } # A status timestamp acknowledges a completed poll. The parent suite bounds # this wait even if the relay or the settings reader stops making progress. -polled_after() { # ROOT LAST_POLL +polled_after() { # ROOT LAST_POLL [RESUMED] local tries=0 value while [ "$tries" -lt 100 ]; do - value="$(jq -r --argjson after "$2" '.last_poll > $after' "$1/tmp/slack/status.json" 2>/dev/null)" + value="$(jq -r --argjson after "$2" --argjson resumed "${3:-false}" \ + '.last_poll > $after and ($resumed == false or (.last_poll_ok and .held_by == ""))' "$1/tmp/slack/status.json" 2>/dev/null)" [ "$value" != true ] || return 0 tries=$((tries + 1)); sleep 0.1 done @@ -594,6 +595,29 @@ while read -r name source caller control; do if [ "$control" = mutant ]; then sk_mutant no-poll-presence relay.py 'presence = load_presence\(self.path\)' 'presence = self.presence' fi + if [ "$control:$caller" = real:none ]; then + sk_copy "resume-$name" + # Pause the real refused poll before it records its completion time. + # Removal then precedes that old reading's status write on every run. + python3 - "${SK_BIN%/*}/lib/relay.py" <<'PY' +from pathlib import Path +import sys + +path = Path(sys.argv[1]) +text = path.read_text() +needle = ' delivered = max(self.state.delivered, key=float, default="")' +assert text.count(needle) == 1 +barrier = ''' arm = self.path / "tmp/resume-arm" + if not ok and "setting-invalid" in error and arm.exists(): + arm.unlink() + (self.path / "tmp/resume-entered").touch() + while not (self.path / "tmp/resume-release").exists(): + time.sleep(0.01) +''' +path.write_text(text.replace(needle, barrier + needle)) +PY + [ "$?" -eq 0 ] || exit 1 + fi case "$caller" in empty) sk_relay_start "$R" --root "$S" SLACK_JARVIS_SESSION_FILE= ;; *) sk_relay_start "$R" --root "$S" ;; @@ -631,6 +655,12 @@ while read -r name source caller control; do assert_has "$(jq -r .last_error "$R/tmp/slack/status.json")" "setting-invalid=SLACK_JARVIS_SESSION_MAX_AGE=invalid root=$R" \ "$name: the refused reading names its root" row "$S"; assert_eq "$(field "$LINE" state)" ok "$name: a refused reading leaves its peer ok" + touch "$R/tmp/resume-arm" || exit 1 + TRIES=0 + while [ ! -f "$R/tmp/resume-entered" ] && [ "$TRIES" -lt 100 ]; do + TRIES=$((TRIES + 1)); sleep 0.1 + done + [ -f "$R/tmp/resume-entered" ] || { bad "refused poll did not reach the resume barrier" "$R"; exit 1; } case "$source" in toml) rm -- "$R/kendex.settings.toml" ;; local) rm -- "$R/.kendex/settings.toml" ;; @@ -638,7 +668,10 @@ while read -r name source caller control; do named) printf 'SLACK_JARVIS_SESSION_FILE=\n' > "$R/private.env" ;; esac AFTER="$(jq -r .last_poll "$R/tmp/slack/status.json")" - polled_after "$R" "$AFTER" || exit 1 + touch "$R/tmp/resume-release" || exit 1 + # A refused poll can finish after removal. Its timestamp does not + # acknowledge the new setting; wait for a successful, unheld poll. + polled_after "$R" "$AFTER" true || exit 1 row "$R"; assert_eq "$(field "$LINE" state)=$(field "$LINE" held-by)" "ok=" "$name: removing or emptying the setting resumes" assert_eq "$(count "$CHANNEL" "Changed hold $name.")=$(count "$CHANNEL" "Invalid hold $name.")" "1=1" \ "$name: resume posts the held notices once"