Skip to content

Commit 067eb93

Browse files
chore: improve chaos test resilience — rate limit handling and login fallback
Reduce API logins per iteration to 3 (within 5/min rate limit), add token injection fallback for UI login timeout, fix bank balance tracking to use API response values. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1f336de commit 067eb93

File tree

1 file changed

+37
-29
lines changed

1 file changed

+37
-29
lines changed

e2e/tests/iteration/chaos-cycle.spec.ts

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ async function opAdminDeposit(page: Page, state: TestState) {
418418
const depositAmount = randomInt(50, 500);
419419

420420
// Do the deposit via API since admin UI requires navigating to account detail
421-
await state.api.deposit(state.adminToken, account.id, depositAmount, `Chaos deposit ${RUN_ID}`);
422-
account.balance += depositAmount;
421+
const txn = await state.api.deposit(state.adminToken, account.id, depositAmount, `Chaos deposit ${RUN_ID}`);
422+
account.balance = Number(txn.balance_after);
423423

424424
state.operationLog.push(`API_DEPOSIT: account=${account.id} amount=${depositAmount} new_balance=${account.balance}`);
425425
}
@@ -428,20 +428,21 @@ async function opAdminWithdraw(page: Page, state: TestState) {
428428
if (state.bankAccounts.length === 0) return;
429429
const account = pick(state.bankAccounts);
430430

431-
if (account.balance <= 0) {
432-
state.operationLog.push(`SKIP_WITHDRAW: account=${account.id} balance=${account.balance}`);
431+
// Refresh balance from API before withdrawing
432+
const apiAccount = await state.api.getBankAccount(state.adminToken, account.id);
433+
const currentBalance = Number(apiAccount.current_balance ?? apiAccount.balance ?? 0);
434+
account.balance = currentBalance;
435+
436+
if (currentBalance <= 0) {
437+
state.operationLog.push(`SKIP_WITHDRAW: account=${account.id} balance=${currentBalance}`);
433438
return;
434439
}
435440

436-
const maxWithdraw = Math.min(account.balance, 200);
437-
const withdrawAmount = randomInt(1, maxWithdraw);
441+
const maxWithdraw = Math.min(currentBalance, 200);
442+
const withdrawAmount = randomInt(1, Math.floor(maxWithdraw));
438443

439-
const result = await state.api.withdraw(state.adminToken, account.id, withdrawAmount, `Chaos withdraw ${RUN_ID}`);
440-
if (result.balance_after !== undefined) {
441-
account.balance = result.balance_after;
442-
} else {
443-
account.balance -= withdrawAmount;
444-
}
444+
const txn = await state.api.withdraw(state.adminToken, account.id, withdrawAmount, `Chaos withdraw ${RUN_ID}`);
445+
account.balance = Number(txn.balance_after);
445446

446447
state.operationLog.push(`API_WITHDRAW: account=${account.id} amount=${withdrawAmount} balance=${account.balance}`);
447448
}
@@ -762,8 +763,10 @@ test.describe(`Chaos Cycle — Iteration ${ITERATION}`, () => {
762763
console.log(` Created borrower: ${borrowerEmail}`);
763764

764765
// Login as test users (stagger to respect 5/min rate limit — 5 req/min per IP)
765-
await page.waitForTimeout(15000);
766+
// Wait for rate limit window to clear from prior iterations
767+
await page.waitForTimeout(20000);
766768
const creditorLogin = await api.login(creditorEmail, "TestPass123!");
769+
await page.waitForTimeout(13000);
767770
const borrowerLogin = await api.login(borrowerEmail, "TestPass123!");
768771

769772
// Create bank accounts for both users
@@ -834,19 +837,27 @@ test.describe(`Chaos Cycle — Iteration ${ITERATION}`, () => {
834837
// ──────────────────────────────────────────────────────────────────────
835838
console.log("\nStep 2: Logging in via UI...");
836839

837-
// Set auth token directly via localStorage to avoid rate limiting on login endpoint
840+
// Login via UI form — use the actual login flow
838841
await page.goto("/login");
839-
await page.evaluate(
840-
([token]) => {
841-
localStorage.setItem("lendq_access_token", token);
842-
},
843-
[creditorLogin.access_token],
844-
);
845-
await page.goto("/dashboard");
842+
await page.getByLabel("Email Address").fill(creditorEmail);
843+
await page.getByLabel("Password").fill("TestPass123!");
844+
await page.getByRole("button", { name: "Sign In" }).click();
845+
846+
// Wait for navigation to dashboard (generous timeout for staging)
847+
try {
848+
await page.waitForURL("**/dashboard", { timeout: 30000 });
849+
} catch {
850+
// If rate limited, inject token directly as fallback
851+
console.log(" UI login timed out, falling back to token injection...");
852+
await page.evaluate(
853+
([token]) => localStorage.setItem("lendq_access_token", token),
854+
[creditorLogin.access_token],
855+
);
856+
await page.goto("/dashboard");
857+
}
846858

847-
// Wait for dashboard to load with generous timeout for staging
848859
await page.waitForSelector("[data-testid='metric-total-lent-out']", { timeout: 30000 });
849-
console.log(" Logged in successfully (via token injection)");
860+
console.log(" Logged in successfully");
850861

851862
// ──────────────────────────────────────────────────────────────────────
852863
// STEP 3: Perform 30 random operations
@@ -927,12 +938,9 @@ test.describe(`Chaos Cycle — Iteration ${ITERATION}`, () => {
927938
// ──────────────────────────────────────────────────────────────────────
928939
console.log("\nStep 6: Cleaning up test data...");
929940

930-
// Refresh admin token
931-
const freshAdminLogin = await api.login("admin@family.com", "password123");
932-
const freshAdminToken = freshAdminLogin.access_token;
933-
934-
await api.purgeUser(freshAdminToken, creditorUser.id);
935-
await api.purgeUser(freshAdminToken, borrowerUser.id);
941+
// Use existing admin token (avoid extra login for rate limit)
942+
await api.purgeUser(adminToken, creditorUser.id);
943+
await api.purgeUser(adminToken, borrowerUser.id);
936944
console.log(" Test users purged");
937945

938946
console.log(`\n=== ITERATION ${ITERATION} COMPLETE ===`);

0 commit comments

Comments
 (0)