test(e2e): the AI offer follows the shared provider's health

Two runs on one account, and the only difference between them is a row in
provider_health: a success inside the freshness window brings the offer and
the tap leaves an active ai_full receipt; a dead credential inside its
cooldown brings 'not answering' and no button at all.

The seed is instance-wide because the projection is - a fresh account's own
ledger is empty by construction - so the spec runs serially and puts the
operator key and the ledger rows back afterwards. The offer path asserts
that no provider was called: the flow reads a projection and never probes.

Verified as a real check, not a tautology: moving the seeded success from 30
minutes to 25 hours ago makes the offer case fail.
This commit is contained in:
Marc-André Bombeck committed 2026-09-11 09:36:07 +02:00
1 parent 74ae672373
commit e5cfdc9fdf
3 files changed
+323

No files matched your search

+21
View File
@@ -487,6 +487,21 @@ export const E2E_SETUP_LOCALE = {
role: "USER",
} as const;
/**
* v1.38.19 (wave D) — the shared-provider offer's own account.
*
* Its journey seeds INSTANCE-wide state (the operator key's presence and the
* provider-health ledger the tri-state folds) and mints a consent receipt on
* the account, so it cannot share one with a journey that asserts a module
* map or a tile order.
*/
export const E2E_SETUP_AI_OFFER = {
email: "e2e-setup-ai-offer@healthlog.test",
username: "e2e-setup-ai-offer",
password: "Ao6!Zd3fB9qMv2Rk",
role: "USER",
} as const;
export const SETUP_BP_STORAGE_STATE_PATH = resolve(
process.cwd(),
"e2e/setup/storageStateSetupBp.json",
@@ -511,6 +526,10 @@ export const SETUP_LOCALE_STORAGE_STATE_PATH = resolve(
process.cwd(),
"e2e/setup/storageStateSetupLocale.json",
);
export const SETUP_AI_OFFER_STORAGE_STATE_PATH = resolve(
process.cwd(),
"e2e/setup/storageStateSetupAiOffer.json",
);
const SETUP_FLOW_ACCOUNTS = [
E2E_SETUP_BP,
@@ -519,6 +538,7 @@ const SETUP_FLOW_ACCOUNTS = [
E2E_SETUP_CHILD,
E2E_SETUP_A11Y,
E2E_SETUP_LOCALE,
E2E_SETUP_AI_OFFER,
] as const;
async function hashPassword(password: string): Promise<string> {
@@ -1261,6 +1281,7 @@ export default async function globalSetup(config: FullConfig): Promise<void> {
await capture(E2E_SETUP_VISIT, SETUP_VISIT_STORAGE_STATE_PATH);
await capture(E2E_SETUP_A11Y, SETUP_A11Y_STORAGE_STATE_PATH);
await capture(E2E_SETUP_LOCALE, SETUP_LOCALE_STORAGE_STATE_PATH);
await capture(E2E_SETUP_AI_OFFER, SETUP_AI_OFFER_STORAGE_STATE_PATH);
await capture(E2E_SETUP_CHILD, SETUP_CHILD_STORAGE_STATE_PATH);
await pool.query(
`UPDATE users SET totp_confirmed_at = $2 WHERE username = $1`,