OTP test infrastructure for Maestro flows

Dev-only /internal/_test/peek-otp + /internal/_test/reset-phone endpoints
gated by NODE_ENV !== 'production'. peek-otp reads the latest stub OTP
out of an in-memory map populated by otp.service.js fazpassSendStub;
reset-phone wipes otp_requests rows (and optionally the customers row)
so flows can re-run without tripping cooldowns.

JS + shell helpers under .maestro/scripts/ wrap the endpoints for use
inside Maestro runScript steps. 01_smoke.yaml expanded from a launch-only
sanity check to a full cold-start onboarding -> force-register -> OTP ->
home walk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 16:19:22 +08:00
parent d33d4419ea
commit 4680c36e34
8 changed files with 212 additions and 8 deletions

View File

@@ -0,0 +1,13 @@
// Read the latest stub-generated OTP code for TEST_PHONE from the
// backend's dev-only /internal/_test/peek-otp endpoint.
//
// Writes the 6-digit code to output.OTP so the calling flow can use ${output.OTP}.
const phone = TEST_PHONE
const url = BACKEND_INTERNAL_URL || 'http://localhost:3001'
const encoded = encodeURIComponent(phone)
const resp = http.get(`${url}/internal/_test/peek-otp?phone=${encoded}`)
if (resp.status !== 200) {
throw new Error(`peek-otp failed (${resp.status}): ${resp.body}`)
}
const data = json(resp.body)
output.OTP = data.code

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Read the latest stub-generated OTP code for ${TEST_PHONE} from the
# backend's dev-only /internal/_test/peek-otp endpoint.
#
# Echoes the 6-digit code to stdout. Maestro captures the last line of
# stdout into the variable named by the calling runScript step.
set -euo pipefail
phone="${TEST_PHONE:-}"
url="${BACKEND_INTERNAL_URL:-http://localhost:3001}"
if [[ -z "$phone" ]]; then
echo "TEST_PHONE env var required" >&2
exit 1
fi
# url-encode the leading +
encoded_phone="$(printf %s "$phone" | sed 's/+/%2B/')"
resp="$(curl -fsS "${url}/internal/_test/peek-otp?phone=${encoded_phone}")"
echo "$resp" | jq -r .code

View File

@@ -0,0 +1,11 @@
// Wipe otp_requests rows + customer row for TEST_PHONE so repeated runs
// don't trip the 60s cooldown or hit IDENTITY_CONFLICT.
const phone = TEST_PHONE
const url = BACKEND_INTERNAL_URL || 'http://localhost:3001'
const resp = http.post(`${url}/internal/_test/reset-phone`, {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, drop_customer: true }),
})
if (resp.status !== 200) {
throw new Error(`reset-phone failed (${resp.status}): ${resp.body}`)
}

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Wipe otp_requests rows + (optionally) customer row for ${TEST_PHONE} so
# repeated test runs don't trip the 60s cooldown or hit IDENTITY_CONFLICT.
#
# Runs against backend's dev-only /internal/_test/reset-phone endpoint.
set -euo pipefail
phone="${TEST_PHONE:-}"
url="${BACKEND_INTERNAL_URL:-http://localhost:3001}"
drop_customer="${DROP_CUSTOMER:-true}"
if [[ -z "$phone" ]]; then
echo "TEST_PHONE env var required" >&2
exit 1
fi
curl -fsS -X POST "${url}/internal/_test/reset-phone" \
-H "Content-Type: application/json" \
-d "{\"phone\":\"${phone}\",\"drop_customer\":${drop_customer}}" >/dev/null
echo "reset complete: ${phone}"