#!/usr/bin/env bash # next/tests/backfill.sh — m2 Step 9a test. # # Backfill mode slicing per design §13.3. Given an outbox log + # a mode (none / last_n / last_t / full / since_cid), backfill:slice # returns the activity list to send to a new follower as backfill. set -uo pipefail cd "$(git rev-parse --show-toplevel)" SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}" if [ ! -x "$SX_SERVER" ]; then SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe" fi if [ ! -x "$SX_SERVER" ]; then echo "ERROR: sx_server.exe not found." >&2 exit 1 fi VERBOSE="${1:-}" PASS=0; FAIL=0; ERRORS="" TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT # Five activities published at :published = 1, 2, 3, 4, 5 SETUP='Act1 = [{id, <<1>>}, {type, note}, {actor, alice}, {published, 1}], Act2 = [{id, <<2>>}, {type, note}, {actor, alice}, {published, 2}], Act3 = [{id, <<3>>}, {type, note}, {actor, alice}, {published, 3}], Act4 = [{id, <<4>>}, {type, note}, {actor, alice}, {published, 4}], Act5 = [{id, <<5>>}, {type, note}, {actor, alice}, {published, 5}], {ok, L0} = log:open(alice, <<98,97,115,101>>), {ok, L1, _} = log:append(L0, Act1), {ok, L2, _} = log:append(L1, Act2), {ok, L3, _} = log:append(L2, Act3), {ok, L4, _} = log:append(L3, Act4), {ok, L5, _} = log:append(L4, Act5),' cat > "$TMPFILE" < [] (epoch 10) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice(none, L5) =:= []\") :name)") ;; full mode -> all 5 (epoch 11) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice(full, L5) =:= [Act1, Act2, Act3, Act4, Act5]\") :name)") ;; last_n with N=2 -> tail 2 (Act4, Act5) (epoch 12) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({last_n, 2}, L5) =:= [Act4, Act5]\") :name)") ;; last_n with N > total -> all entries (epoch 13) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({last_n, 100}, L5) =:= [Act1, Act2, Act3, Act4, Act5]\") :name)") ;; last_n with N = 0 -> [] (epoch 14) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({last_n, 0}, L5) =:= []\") :name)") ;; last_t with T=2, Now=5 -> activities with :published > 3 and <= 5 -> [Act4, Act5] (epoch 15) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({last_t, 2, fun() -> 5 end}, L5) =:= [Act4, Act5]\") :name)") ;; last_t with T=10, Now=5 -> covers everything from :published > -5 -> all 5 (epoch 16) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({last_t, 10, fun() -> 5 end}, L5) =:= [Act1, Act2, Act3, Act4, Act5]\") :name)") ;; last_t with T=0, Now=5 -> only entries at exactly Now (>0, <=5) — really [] because window is (5..5] (epoch 17) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({last_t, 0, fun() -> 5 end}, L5) =:= []\") :name)") ;; since_cid with the 2nd cid -> entries AFTER it (Act3..Act5) (epoch 18) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({since_cid, <<2>>}, L5) =:= [Act3, Act4, Act5]\") :name)") ;; since_cid with last cid -> [] (epoch 19) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({since_cid, <<5>>}, L5) =:= []\") :name)") ;; since_cid with unknown cid -> [] (epoch 20) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice({since_cid, <<99>>}, L5) =:= []\") :name)") ;; wrap_backfill adds {backfilled, true} to each entry (epoch 21) (eval "(get (erlang-eval-ast \"${SETUP} Wrapped = backfill:slice({last_n, 1}, L5, true), [Act5W] = Wrapped, envelope:get_field(backfilled, Act5W) =:= {ok, true}\") :name)") ;; Wrapped entries preserve :id (epoch 22) (eval "(get (erlang-eval-ast \"${SETUP} Wrapped = backfill:slice({last_n, 1}, L5, true), [Act5W] = Wrapped, envelope:get_field(id, Act5W) =:= {ok, <<5>>}\") :name)") ;; parse_mode: nil / none / atoms (epoch 23) (eval "(get (erlang-eval-ast \"{backfill:parse_mode(nil), backfill:parse_mode(none), backfill:parse_mode(full)} =:= {none, none, full}\") :name)") ;; parse_mode: tuple shapes pass through (epoch 24) (eval "(get (erlang-eval-ast \"backfill:parse_mode({last_n, 3}) =:= {last_n, 3}\") :name)") ;; parse_mode: proplist with mode + limit (epoch 25) (eval "(get (erlang-eval-ast \"backfill:parse_mode([{mode, last_n}, {limit, 50}]) =:= {last_n, 50}\") :name)") ;; parse_mode: proplist with mode = full (epoch 26) (eval "(get (erlang-eval-ast \"backfill:parse_mode([{mode, full}]) =:= full\") :name)") ;; parse_mode: unknown -> none (epoch 27) (eval "(get (erlang-eval-ast \"backfill:parse_mode([{mode, mystery}]) =:= none\") :name)") ;; Unknown mode -> [] (epoch 28) (eval "(get (erlang-eval-ast \"${SETUP} backfill:slice(garbage, L5) =:= []\") :name)") EPOCHS OUTPUT=$(timeout 280 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) check() { local epoch="$1" desc="$2" expected="$3" local actual actual=$(echo "$OUTPUT" | awk -v e="$epoch" ' $0 ~ "^\\(ok-len " e " " { getline; print; exit } $0 ~ "^\\(ok " e " " { print; exit } $0 ~ "^\\(error " e " " { print; exit } ') [ -z "$actual" ] && actual="" if echo "$actual" | grep -qF -- "$expected"; then PASS=$((PASS+1)) [ "$VERBOSE" = "-v" ] && echo " ok $desc" else FAIL=$((FAIL+1)) ERRORS+=" FAIL [$desc] (epoch $epoch) expected: $expected | actual: $actual " fi } check 4 "backfill module loaded" "backfill" check 10 "none mode -> []" "true" check 11 "full mode -> all 5" "true" check 12 "last_n N=2 -> tail 2" "true" check 13 "last_n N=100 -> all 5" "true" check 14 "last_n N=0 -> []" "true" check 15 "last_t T=2 Now=5 -> 4,5" "true" check 16 "last_t T=10 Now=5 -> all 5" "true" check 17 "last_t T=0 Now=5 -> []" "true" check 18 "since_cid mid -> tail 3" "true" check 19 "since_cid last -> []" "true" check 20 "since_cid unknown -> []" "true" check 21 "wrap adds backfilled=true" "true" check 22 "wrap preserves :id" "true" check 23 "parse_mode atoms" "true" check 24 "parse_mode tuple passthrough" "true" check 25 "parse_mode proplist last_n" "true" check 26 "parse_mode proplist full" "true" check 27 "parse_mode unknown -> none" "true" check 28 "unknown slice mode -> []" "true" TOTAL=$((PASS+FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL next/tests/backfill.sh passed" else echo "FAIL $PASS/$TOTAL passed, $FAIL failed:" echo "$ERRORS" fi [ $FAIL -eq 0 ]