fix(folio): author spec weights to match testing intent

Revert the account saturation gate: it starved newAccountBalanceIsZero
once it tripped, and a magic account count is app-state tuning, not
intent. Instead weight the generators by what the properties need:
the transaction chain leads, account creation stays exercised, and
doubleTaps gets explicit weight everywhere because double-submission
idempotency is what the spec is testing for.
This commit is contained in:
pj committed 2026-06-05 18:05:49 +05:30
1 parent 0b34af7e7a
commit 691e2e985f
1 file changed
+11 -13
+11 -13
View File
@@ -10,7 +10,7 @@ import {
weighted, weighted,
whenRoute, whenRoute,
} from "@sanderling/spec"; } from "@sanderling/spec";
import { defaultActions } from "@sanderling/spec/defaults"; import { defaultActions, doubleTaps } from "@sanderling/spec/defaults";
import { import {
computeHomeTotalBalance, computeHomeTotalBalance,
parseTypedAmount, parseTypedAmount,
@@ -134,15 +134,8 @@ const login = actions(() => {
const accountNames = from(["Checking", "Savings", "Travel", "Emergency Fund", "Investments"]); const accountNames = from(["Checking", "Savings", "Travel", "Emergency Fund", "Investments"]);
// Saturation gate: a few accounts are enough to exercise every balance
// property. Without the gate the short add-account loop (2-3 steps, back to
// home) outcompetes the 5-step transaction chain at every re-draw and the
// run fills with account creation instead of transactions.
const ACCOUNTS_ENOUGH = 3;
const addAccount = whenRoute(route, ["home", "add-account"], () => { const addAccount = whenRoute(route, ["home", "add-account"], () => {
if (route.current === "home") { if (route.current === "home") {
if (accounts.current.length >= ACCOUNTS_ENOUGH) return [];
const btn = addAccountButton.current; const btn = addAccountButton.current;
return btn ? [Tap({ on: btn })] : []; return btn ? [Tap({ on: btn })] : [];
} }
@@ -181,11 +174,16 @@ export const properties = {
export const setup = login; export const setup = login;
// Targeted depth (addAccount / addTxn) drives the deep flows; defaultActions // Weights declare testing intent. The transaction chain is the focus: it is
// adds breadth so the fuzzer wanders the whole app and types edge-case values // the deepest flow and both balance properties observe it. Account creation
// into every field, stressing the balance invariants above. // stays in the mix because newAccountBalanceIsZero needs fresh accounts to
// fire. doubleTaps gets explicit weight on every screen because rapid
// double-submission is a failure mode these forms must be idempotent under.
// defaultActions adds breadth so the fuzzer wanders the whole app and types
// edge-case values into every field.
export const actionsRoot = weighted( export const actionsRoot = weighted(
[50, addAccount], [25, addAccount],
[30, addTxn], [45, addTxn],
[10, doubleTaps],
[20, defaultActions], [20, defaultActions],
); );