banana_split/tests
Kirill Pimenov 0c77cc4534
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
End to end test / e2e-test (push) Has been cancelled
Trivy scan / Analyze (push) Has been cancelled
Yarn tests / yarn (push) Has been cancelled
fix: correct reprint threshold display and de-duplicate the formula (#124)
* fix: correct reprint threshold display and de-duplicate the formula (F5)

Print.vue recomputed the recovery threshold as floor(total/2)+2 and stored
the *total* in a field misleadingly named `requiredShards`. The reprint
sheets therefore overstated how many more QR codes are needed (e.g. total=5
showed "need 4" instead of the correct 3) — misleading during recovery,
though the QR payloads themselves were always untouched.

Root cause: the threshold policy was duplicated. Share.vue computes
floor(total/2)+1; Print.vue re-implemented it and drifted. Extract a single
`defaultThreshold()` helper (src/util/shards.ts) and use it in both, so they
can never disagree again. Rename Print.vue's field to `totalShards` to match
what it actually holds.

Add unit tests for the helper, including the exact F5 case (total=5 -> 3) and
a strict-majority invariant across the whole 3..255 UI range.

* fix: reject fractional shard counts, clamp the remaining-code count

Review follow-ups on the reprint view.

`<input type="number">` only constrains the spinner, not the value: typing
"3.5" (or clearing the box, which `v-model.number` leaves as "") sailed
through the old `>= 3 && <= 255` check and reached defaultThreshold(),
producing fractional totals and remaining counts. Add `step="1"` for the
spinner and gate on a shared `isValidShardCount()` predicate that also
requires a whole number, so the input and the threshold policy keep agreeing
about what a shard count is.

`needMoreShards` compared with `!==`, so scanning more codes than announced
kept the scanner open and drove `remainingCodes` negative. Compare with `<`
and clamp the remainder at 0.

`threshold` returned 0 for "nothing entered yet", a valid-looking value that
could reach ShardInfo's required-shards prop. Return `undefined` instead and
guard the consuming block on it, making the not-yet-entered state explicit.

The `threshold` and validation paths are covered by shards.spec.ts; the
scanner-overshoot clamp is not, as the repo has no component-test harness yet.

* fix: apply the same shard-count validation to the generator

Share.vue's shard-count input had the defect Copilot flagged on Print.vue's:
no `step`, no integer check, and `totalShards` fed straight into
crypto.share() — a fractional count surfaced as an opaque secrets.js error
routed through the generic error hub, and an emptied box passed "" through.

Reuse `isValidShardCount()` and gate the generate button on it, matching the
existing `secretTooLong` pattern (disabled button plus an inline error span),
so both shard-count inputs now agree on what a shard count is.

* fix: stop rendering a threshold derived from an invalid shard count

Gating the generate button left the sentence above it still interpolating
defaultThreshold(totalShards) for values that had just been rejected: an
emptied field coerces to 0 and renders "Will require any 1 shards", and 3.5
renders 2. Give Share.vue's `requiredShards` the same contract as Print.vue's
`threshold` — `undefined` unless the count is usable — show an em dash in the
sentence, and guard both the generated-shards block and crypto.share() on it.

Also strengthen the threshold invariant test. Asserting only
`defaultThreshold(n) > n / 2` does not pin the policy: floor(n/2)+2, the very
formula this branch removed, satisfies it for every even n. Assert the
smallest strict majority instead, which floor(n/2)+1 alone satisfies.
2026-08-04 16:10:30 +02:00
..
e2e add end-to-end UI tests. Resolves #30 2021-11-17 11:21:00 +07:00
unit fix: correct reprint threshold display and de-duplicate the formula (#124) 2026-08-04 16:10:30 +02:00