bursa / client / test / quoteform.test.mjs
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
// The quote form's pure decisions, pinned against the two complaints its
// first human trader filed live: (1) the form quoted whatever pair its
// dropdowns woke up on instead of the pair the book was showing, and (2) a
// full re-render — the market ticks constantly, six bots re-quote — rebuilt
// the controls from defaults and clobbered what the person had picked. The
// model here is what makes a rebuild idempotent w.r.t. the person's input:
// same saved state in, same rendered state out, however often it runs.
//
// And the third honesty fix that ships with them: a currency whose charter
// this device hasn't read still CLAIMS a code inside its own id. The claim is
// shown as a claim (never as a fact, never as the raw banker pub) —
// claimedCode is the extraction the screen renders unverified.

import test from "node:test";
import assert from "node:assert/strict";
import * as L from "../test-dist/testlib.js";
import { ACTORS, CUR_BASE, CUR_QUOTE, PAIR } from "./vectors/independent.mjs";

// ---- the book's chosen pair -------------------------------------------------

test("the sticky pair holds while its book lives, and falls back honestly", () => {
  assert.equal(L.chosenPair([PAIR, "x|y"], PAIR), PAIR, "a live choice is kept");
  assert.equal(L.chosenPair([PAIR, "x|y"], "x|y"), "x|y");
  assert.equal(L.chosenPair([PAIR], "gone|pair"), PAIR, "a dead choice falls to the first pair");
  assert.equal(L.chosenPair([PAIR], null), PAIR, "no choice yet is the first pair");
  assert.equal(L.chosenPair([], PAIR), null, "an empty book chooses nothing");
});

// ---- select values that survive a rebuild -----------------------------------

test("a saved selection is kept while it is still offered", () => {
  const curs = [CUR_BASE, CUR_QUOTE];
  assert.equal(L.pickOption(CUR_QUOTE, curs, 0), CUR_QUOTE);
  assert.equal(L.pickOption("ask", ["ask", "bid"], 0), "ask");
});

test("a stale or absent selection falls to the default, never to nothing", () => {
  const curs = [CUR_BASE, CUR_QUOTE];
  assert.equal(L.pickOption("gone.CUR", curs, 0), CUR_BASE, "stale → the default index");
  assert.equal(L.pickOption(null, curs, 1), CUR_QUOTE, "unset → the default index");
  assert.equal(L.pickOption(null, [CUR_BASE], 1), CUR_BASE, "the index clamps");
  assert.equal(L.pickOption(CUR_BASE, [], 0), null, "no options → nil, not a ghost");
});

test("picking is idempotent — the round-trip a re-render depends on", () => {
  // what one render shows is what the next render reads back: pick(pick(x))
  // must equal pick(x) for every shape of x, or a tick would drift the form
  const curs = [CUR_BASE, CUR_QUOTE];
  for (const saved of [CUR_BASE, CUR_QUOTE, "gone.CUR", null]) {
    const once = L.pickOption(saved, curs, 1);
    assert.equal(L.pickOption(once, curs, 1), once, JSON.stringify(saved));
  }
});

// ---- the model: bound to the book, free behind the disclosure ---------------

test("with a book pair selected the form is BOUND to it", () => {
  const m = L.quoteModel(PAIR, false, null, null, [CUR_BASE, CUR_QUOTE]);
  assert.equal(m.mode, "bound");
  assert.equal(m.base, CUR_BASE);
  assert.equal(m.quote, CUR_QUOTE);
});

test("binding wins over any saved dropdown state — the book is looking at THIS pair", () => {
  const m = L.quoteModel(PAIR, false, CUR_QUOTE, CUR_BASE, [CUR_BASE, CUR_QUOTE]);
  assert.equal(m.mode, "bound");
  assert.equal(m.base, CUR_BASE, "the pair's own base, not the saved dropdown");
  assert.equal(m.quote, CUR_QUOTE);
});

test("binding does not need the banks open — the pair names its currencies itself", () => {
  // the curs list (open banks only) may be empty; the bound labels still say
  // which pair this is, and the UI says what's missing instead of going blank
  const m = L.quoteModel(PAIR, false, null, null, []);
  assert.equal(m.mode, "bound");
  assert.equal(m.base, CUR_BASE);
});

test("the disclosure frees the dropdowns; no book frees them too", () => {
  const curs = [CUR_BASE, CUR_QUOTE];
  const disclosed = L.quoteModel(PAIR, true, null, null, curs);
  assert.equal(disclosed.mode, "free");
  assert.equal(disclosed.base, CUR_BASE, "defaults to the first open bank");
  assert.equal(disclosed.quote, CUR_QUOTE, "…and the second, so the pair starts distinct");
  const bookless = L.quoteModel(null, false, null, null, curs);
  assert.equal(bookless.mode, "free");
});

test("free mode keeps the saved selections while they are offered, and only then", () => {
  const curs = [CUR_BASE, CUR_QUOTE];
  const kept = L.quoteModel(null, false, CUR_QUOTE, CUR_BASE, curs);
  assert.equal(kept.base, CUR_QUOTE, "a person's pick survives the rebuild");
  assert.equal(kept.quote, CUR_BASE);
  const stale = L.quoteModel(null, false, "gone.CUR", "gone.CUR", curs);
  assert.equal(stale.base, CUR_BASE, "a stale pick is not resurrected");
  assert.equal(stale.quote, CUR_QUOTE);
});

// ---- codes as claims --------------------------------------------------------

test("a currency id's claimed code is extracted — and is never the banker pub", () => {
  assert.equal(L.claimedCode(CUR_BASE), "DUH");
  assert.equal(L.claimedCode(CUR_QUOTE), "MOR");
  assert.notEqual(L.claimedCode(CUR_BASE), ACTORS.bankBPub);
  assert.ok(!String(L.claimedCode(CUR_BASE)).includes(ACTORS.bankBPub));
});

test("an id with no claim yields nil, never a crash and never a guess", () => {
  for (const bad of ["nodot", "trailingdot.", "", null, undefined, 7, {}]) {
    assert.equal(L.claimedCode(bad), null, JSON.stringify(bad ?? String(bad)));
  }
});

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/bursa.git