test: tighten two checks in the persisted-field harness (closes #379)
The check that every swept field is driven both truthy and falsy now counts only `hostile` and `falsy` values; a `hostileRestore` value reaches only the views its entry names. The stale index 5 moves into `hostile` for `selectedWallet` and `selectedAddress`, as each field's one value still truthy after the floor. Each `hostileRestore` entry now names its views and declares whether the boot lands on them or falls back to Home, and the test asserts it, so an entry that stops reaching its renderer goes red. The file's header and the README restate the remaining limits as built and say which boots are held to where the popup lands. Model: opus-5-5
This commit is contained in:
@@ -1263,14 +1263,21 @@ path rather than on the home screen. Read the claim narrowly, as that file
|
|||||||
states it: what those boots prove is no structural dereference on the code paths
|
states it: what those boots prove is no structural dereference on the code paths
|
||||||
a WHOLLY-CORRUPTED PROFILE takes, which is not every path a stored record takes.
|
a WHOLLY-CORRUPTED PROFILE takes, which is not every path a stored record takes.
|
||||||
Not driven: any pairing of values the four slots do not produce, a view only
|
Not driven: any pairing of values the four slots do not produce, a view only
|
||||||
forward navigation opens, anything behind a click, and everything a healthy
|
forward navigation opens, anything behind a click or a timer, and, of what a
|
||||||
profile reaches. Within that boundary the verdict is unconditional — if one of
|
healthy profile reaches, anything beyond its boot onto each view the popup can
|
||||||
those boots leaves the popup unhealthy or off the view it stored, `make check`
|
reopen onto. The fields the router does not read share a slot on each boot, so
|
||||||
fails, including when it takes two corrupted fields at once, because the verdict
|
one of them truthy while another is falsy is reached only where the falsy slot
|
||||||
is the combined boot and the per-field re-boot that names a culprit can only
|
pairs a field that cannot be falsy with one that is. Within that boundary the
|
||||||
decorate the message. So does a field that gains a floor while its row still
|
verdict is unconditional — if one of those boots leaves the popup unhealthy,
|
||||||
claims it has none, and so does a field added to `PERSISTED_FIELDS` with no row
|
`make check` fails, including when it takes two corrupted fields at once,
|
||||||
at all. The per-field justification that used to live in the header of
|
because the verdict is the combined boot and the per-field re-boot that names a
|
||||||
|
culprit can only decorate the message. The combined boot must also land on the
|
||||||
|
view it stored, and each value driven only onto the restore path must land on
|
||||||
|
its view, or fall back to Home, as its row declares; a field the router reads
|
||||||
|
can legitimately change which view renders, so its own sweep is held to health
|
||||||
|
alone. `make check` also fails on a field that gains a floor while its row still
|
||||||
|
claims it has none, and on a field added to `PERSISTED_FIELDS` with no row at
|
||||||
|
all. The per-field justification that used to live in the header of
|
||||||
`src/shared/stateSchema.js` shipped a false claim in three consecutive changes,
|
`src/shared/stateSchema.js` shipped a false claim in three consecutive changes,
|
||||||
each caught only by a reviewer re-deriving thirty fields by hand.
|
each caught only by a reviewer re-deriving thirty fields by hand.
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,20 @@ then continue tagging as milestones land.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-07: Two holes in what `tests/persistedFieldContract.test.js` checks
|
||||||
|
are closed ([#379](https://git.eeqj.de/sneak/AutistMask/issues/379)). The
|
||||||
|
check that every swept field is driven both truthy and falsy counts only
|
||||||
|
`hostile` and `falsy` values, which the sweep drives onto every restorable
|
||||||
|
view, and no longer a `hostileRestore` value, which reaches only the views its
|
||||||
|
entry names; the stale index 5 moves into `hostile` for `selectedWallet` and
|
||||||
|
`selectedAddress`, as the one value of either still truthy after the floor.
|
||||||
|
Each `hostileRestore` entry declares whether its boot lands on its view or
|
||||||
|
falls back to Home, and is held to it. The limits that remain, fields that
|
||||||
|
share a slot on one boot and anything no stored record reaches by itself, are
|
||||||
|
restated as built in the file's header and the README, which now also say
|
||||||
|
which boots are held to where the popup lands and that a healthy profile is
|
||||||
|
booted onto every restorable view.
|
||||||
|
|
||||||
- 2026-10-07: Pre-1.0 security review of the extension
|
- 2026-10-07: Pre-1.0 security review of the extension
|
||||||
([#383](https://git.eeqj.de/sneak/AutistMask/issues/383)), reading the tree at
|
([#383](https://git.eeqj.de/sneak/AutistMask/issues/383)), reading the tree at
|
||||||
`99292b9` for key handling, the DEBUG mode policy, and what the background
|
`99292b9` for key handling, the DEBUG mode policy, and what the background
|
||||||
|
|||||||
@@ -49,22 +49,37 @@
|
|||||||
// every path a stored record takes, and the difference is the whole of what
|
// every path a stored record takes, and the difference is the whole of what
|
||||||
// this file does not cover:
|
// this file does not cover:
|
||||||
//
|
//
|
||||||
// - Only the values in the table, in the SLOT arrangement below: four value
|
// - Only the values in the table, in the SLOT arrangement below. On the
|
||||||
// combinations per view, not the product of twelve fields. A dereference
|
// restore path the twelve fields the router does not read are corrupted
|
||||||
// reached only under a pairing no slot produces is not driven at all.
|
// together, every field on the same slot, so a view gets four value
|
||||||
// - Only what a stored record reaches by ITSELF. A view only forward
|
// combinations of them, not their product. A branch entered only when one
|
||||||
// navigation opens, and anything behind a click, is not driven.
|
// of them is truthy and another falsy is reached only where the falsy
|
||||||
// - Nothing about the paths a HEALTHY profile takes, which is most of the
|
// slot happens to pair a field that cannot be falsy with one that is.
|
||||||
// popup. This file is a floor under one defect class, not a proof about
|
// Each field the router reads is corrupted alone, over an otherwise
|
||||||
// the renderers.
|
// well-formed record.
|
||||||
|
// - Only what a stored record reaches by ITSELF, as the boot renders it. A
|
||||||
|
// view only forward navigation opens, anything behind a click, and
|
||||||
|
// anything behind a timer (bootPopup() records every interval, and this
|
||||||
|
// file never runs one) is not driven.
|
||||||
|
// - Of the paths a HEALTHY profile takes, only its boot onto each
|
||||||
|
// restorable view ("the base profile the sweep corrupts" below). The rest
|
||||||
|
// of the popup is not covered: this file is a floor under one defect
|
||||||
|
// class, not a proof about the renderers.
|
||||||
//
|
//
|
||||||
// Within that boundary it is unconditional: if one of these boots leaves the
|
// Within that boundary it is unconditional: if a boot that corrupts a field
|
||||||
// popup unhealthy or off the view it stored, this file goes red — including
|
// leaves the popup unhealthy, this file goes red — including when it takes
|
||||||
// when it takes two corrupted fields at once, because the verdict is the
|
// two corrupted fields at once, because the verdict is the combined boot
|
||||||
// combined boot itself and the per-field re-boot below can only decorate the
|
// itself and the per-field re-boot below can only decorate the message. That
|
||||||
// message. That last part is the one thing an earlier version got wrong: it
|
// last part is the one thing an earlier version got wrong: it asserted on the
|
||||||
// asserted on the per-field list, so an observed dead popup that no single
|
// per-field list, so an observed dead popup that no single field reproduced
|
||||||
// field reproduced was reported green.
|
// was reported green.
|
||||||
|
//
|
||||||
|
// Where the popup lands is held for some of those boots and not others. The
|
||||||
|
// combined boot must land on the view it stored, and each `hostileRestore`
|
||||||
|
// value must land on its view, or fall back to Home, as its entry declares.
|
||||||
|
// The boots in "a hostile routing value restoring onto" are held to health
|
||||||
|
// alone, because a value in a field the router reads legitimately changes
|
||||||
|
// which view renders; so are the boots onto Home, which store no view.
|
||||||
//
|
//
|
||||||
// Booting every field separately at every value would be several hundred boots
|
// Booting every field separately at every value would be several hundred boots
|
||||||
// and most of the suite's budget; this is forty-four. Widening it further is
|
// and most of the suite's budget; this is forty-four. Widening it further is
|
||||||
@@ -128,7 +143,11 @@ const sweptValues = (row) => [...row.hostile, ...(row.falsy || [])];
|
|||||||
// list short and pointed. `floorOnly` is extra values checked against the
|
// list short and pointed. `floorOnly` is extra values checked against the
|
||||||
// floor alone, which is pure and free. `hostileRestore` is extra values driven
|
// floor alone, which is pure and free. `hostileRestore` is extra values driven
|
||||||
// through the restore path only, for a value that means nothing until a
|
// through the restore path only, for a value that means nothing until a
|
||||||
// particular branch's gate has let it past.
|
// particular branch's gate has let it past. Each of its entries names the
|
||||||
|
// `views` it is driven onto and declares whether the boot lands on them
|
||||||
|
// (`restored: true`) or falls back to Home (`restored: false`), so a value
|
||||||
|
// written for one renderer cannot stop reaching it unnoticed. A value that
|
||||||
|
// lands on some views and not others is one entry per outcome.
|
||||||
//
|
//
|
||||||
// `falsy` is the other POLARITY of a swept field, driven for the same reason.
|
// `falsy` is the other POLARITY of a swept field, driven for the same reason.
|
||||||
// It is not a value src/ never writes — for three of these fields it is the
|
// It is not a value src/ never writes — for three of these fields it is the
|
||||||
@@ -280,28 +299,56 @@ const CONTRACT = [
|
|||||||
kind: KIND.SCALAR,
|
kind: KIND.SCALAR,
|
||||||
// The prototype members are the whole point: `wallets["map"]` is
|
// The prototype members are the whole point: `wallets["map"]` is
|
||||||
// TRUTHY, so hasValidAddress()'s `&&` does not short-circuit and
|
// TRUTHY, so hasValidAddress()'s `&&` does not short-circuit and
|
||||||
// `.addresses[…]` throws. A stale INTEGER is the safe case.
|
// `.addresses[…]` throws. A stale INTEGER is the safe case and has to
|
||||||
hostile: ["map", "__proto__", { a: 1 }],
|
// stay so. 5 is one, out of range for the one wallet in the profile,
|
||||||
|
// and it is also this field's truthy polarity: every other value here
|
||||||
|
// comes back from the floor as null.
|
||||||
|
hostile: ["map", "__proto__", { a: 1 }, 5],
|
||||||
floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true],
|
floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true],
|
||||||
holds: isIndexOrNull,
|
holds: isIndexOrNull,
|
||||||
// SCALAR, and swept anyway: the restore path is precisely why this
|
// SCALAR, and swept anyway: the restore path is precisely why this
|
||||||
// field gained a floor, so the sweep is the regression guard on it.
|
// field gained a floor, so the sweep is the regression guard on it.
|
||||||
alsoSweep: true,
|
alsoSweep: true,
|
||||||
routes: true,
|
routes: true,
|
||||||
// A stale INTEGER index, which reaches the restore path by a different
|
// Comes back from the floor as null, which hasValidAddress() reads as
|
||||||
// route from the prototype members above — falsy or out of range
|
// nothing selected: the popup falls back to Home on the five views
|
||||||
// rather than truthy — and has to keep being the safe case.
|
// that need an address, and lands on the other six.
|
||||||
hostileRestore: [{ value: "length" }, { value: 5 }],
|
hostileRestore: [
|
||||||
|
{
|
||||||
|
value: "length",
|
||||||
|
views: [
|
||||||
|
"address",
|
||||||
|
"address-token",
|
||||||
|
"receive",
|
||||||
|
"transaction",
|
||||||
|
"confirm-tx",
|
||||||
|
],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "length",
|
||||||
|
views: [
|
||||||
|
"main",
|
||||||
|
"settings",
|
||||||
|
"settings-addtoken",
|
||||||
|
"wait-tx",
|
||||||
|
"success-tx",
|
||||||
|
"error-tx",
|
||||||
|
],
|
||||||
|
restored: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "selectedAddress",
|
field: "selectedAddress",
|
||||||
kind: KIND.SCALAR,
|
kind: KIND.SCALAR,
|
||||||
hostile: ["map", "__proto__", { a: 1 }],
|
// 5 for the same reason as in selectedWallet: a stale index, and the
|
||||||
|
// one value here still truthy after the floor.
|
||||||
|
hostile: ["map", "__proto__", { a: 1 }, 5],
|
||||||
floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true],
|
floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true],
|
||||||
holds: isIndexOrNull,
|
holds: isIndexOrNull,
|
||||||
alsoSweep: true,
|
alsoSweep: true,
|
||||||
routes: true,
|
routes: true,
|
||||||
hostileRestore: [{ value: 5 }],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "currentView",
|
field: "currentView",
|
||||||
@@ -325,7 +372,9 @@ const CONTRACT = [
|
|||||||
// container shapes below onto every restorable view; hostileRestore
|
// container shapes below onto every restorable view; hostileRestore
|
||||||
// adds the records that PASS a branch's gate and then hand its
|
// adds the records that PASS a branch's gate and then hand its
|
||||||
// renderer something it dereferences, which is where the entries are
|
// renderer something it dereferences, which is where the entries are
|
||||||
// actually decided.
|
// actually decided. The guard refuses each single-view record below,
|
||||||
|
// so each is declared to fall back to Home: one that started landing
|
||||||
|
// would be reaching the renderer it was written against.
|
||||||
hostile: [42, "notarecord", { a: 1 }, [1, 2]],
|
hostile: [42, "notarecord", { a: 1 }, [1, 2]],
|
||||||
// `structuredClone(saved.viewData || {})`: the container is never falsy
|
// `structuredClone(saved.viewData || {})`: the container is never falsy
|
||||||
// in state whatever was stored, so no `!state.viewData` branch exists to
|
// in state whatever was stored, so no `!state.viewData` branch exists to
|
||||||
@@ -334,11 +383,20 @@ const CONTRACT = [
|
|||||||
hostileRestore: [
|
hostileRestore: [
|
||||||
// success-tx passes on `data.hash`, and renderSuccess() then calls
|
// success-tx passes on `data.hash`, and renderSuccess() then calls
|
||||||
// toAddressHtml(d.to) -> addressTitle() -> address.toLowerCase().
|
// toAddressHtml(d.to) -> addressTitle() -> address.toLowerCase().
|
||||||
{ value: { hash: "0x1" }, views: ["success-tx"] },
|
{
|
||||||
{ value: { hash: "0x1", to: 42 }, views: ["success-tx"] },
|
value: { hash: "0x1" },
|
||||||
|
views: ["success-tx"],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: { hash: "0x1", to: 42 },
|
||||||
|
views: ["success-tx"],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: { hash: "0x1", to: ADDRESS, decoded: { details: 7 } },
|
value: { hash: "0x1", to: ADDRESS, decoded: { details: 7 } },
|
||||||
views: ["success-tx"],
|
views: ["success-tx"],
|
||||||
|
restored: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: {
|
value: {
|
||||||
@@ -347,12 +405,25 @@ const CONTRACT = [
|
|||||||
decoded: { details: [{ address: 42 }] },
|
decoded: { details: [{ address: 42 }] },
|
||||||
},
|
},
|
||||||
views: ["success-tx"],
|
views: ["success-tx"],
|
||||||
|
restored: false,
|
||||||
},
|
},
|
||||||
// error-tx passes on `data.message`, same dereference.
|
// error-tx passes on `data.message`, same dereference.
|
||||||
{ value: { message: "boom" }, views: ["error-tx"] },
|
{
|
||||||
{ value: { message: "boom", to: 42 }, views: ["error-tx"] },
|
value: { message: "boom" },
|
||||||
|
views: ["error-tx"],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: { message: "boom", to: 42 },
|
||||||
|
views: ["error-tx"],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
// transaction passes on `data.tx`.
|
// transaction passes on `data.tx`.
|
||||||
{ value: { tx: { hash: "0x1" } }, views: ["transaction"] },
|
{
|
||||||
|
value: { tx: { hash: "0x1" } },
|
||||||
|
views: ["transaction"],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: {
|
value: {
|
||||||
tx: {
|
tx: {
|
||||||
@@ -363,9 +434,14 @@ const CONTRACT = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
views: ["transaction"],
|
views: ["transaction"],
|
||||||
|
restored: false,
|
||||||
},
|
},
|
||||||
// confirm-tx passes on `data.pendingTx`.
|
// confirm-tx passes on `data.pendingTx`.
|
||||||
{ value: { pendingTx: { amount: "1" } }, views: ["confirm-tx"] },
|
{
|
||||||
|
value: { pendingTx: { amount: "1" } },
|
||||||
|
views: ["confirm-tx"],
|
||||||
|
restored: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: {
|
value: {
|
||||||
pendingTx: {
|
pendingTx: {
|
||||||
@@ -376,6 +452,7 @@ const CONTRACT = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
views: ["confirm-tx"],
|
views: ["confirm-tx"],
|
||||||
|
restored: false,
|
||||||
},
|
},
|
||||||
// wait-tx passes on `pendingWait.hash`; restoreWait() has checked
|
// wait-tx passes on `pendingWait.hash`; restoreWait() has checked
|
||||||
// the fields below it since it was written, and this is the
|
// the fields below it since it was written, and this is the
|
||||||
@@ -388,11 +465,13 @@ const CONTRACT = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
views: ["wait-tx"],
|
views: ["wait-tx"],
|
||||||
|
restored: false,
|
||||||
},
|
},
|
||||||
// A record that passes EVERY branch's gate at once, driven onto
|
// A record that passes EVERY branch's gate at once, driven onto
|
||||||
// every restorable view: a branch a view does not read must stay
|
// every restorable view: a branch a view does not read must stay
|
||||||
// one it does not read, and each renderer must survive the fields
|
// one it does not read. The six views with no viewData branch
|
||||||
// another branch left behind.
|
// render it, and must survive the fields every branch left behind;
|
||||||
|
// on the five that have one, the guard refuses it.
|
||||||
{
|
{
|
||||||
value: {
|
value: {
|
||||||
hash: "0x1",
|
hash: "0x1",
|
||||||
@@ -401,6 +480,32 @@ const CONTRACT = [
|
|||||||
pendingTx: { amount: "1" },
|
pendingTx: { amount: "1" },
|
||||||
pendingWait: { hash: "0x1" },
|
pendingWait: { hash: "0x1" },
|
||||||
},
|
},
|
||||||
|
views: [
|
||||||
|
"main",
|
||||||
|
"address",
|
||||||
|
"address-token",
|
||||||
|
"receive",
|
||||||
|
"settings",
|
||||||
|
"settings-addtoken",
|
||||||
|
],
|
||||||
|
restored: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: {
|
||||||
|
hash: "0x1",
|
||||||
|
message: "boom",
|
||||||
|
tx: { hash: "0x1" },
|
||||||
|
pendingTx: { amount: "1" },
|
||||||
|
pendingWait: { hash: "0x1" },
|
||||||
|
},
|
||||||
|
views: [
|
||||||
|
"confirm-tx",
|
||||||
|
"transaction",
|
||||||
|
"wait-tx",
|
||||||
|
"success-tx",
|
||||||
|
"error-tx",
|
||||||
|
],
|
||||||
|
restored: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -583,6 +688,11 @@ const HEALTHY = { errors: [], blank: false };
|
|||||||
// set is all-truthy by construction, so without a falsy slot a dereference
|
// set is all-truthy by construction, so without a falsy slot a dereference
|
||||||
// behind `if (!state.x)` is never reached on the boot that corrupts x — the
|
// behind `if (!state.x)` is never reached on the boot that corrupts x — the
|
||||||
// same falsy-collapse blind spot the fields below were floored for.
|
// same falsy-collapse blind spot the fields below were floored for.
|
||||||
|
//
|
||||||
|
// Only `hostile` and `falsy` values count. Those go through the sweep below,
|
||||||
|
// which covers every restorable view; a `hostileRestore` entry is driven onto
|
||||||
|
// only the views it names, so a polarity it alone supplied might reach a single
|
||||||
|
// renderer.
|
||||||
describe("both polarities of every swept field are driven", () => {
|
describe("both polarities of every swept field are driven", () => {
|
||||||
const FALSY_STORED = [0, "", false, null];
|
const FALSY_STORED = [0, "", false, null];
|
||||||
const floored = (field, value) =>
|
const floored = (field, value) =>
|
||||||
@@ -606,10 +716,9 @@ describe("both polarities of every swept field are driven", () => {
|
|||||||
test(`${row.field}: truthy and falsy`, () => {
|
test(`${row.field}: truthy and falsy`, () => {
|
||||||
// What the boots below actually drive, floored the way a renderer
|
// What the boots below actually drive, floored the way a renderer
|
||||||
// sees it — not what the row says it drives.
|
// sees it — not what the row says it drives.
|
||||||
const driven = [
|
const driven = sweptValues(row).map((value) =>
|
||||||
...sweptValues(row),
|
floored(row.field, value),
|
||||||
...(row.hostileRestore || []).map((entry) => entry.value),
|
);
|
||||||
].map((value) => floored(row.field, value));
|
|
||||||
|
|
||||||
expect({
|
expect({
|
||||||
truthy: driven.some((value) => Boolean(value)),
|
truthy: driven.some((value) => Boolean(value)),
|
||||||
@@ -701,16 +810,19 @@ function restoringOnto(view, extra) {
|
|||||||
|
|
||||||
// A boot that RESTORED is healthy and landed on the view it stored, rather
|
// A boot that RESTORED is healthy and landed on the view it stored, rather
|
||||||
// than falling back to Home — which a healthy boot also does, and which would
|
// than falling back to Home — which a healthy boot also does, and which would
|
||||||
// let a sweep pass by never running the renderer it is aimed at.
|
// let a sweep pass by never running the renderer it is aimed at. `blank` is
|
||||||
|
// reported as bootHealth() reports it, so a boot expected to fall back is still
|
||||||
|
// held to putting something on screen.
|
||||||
async function restoredHealth(profile, view) {
|
async function restoredHealth(profile, view) {
|
||||||
const env = await bootPopup(profile);
|
const env = await bootPopup(profile);
|
||||||
return {
|
return {
|
||||||
errors: env.pageErrors,
|
errors: env.pageErrors,
|
||||||
|
blank: env.visibleViews().length === 0,
|
||||||
restored: env.visibleViews().includes(view),
|
restored: env.visibleViews().includes(view),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const RESTORED = { errors: [], restored: true };
|
const RESTORED = { ...HEALTHY, restored: true };
|
||||||
|
|
||||||
describe("the base profile the sweep corrupts", () => {
|
describe("the base profile the sweep corrupts", () => {
|
||||||
for (const view of RESTORABLE_VIEWS) {
|
for (const view of RESTORABLE_VIEWS) {
|
||||||
@@ -865,20 +977,25 @@ describe("every field the router does not read, corrupted at once, onto", () =>
|
|||||||
|
|
||||||
// The values that only mean something on the restore path: a viewData that
|
// The values that only mean something on the restore path: a viewData that
|
||||||
// PASSES a branch's gate and then hands its renderer something dereferenced,
|
// PASSES a branch's gate and then hands its renderer something dereferenced,
|
||||||
// and the index values whose route through hasValidAddress() differs from the
|
// and a selectedWallet the floor turns into nothing selected. Each boot is held
|
||||||
// row's own hostile set.
|
// to health and to where its entry says it lands, on its view or back on Home,
|
||||||
|
// so a value written for one renderer cannot stop reaching it and still pass.
|
||||||
describe("a restore-only hostile value onto", () => {
|
describe("a restore-only hostile value onto", () => {
|
||||||
for (const row of CONTRACT) {
|
for (const row of CONTRACT) {
|
||||||
for (const entry of row.hostileRestore || []) {
|
for (const entry of row.hostileRestore || []) {
|
||||||
for (const view of entry.views || RESTORABLE_VIEWS) {
|
for (const view of entry.views) {
|
||||||
test(`${view}: ${row.field} = ${JSON.stringify(
|
test(`${view}: ${row.field} = ${JSON.stringify(
|
||||||
entry.value,
|
entry.value,
|
||||||
)}`, async () => {
|
)}`, async () => {
|
||||||
await expect(
|
await expect(
|
||||||
bootHealth(
|
restoredHealth(
|
||||||
restoringOnto(view, { [row.field]: entry.value }),
|
restoringOnto(view, { [row.field]: entry.value }),
|
||||||
|
view,
|
||||||
),
|
),
|
||||||
).resolves.toEqual(HEALTHY);
|
).resolves.toEqual({
|
||||||
|
...HEALTHY,
|
||||||
|
restored: entry.restored,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user