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)));
}
});
|