test(infra): verify-objecten-notifications asserts Objecten publishes to NRC (refs #152)
Registers an abonnement on the `objecten` kanaal pointing at the existing
webhook sink, writes a RegisterRecord exactly as the ACL does on approval, and
waits for the delivery. That is the whole publish chain in one assertion:
Objecten → its celery worker → NRC → nrc-beat → the callback.
Fails today at the first hop, which is the point:
NRC POST /api/v1/abonnement → 400: {"name":"naam","code":"kanaal_naam",
"reason":"Kanaal met deze naam bestaat niet."}
Also splits S-19b (#150) into #152/#153 in BACKLOG.md — the notification wiring
and the projection re-source are independently deployable (CLAUDE.md §13).
This commit is contained in:
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# S-19b-1 (#152): verify the Objecten → NRC notification path against an ALREADY-RUNNING full
|
||||
# stack. Registers an abonnement on the `objecten` kanaal pointing at a throwaway webhook sink,
|
||||
# writes a RegisterRecord object (exactly as the ACL does on approval, S-19a), and asserts the sink
|
||||
# receives the notification.
|
||||
#
|
||||
# This is the whole publish chain in one assertion: Objecten → its celery worker → NRC → nrc-beat →
|
||||
# the subscriber callback. S-19a deliberately left it disconnected (ADR-0028); this proves it is
|
||||
# connected for real, rather than merely configured.
|
||||
#
|
||||
# All in-network, reaching services by container IP (a single-label host isn't URL-valid for NRC's
|
||||
# callbackUrl validator; the runner can't reach published ports — gitea-actions-gotchas.md §5/§6).
|
||||
# EXCEPT Objecten/Objecttypen, which must be reached by SERVICE NAME: Objecttypen echoes the request
|
||||
# Host into the objecttype `url` and Objecten only accepts the one matching its configured api_root
|
||||
# (ADR-0028).
|
||||
#
|
||||
# Does NOT manage the stack lifecycle, but cleans up the sink/driver it creates.
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SINK_AUTH="Bearer objecten-notification-sink-token"
|
||||
|
||||
cleanup() { docker rm -f rr-osink rr-overify >/dev/null 2>&1 || true; }
|
||||
trap cleanup EXIT
|
||||
|
||||
ip() { docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$1"; }
|
||||
|
||||
# Anchored on the compose replica suffix so they don't also match objecten-db / objecten-redis.
|
||||
obj="$(docker ps -q --filter 'name=objecten[-_][0-9]+$' | head -1)"
|
||||
nrc="$(docker ps -q --filter 'name=nrc-web' | head -1)"
|
||||
[ -n "$obj" ] || { echo "ERROR: no running objecten container — bring the stack up first" >&2; exit 1; }
|
||||
[ -n "$nrc" ] || { echo "ERROR: no running nrc-web container — bring the stack up first" >&2; exit 1; }
|
||||
net="$(docker inspect -f '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' "$obj" | head -1)"
|
||||
nrc_ip="$(ip "$nrc")"
|
||||
echo ">> network=$net nrc=$nrc_ip"
|
||||
|
||||
echo ">> starting the webhook sink"
|
||||
docker rm -f rr-osink >/dev/null 2>&1 || true
|
||||
sink="$(docker create --network "$net" --name rr-osink -e "EXPECTED_AUTH=$SINK_AUTH" \
|
||||
python:3-slim python /sink.py)"
|
||||
docker cp "$here/notification-sink.py" "$sink:/sink.py" >/dev/null
|
||||
docker start "$sink" >/dev/null
|
||||
sleep 1
|
||||
sink_ip="$(ip rr-osink)"
|
||||
echo ">> sink at $sink_ip:9000"
|
||||
|
||||
echo ">> registering the abonnement + writing a RegisterRecord"
|
||||
docker rm -f rr-overify >/dev/null 2>&1 || true
|
||||
drv="$(docker create --network "$net" --name rr-overify \
|
||||
-e "OBJECTEN=http://objecten:8000" \
|
||||
-e "OBJECTEN_TOKEN=${OBJECTEN_TOKEN:-1234567890abcdef1234567890abcdef12345678}" \
|
||||
-e "OBJECTTYPEN=http://objecttypen:8000" \
|
||||
-e "OBJECTTYPEN_TOKEN=${OBJECTTYPEN_TOKEN:-0123456789abcdef0123456789abcdef01234567}" \
|
||||
-e "NRC_BASE=http://$nrc_ip:8000" \
|
||||
-e "SINK_CALLBACK=http://$sink_ip:9000/" -e "SINK_AUTH=$SINK_AUTH" \
|
||||
python:3-slim python /driver.py)"
|
||||
docker cp "$here/objecten-notifications-check.py" "$drv:/driver.py" >/dev/null
|
||||
docker start -a "$drv"
|
||||
reference="$(docker logs rr-overify 2>/dev/null | sed -n 's/^REFERENCE //p' | head -1)"
|
||||
docker rm -f rr-overify >/dev/null
|
||||
[ -n "$reference" ] || { echo "FAIL — the driver did not write a RegisterRecord" >&2; exit 1; }
|
||||
echo ">> wrote reference $reference"
|
||||
|
||||
echo ">> waiting for the notification to reach the sink"
|
||||
for _ in $(seq 1 "${NOTIFICATION_TRIES:-40}"); do
|
||||
if docker logs rr-osink 2>&1 | grep -q "$reference"; then
|
||||
echo "OK — Objecten published to NRC and the abonnement delivered it:"
|
||||
docker logs rr-osink 2>&1 | grep "$reference" | tail -1 | cut -c1-500
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "FAIL — no 'objecten' notification for $reference reached the sink." >&2
|
||||
echo " Objecten accepted the write, so the gap is downstream: the celery broker/worker," >&2
|
||||
echo " the kanaal registration, or Objecten's notifications_config." >&2
|
||||
echo "--- sink log ---" >&2; docker logs rr-osink 2>&1 | tail -8 >&2
|
||||
echo "--- objecten log ---" >&2; docker logs "$obj" 2>&1 | tail -15 >&2
|
||||
exit 1
|
||||
Reference in New Issue
Block a user