mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
Merge branch 'folio-spec-attribution' into trial-merge
This commit is contained in:
commit
6a7764eac7
5 files changed
+169
-12
No files matched your search
@@ -107,6 +107,7 @@ function run(steps: { route: string | null; cards: CardReading[]; lastAction: un
|
||||
|
||||
const idle = { kind: "Tap", on: "testTag:AccountCard" };
|
||||
const doubleSubmit = { kind: "DoubleTap", on: "testTag:AddTransactionScreen > testTag:TxnSubmit" };
|
||||
const submit = { kind: "Tap", on: "testTag:AddTransactionScreen > testTag:TxnSubmit" };
|
||||
|
||||
test("an un-laid-out Home no longer kills the counting invariant", () => {
|
||||
const trace = run([
|
||||
@@ -157,3 +158,45 @@ test("an un-laid-out Home does not close the counting window", () => {
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
// What keeps a healthy submit from ever arriving as a rise nobody paid for. The
|
||||
// reading banked here also resets the submit window, so a Home card list drawn
|
||||
// before the store caught up with a commit would bank stale counts, start the
|
||||
// next window empty, and leave the rise turning up with no budget to cover it.
|
||||
// The app cannot put that frame in front of the spec: submit() pops one entry,
|
||||
// so a commit lands back on the ledger it came from and the first Home reading
|
||||
// is a whole action later, with the submit still in the window when the rise
|
||||
// does show up.
|
||||
test("a submit landing on the ledger is still in the window when Home reads it", () => {
|
||||
const trace = run([
|
||||
{ route: "home", cards: [card("Checking", 0, "3")], lastAction: idle },
|
||||
{ route: "ledger", cards: [], lastAction: submit },
|
||||
{ route: "home", cards: [card("Checking", 5000, "4")], lastAction: idle },
|
||||
]);
|
||||
assert.equal(trace[2]?.submits, 1);
|
||||
assert.equal(
|
||||
committedTransactionsExceedSubmits({
|
||||
countsBefore: trace[1]?.counts ?? null,
|
||||
countsAfter: trace[2]?.counts ?? null,
|
||||
submitsInWindow: trace[2]?.submits ?? 0,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("and a double submit down that same path still convicts", () => {
|
||||
const trace = run([
|
||||
{ route: "home", cards: [card("Checking", 0, "3")], lastAction: idle },
|
||||
{ route: "ledger", cards: [], lastAction: doubleSubmit },
|
||||
{ route: "home", cards: [card("Checking", 10000, "5")], lastAction: idle },
|
||||
]);
|
||||
assert.equal(trace[2]?.submits, 1);
|
||||
assert.equal(
|
||||
committedTransactionsExceedSubmits({
|
||||
countsBefore: trace[1]?.counts ?? null,
|
||||
countsAfter: trace[2]?.counts ?? null,
|
||||
submitsInWindow: trace[2]?.submits ?? 0,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
@@ -521,6 +521,67 @@ test("a submit the runner could not confirm demands no balance move", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// The write finishes before AddTransactionViewModel navigates, but nothing
|
||||
// establishes that Home's total has re-rendered by the time the frame is read:
|
||||
// the store's flow re-emits on its own schedule and the destination composes off
|
||||
// whatever value it has. A total that has not caught up has not moved at all,
|
||||
// and an equality reads that as the app having ignored the amount.
|
||||
test("a commit the Home total has not caught up with is not a violation", () => {
|
||||
assert.equal(
|
||||
submitChangesBalanceByTypedAmount({
|
||||
route: "home",
|
||||
lastAction: { kind: "Tap", on: submitOn, applied: true },
|
||||
submitsInWindow: 1,
|
||||
typedAmount: 19600,
|
||||
prevTotalBalance: 220900,
|
||||
currTotalBalance: 220900,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
// What the bound gives up, and it is a real bug class: an app that moves the
|
||||
// balance by LESS than the amount typed. Nothing in this spec judges that any
|
||||
// more. It cannot be told apart from a total that has not caught up, and a check
|
||||
// that fires on both is not evidence about either.
|
||||
test("an under-move is no longer judged, which is the trade", () => {
|
||||
assert.equal(
|
||||
submitChangesBalanceByTypedAmount({
|
||||
route: "home",
|
||||
lastAction: { kind: "Tap", on: submitOn, applied: true },
|
||||
submitsInWindow: 1,
|
||||
typedAmount: 19600,
|
||||
prevTotalBalance: 0,
|
||||
currTotalBalance: 10000,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
// The witness measured on four recorded android runs, all four of which convict
|
||||
// here and nowhere else: the double tap moved the total by 6400 against 3200
|
||||
// typed. The bound has to keep every one of them.
|
||||
test("the measured double submit still fires under the bound", () => {
|
||||
for (const [prev, curr] of [
|
||||
[17952800, 17959200],
|
||||
[19796100, 19802500],
|
||||
[200000032904800, 200000032911200],
|
||||
]) {
|
||||
assert.equal(
|
||||
submitChangesBalanceByTypedAmount({
|
||||
route: "home",
|
||||
lastAction: { kind: "DoubleTap", on: submitOn, applied: true },
|
||||
submitsInWindow: 1,
|
||||
typedAmount: 3200,
|
||||
prevTotalBalance: prev ?? null,
|
||||
currTotalBalance: curr ?? null,
|
||||
}),
|
||||
false,
|
||||
`the double submit at ${prev} -> ${curr} stopped firing`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// relaunched: true is the runner saying its foreground guard restarted the app
|
||||
// after this action. The tap landed, so the window still counts it, but nobody
|
||||
// can promise the process lived long enough for the write to reach sqlite. A
|
||||
|
||||
@@ -131,7 +131,11 @@ test("the measured android transition chain no longer convicts at delta 0", () =
|
||||
);
|
||||
|
||||
// What the reset bought the old spec: the same landing, judged against a
|
||||
// window of one and a total the transition frame had already banked.
|
||||
// window of one and a total the transition frame had already banked. It
|
||||
// convicted on a delta of zero, and that shape cannot convict any more even
|
||||
// with the window reset back to one, because the property is a bound rather
|
||||
// than an equality. A balance that did not move is under any typed amount,
|
||||
// whether nothing was submitted or the total has not caught up yet.
|
||||
assert.equal(
|
||||
submitChangesBalanceByTypedAmount({
|
||||
route: "home",
|
||||
@@ -141,6 +145,20 @@ test("the measured android transition chain no longer convicts at delta 0", () =
|
||||
prevTotalBalance: 8691100,
|
||||
currTotalBalance: 8691100,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
|
||||
// The double tap it was always meant to catch is untouched by that: two
|
||||
// 33900 debits against one action still exceed the amount typed for it.
|
||||
assert.equal(
|
||||
submitChangesBalanceByTypedAmount({
|
||||
route: "home",
|
||||
lastAction: { ...phantomSubmit, kind: "DoubleTap" },
|
||||
submitsInWindow: 1,
|
||||
typedAmount: 33900,
|
||||
prevTotalBalance: 8691100,
|
||||
currTotalBalance: 8691100 - 67800,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user