Auto-select hosted staging tenant

This commit is contained in:
rcourtman 2026-04-15 12:23:57 +01:00
parent b73dab2b64
commit 5afffa17d3
3 changed files with 55 additions and 3 deletions

View file

@ -254,7 +254,10 @@ repo-tracked operator command that composes the hosted signup/billing eval pack
with the hosted mobile onboarding bootstrap helpers under
`tests/integration/scripts/`, and those helpers must fail closed onto explicit
target cloud host and control-plane URL input instead of silently defaulting to
production infrastructure.
production infrastructure. When the operator does not pin
`PULSE_E2E_HOSTED_TENANT_ID`, that entrypoint may auto-select the newest active
tenant exposed by the authenticated `/admin/tenants?state=active` control-plane
view, but it must still fail closed when no active tenant is available.
Those same governed release workflows also own the operator-facing wording for
that promotion metadata. Human-visible workflow inputs, summaries, and error
messages must describe the path as a prerelease or preview flow rather than

View file

@ -33,6 +33,47 @@ require_env() {
fi
}
resolve_hosted_tenant_id() {
if [[ -n "${PULSE_E2E_HOSTED_TENANT_ID}" ]]; then
return 0
fi
local tenants_payload
if ! tenants_payload="$(
curl -fsS \
-H "X-Admin-Key: ${PULSE_CP_ADMIN_KEY}" \
-H 'Accept: application/json' \
"${PULSE_CLOUD_BASE_URL%/}/admin/tenants?state=active"
)"; then
echo "Failed to list active hosted tenants from ${PULSE_CLOUD_BASE_URL}" >&2
exit 1
fi
if ! PULSE_E2E_HOSTED_TENANT_ID="$(
printf '%s' "${tenants_payload}" | node -e '
let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => {
input += chunk;
});
process.stdin.on("end", () => {
const parsed = JSON.parse(input);
const tenants = Array.isArray(parsed?.tenants) ? parsed.tenants : [];
const selected = tenants.find((entry) => typeof entry?.id === "string" && entry.id.trim() !== "");
if (!selected) {
process.exit(2);
}
process.stdout.write(selected.id.trim());
});
'
)"; then
echo "No active hosted tenant found; set PULSE_E2E_HOSTED_TENANT_ID explicitly." >&2
exit 1
fi
echo "Auto-selected active hosted tenant ${PULSE_E2E_HOSTED_TENANT_ID}"
}
require_command curl
require_command go
require_command node
@ -50,7 +91,6 @@ require_env "PULSE_CLOUD_SSH_TARGET" "${PULSE_CLOUD_SSH_TARGET}"
require_env "PULSE_CP_ADMIN_KEY" "${PULSE_CP_ADMIN_KEY}"
require_env "PULSE_E2E_STRIPE_API_KEY" "${PULSE_E2E_STRIPE_API_KEY}"
require_env "PULSE_E2E_STRIPE_WEBHOOK_SECRET" "${PULSE_E2E_STRIPE_WEBHOOK_SECRET}"
require_env "PULSE_E2E_HOSTED_TENANT_ID" "${PULSE_E2E_HOSTED_TENANT_ID}"
echo "[1/2] Running hosted signup and billing smoke against ${PULSE_CLOUD_BASE_URL}"
(
@ -64,6 +104,8 @@ echo "[1/2] Running hosted signup and billing smoke against ${PULSE_CLOUD_BASE_U
node ./scripts/run-evals.mjs --mode deterministic --scenario "${HOSTED_SCENARIOS}"
)
resolve_hosted_tenant_id
echo "[2/2] Running hosted mobile onboarding smoke against tenant ${PULSE_E2E_HOSTED_TENANT_ID}"
bootstrap_args=(
"${INTEGRATION_ROOT}/scripts/bootstrap-hosted-mobile-onboarding.mjs"

View file

@ -79,8 +79,10 @@ async function listTenants(
request: import('@playwright/test').APIRequestContext,
baseURL: string,
adminKey: string,
state?: string,
): Promise<Tenant[]> {
const response = await request.get(`${baseURL}/admin/tenants`, {
const suffix = state ? `?state=${encodeURIComponent(state)}` : '';
const response = await request.get(`${baseURL}/admin/tenants${suffix}`, {
headers: {
'X-Admin-Key': adminKey,
Accept: 'application/json',
@ -174,6 +176,11 @@ test.describe.serial('Cloud billing lifecycle (post-checkout)', () => {
}, { timeout: 60_000 }).toBe('active');
expect(tenantID).toBeTruthy();
const activeTenants = await listTenants(page.request, baseURL, adminKey, 'active');
const activeTenant = activeTenants.find((entry) => entry.id === tenantID);
expect(activeTenant?.id).toBe(tenantID);
expect(activeTenant?.state).toBe('active');
await stripeRequest<StripeSubscriptionResponse>(
stripeSecretKey,
`/v1/subscriptions/${encodeURIComponent(subscriptionID)}`,