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 | // The resident bots' public banks — the lobby's bundled bootstrap set.
//
// Two pure decisions ship with it and both are driven here, black-box through
// the testlib façade (never src/):
//
// · `validPublicBank` — the shape gate the lobby runs over the constant
// before rendering a row. The six bundled entries must pass it, and the
// ways an entry can rot (half a link, a bare secret, an unbounded string)
// must each fail it — so a validator that failed open and data that
// drifted both go red.
//
// · `publicBanksAvailable` — the already-joined filter. A public bank the
// user holds lives in "banks you use" like any saved bank; offering it a
// second time in the public section would claim the section is a
// different kind of thing than a link, and it is not.
//
// The tokens are public capability URLs BY DESIGN (the bots approve everyone
// and gift a small start), so pinning them in a test leaks nothing.
import test from "node:test";
import assert from "node:assert/strict";
import * as L from "../test-dist/testlib.js";
const BANKS = [...L.PUBLIC_BANKS];
test("the bundled set is the six resident banks, every one a valid entry", () => {
assert.equal(BANKS.length, 6);
for (const b of BANKS) {
assert.ok(L.validPublicBank(b), `${b.name} must validate`);
}
assert.deepEqual(BANKS.map((b) => b.code), ["DUH", "MOR", "STR", "IEL", "ZMU", "BAL"]);
assert.deepEqual(BANKS.map((b) => b.name),
["Banca Duhului", "Banca Moroiului", "Banca Strigoiului",
"Banca Ielelor", "Banca Zmeului", "Banca Balaurului"]);
// six DIFFERENT banks: distinct tokens, distinct bankers
assert.equal(new Set(BANKS.map((b) => b.s)).size, 6);
assert.equal(new Set(BANKS.map((b) => L.linkBanker(b.s))).size, 6);
});
test("every bundled token is a WHOLE bank link — secret half and banker half", () => {
// §1: a link without the banker suffix is not a bank. A truncated paste of
// one of these constants must never survive a copy-edit of this file.
for (const b of BANKS) {
assert.ok(L.isBankLink(b.s), `${b.code} carries a banker half`);
const p = L.parseLink(b.s);
assert.equal(p.secret.length, 43);
assert.equal(p.banker.length, 43);
}
});
test("the validator refuses every way an entry can rot", () => {
const good = BANKS[0];
const okBut = (patch) => L.validPublicBank({ ...good, ...patch });
assert.ok(L.validPublicBank({ ...good }), "the control copy must pass");
// not an entry at all
assert.equal(L.validPublicBank(null), false);
assert.equal(L.validPublicBank(undefined), false);
// the token: missing, half a link (a bare secret is NOT a bank), garbage
assert.equal(okBut({ s: undefined }), false);
assert.equal(okBut({ s: L.linkSecret(good.s) }), false, "a bare secret must fail");
assert.equal(okBut({ s: good.s.slice(0, -1) }), false, "a truncated banker half must fail");
assert.equal(okBut({ s: "not a link" }), false);
// the display strings: absent, empty, unbounded (name 40, code 8, sym 8 —
// mirroring lib/protocol's MAX_NAME / MAX_SYM)
assert.equal(okBut({ name: undefined }), false);
assert.equal(okBut({ name: "" }), false);
assert.equal(okBut({ name: "n".repeat(41) }), false);
assert.ok(okBut({ name: "n".repeat(40) }), "40 is within bounds");
assert.equal(okBut({ code: "" }), false);
assert.equal(okBut({ code: "C".repeat(9) }), false);
assert.equal(okBut({ sym: "" }), false);
assert.equal(okBut({ sym: "s".repeat(9) }), false);
assert.equal(okBut({ sym: 7 }), false, "a non-string must fail");
});
test("available: joined banks leave the section, order and strangers untouched", () => {
const room = (s) => ({ s, label: "x", ts: 1 });
const all = [...L.publicBanksAvailable(L.PUBLIC_BANKS, [])];
assert.deepEqual(all.map((b) => b.code), ["DUH", "MOR", "STR", "IEL", "ZMU", "BAL"],
"an empty directory offers all six, in bundle order");
// joining two removes exactly those two — matched on the WHOLE token
const joined = [room(BANKS[1].s), room(BANKS[4].s)];
const left = [...L.publicBanksAvailable(L.PUBLIC_BANKS, joined)];
assert.deepEqual(left.map((b) => b.code), ["DUH", "STR", "IEL", "BAL"]);
// a directory full of OTHER banks removes nothing; malformed rows are inert
const strangers = [room("A".repeat(43) + "." + "B".repeat(43)), null, { label: "no s" }];
assert.equal(L.publicBanksAvailable(L.PUBLIC_BANKS, strangers).length, 6);
// half a link is a DIFFERENT bank (or none), never a match for the whole
assert.equal(L.publicBanksAvailable(L.PUBLIC_BANKS, [room(L.linkSecret(BANKS[0].s))]).length, 6,
"a bare secret in the directory must not shadow the bank");
// all six joined: the section has nothing left to offer
assert.equal(L.publicBanksAvailable(L.PUBLIC_BANKS, BANKS.map((b) => room(b.s))).length, 0);
// and the filter validates on the way out, so a rotten entry is dropped,
// not drawn — the lobby renders this function's answer verbatim
const rotten = [{ ...BANKS[0], name: "" }, BANKS[1]];
assert.deepEqual([...L.publicBanksAvailable(rotten, [])].map((b) => b.code), ["MOR"]);
// non-arrays answer emptiness, not a throw (the lobby must paint offline)
assert.equal(L.publicBanksAvailable(null, []).length, 0);
});
|