Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 20s
Two new projection modules for the rich verbs landed in Step 11a:
next/kernel/announce_state.erl
Per-target-Cid announcer set.
State: [{TargetCid, [AnnouncerActorId, ...]}, ...]
Set semantics — duplicate Announce by the same actor on the
same target is a no-op.
Public API:
new/0, fold/2, fold_fn/0
announcers_for/2, announce_count/2, announced_cids/1
has_announced/3
next/kernel/endorsement_state.erl
Per-target-Cid + per-kind + per-actor endorsement counter.
State: [{TargetCid, [{Kind, [{ActorId, Count}, ...]}, ...]}, ...]
Additive semantics — re-endorse by the same actor under the
same kind bumps the counter. Undo{Endorse} retraction defers
to a follow-up.
Public API:
new/0, fold/2, fold_fn/0
counters_for/2, total_for/2, kinds_for/2
endorsers_for/3, has_endorsed/4
Both fold_fn/0 returns a 2-arity Erlang fun for
projection:start_link/3 (same plug shape as actor_state /
follower_graph / delivery_state). Non-matching activity types
pass through unchanged.
Read-side accessors cover both enumeration (announcers_for,
endorsers_for) and predicates (has_announced, has_endorsed) so
the feed/timeline projection layer doesn't have to re-implement
that logic on every consumer.
19/19 in next/tests/rich_verbs.sh:
announce_state:
- new/0 -> []
- Announce -> announcer added
- Two announces same target -> both in set
- Duplicate announce by same actor -> no-op
- announce_count + announced_cids
- has_announced predicate
- fold_fn/0 is fun/2
- Non-Announce activity passes through
endorsement_state:
- new/0 -> []
- Endorse -> counter 1
- Two likes by different actors -> total 2
- like + share -> two kinds tracked
- endorsers_for(Cid, Kind)
- has_endorsed predicate
- fold_fn/0 is fun/2
- Non-Endorse activity passes through
- Same actor endorsing twice -> total = 2 (additive)
Conformance preserved at 761/761.
164 lines
7.4 KiB
Bash
Executable File
164 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# next/tests/rich_verbs.sh — m2 Step 11b test.
|
|
#
|
|
# Projection folds for Announce + Endorse activity-types.
|
|
# announce_state tracks per-cid announcer sets;
|
|
# endorsement_state tracks per-cid + per-kind + per-actor counters.
|
|
|
|
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
|
|
|
|
# Cid1/Cid2 are the targets being announced/endorsed.
|
|
SETUP='Cid1 = <<99,49>>, Cid2 = <<99,50>>, Ann_BC1 = [{type, announce}, {actor, bob}, {object, Cid1}], Ann_CC1 = [{type, announce}, {actor, carol}, {object, Cid1}], Ann_BC2 = [{type, announce}, {actor, bob}, {object, Cid2}], End_BLikeC1 = [{type, endorse}, {actor, bob}, {object, Cid1}, {kind, like}], End_CLikeC1 = [{type, endorse}, {actor, carol}, {object, Cid1}, {kind, like}], End_BShareC1 = [{type, endorse}, {actor, bob}, {object, Cid1}, {kind, share}],'
|
|
|
|
cat > "$TMPFILE" <<EPOCHS
|
|
(epoch 1)
|
|
(load "lib/erlang/tokenizer.sx")
|
|
(load "lib/erlang/parser.sx")
|
|
(load "lib/erlang/parser-core.sx")
|
|
(load "lib/erlang/parser-expr.sx")
|
|
(load "lib/erlang/parser-module.sx")
|
|
(load "lib/erlang/transpile.sx")
|
|
(load "lib/erlang/runtime.sx")
|
|
(load "lib/erlang/vm/dispatcher.sx")
|
|
(epoch 2)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/envelope.erl\")) :name)")
|
|
(epoch 3)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/announce_state.erl\")) :name)")
|
|
(epoch 4)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/endorsement_state.erl\")) :name)")
|
|
|
|
;; announce_state new/0
|
|
(epoch 10)
|
|
(eval "(get (erlang-eval-ast \"announce_state:new() =:= []\") :name)")
|
|
|
|
;; Announce -> announcer added
|
|
(epoch 11)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = announce_state:fold(Ann_BC1, announce_state:new()), announce_state:announcers_for(Cid1, S) =:= [bob]\") :name)")
|
|
|
|
;; Two announces same target -> both announcers
|
|
(epoch 12)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = announce_state:fold(Ann_CC1, announce_state:fold(Ann_BC1, announce_state:new())), announce_state:announcers_for(Cid1, S) =:= [bob, carol]\") :name)")
|
|
|
|
;; Duplicate announce by same actor -> no double-add
|
|
(epoch 13)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = announce_state:fold(Ann_BC1, announce_state:fold(Ann_BC1, announce_state:new())), announce_state:announcers_for(Cid1, S) =:= [bob]\") :name)")
|
|
|
|
;; announce_count + announced_cids
|
|
(epoch 14)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = announce_state:fold(Ann_BC2, announce_state:fold(Ann_CC1, announce_state:fold(Ann_BC1, announce_state:new()))), {announce_state:announce_count(Cid1, S), announce_state:announce_count(Cid2, S), announce_state:announced_cids(S)} =:= {2, 1, [Cid1, Cid2]}\") :name)")
|
|
|
|
;; has_announced predicate
|
|
(epoch 15)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = announce_state:fold(Ann_BC1, announce_state:new()), {announce_state:has_announced(bob, Cid1, S), announce_state:has_announced(carol, Cid1, S)} =:= {true, false}\") :name)")
|
|
|
|
;; announce_state fold_fn/0 is fun/2
|
|
(epoch 16)
|
|
(eval "(get (erlang-eval-ast \"is_function(announce_state:fold_fn(), 2)\") :name)")
|
|
|
|
;; Non-Announce activity passes through
|
|
(epoch 17)
|
|
(eval "(get (erlang-eval-ast \"Note = [{type, note}, {actor, alice}, {object, [{content, hi}]}], announce_state:fold(Note, announce_state:new()) =:= []\") :name)")
|
|
|
|
;; ── endorsement_state ─────────────────────────────────────
|
|
|
|
;; new/0
|
|
(epoch 20)
|
|
(eval "(get (erlang-eval-ast \"endorsement_state:new() =:= []\") :name)")
|
|
|
|
;; Endorse -> counter goes to 1
|
|
(epoch 21)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = endorsement_state:fold(End_BLikeC1, endorsement_state:new()), endorsement_state:counters_for(Cid1, S) =:= [{like, 1}]\") :name)")
|
|
|
|
;; Two like-endorses by different actors -> total = 2
|
|
(epoch 22)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = endorsement_state:fold(End_CLikeC1, endorsement_state:fold(End_BLikeC1, endorsement_state:new())), endorsement_state:total_for(Cid1, S) =:= 2\") :name)")
|
|
|
|
;; like + share -> two kinds
|
|
(epoch 23)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = endorsement_state:fold(End_BShareC1, endorsement_state:fold(End_BLikeC1, endorsement_state:new())), endorsement_state:kinds_for(Cid1, S) =:= [like, share]\") :name)")
|
|
|
|
;; endorsers_for(Cid, like)
|
|
(epoch 24)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = endorsement_state:fold(End_CLikeC1, endorsement_state:fold(End_BLikeC1, endorsement_state:new())), endorsement_state:endorsers_for(Cid1, like, S) =:= [bob, carol]\") :name)")
|
|
|
|
;; has_endorsed predicate
|
|
(epoch 25)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = endorsement_state:fold(End_BLikeC1, endorsement_state:new()), {endorsement_state:has_endorsed(bob, Cid1, like, S), endorsement_state:has_endorsed(carol, Cid1, like, S), endorsement_state:has_endorsed(bob, Cid1, share, S)} =:= {true, false, false}\") :name)")
|
|
|
|
;; endorsement_state fold_fn/0 is fun/2
|
|
(epoch 26)
|
|
(eval "(get (erlang-eval-ast \"is_function(endorsement_state:fold_fn(), 2)\") :name)")
|
|
|
|
;; Non-Endorse activity passes through
|
|
(epoch 27)
|
|
(eval "(get (erlang-eval-ast \"Note = [{type, note}, {actor, alice}, {object, [{content, hi}]}], endorsement_state:fold(Note, endorsement_state:new()) =:= []\") :name)")
|
|
|
|
;; Same actor endorsing twice bumps the counter (additive semantics)
|
|
(epoch 28)
|
|
(eval "(get (erlang-eval-ast \"${SETUP} S = endorsement_state:fold(End_BLikeC1, endorsement_state:fold(End_BLikeC1, endorsement_state:new())), endorsement_state:total_for(Cid1, S) =:= 2\") :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="<no output for epoch $epoch>"
|
|
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 3 "announce_state module loaded" "announce_state"
|
|
check 4 "endorsement_state module loaded" "endorsement_state"
|
|
check 10 "announce_state:new -> []" "true"
|
|
check 11 "Announce -> announcer" "true"
|
|
check 12 "Two announces same target" "true"
|
|
check 13 "Duplicate announce no-op" "true"
|
|
check 14 "count / announced_cids" "true"
|
|
check 15 "has_announced predicate" "true"
|
|
check 16 "announce fold_fn/0 fun/2" "true"
|
|
check 17 "Non-Announce passes through" "true"
|
|
check 20 "endorsement_state:new -> []" "true"
|
|
check 21 "Endorse -> counter 1" "true"
|
|
check 22 "Two likes -> total 2" "true"
|
|
check 23 "like + share -> two kinds" "true"
|
|
check 24 "endorsers_for(Cid, like)" "true"
|
|
check 25 "has_endorsed predicate" "true"
|
|
check 26 "endorse fold_fn/0 fun/2" "true"
|
|
check 27 "Non-Endorse passes through" "true"
|
|
check 28 "Same actor endorse twice -> 2" "true"
|
|
|
|
TOTAL=$((PASS+FAIL))
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo "ok $PASS/$TOTAL next/tests/rich_verbs.sh passed"
|
|
else
|
|
echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
|
|
echo "$ERRORS"
|
|
fi
|
|
[ $FAIL -eq 0 ]
|