Skip to content

Commit c7e4287

Browse files
test(events): harden public-SSE field test + clarify state-key comment
Addresses code-review minor findings on 46aed25: 1. serviceType mapping (r.type ?? r.serviceType) only tested the type:1 path. Add a fallback case ({serviceType:2}, no type) and a type:0 case that locks in ?? (not ||) semantics so a valid 0 service type is not dropped. 2. The naive balanced-brace/bracket extractors silently truncate if a brace or bracket ever lands inside a string/regex in a target. Add mustExtract(): it asserts each extracted block ends with its closing token AND contains a known last field (baselineAtTest / activeRunNumber / next_in_ms), turning a mis-extraction into a loud, clear failure instead of a misleading field-missing assertion. 3. server.js PUBLIC_STATE_KEYS comment understated the audit — reword to note a subset (testRun/runMode/runPlanId/pricingMode) is also read by public.html. npm test: 57 assertions in the new suite, full suite green.
1 parent 46aed25 commit c7e4287

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1892,7 +1892,8 @@ function _redactPublicError(v, max = 200) {
18921892
// Keep only the state fields /live actually reads. Strips wallet, balance*,
18931893
// spent*, MNEMONIC-derived data, errorMessage internals, and any counter or
18941894
// progress field the public surfaces never consume.
1895-
// Each key below is verified read by live.html:
1895+
// Each key below is verified read by live.html (and a subset — testRun /
1896+
// runMode / runPlanId / pricingMode — also by public.html):
18961897
// - status / totalNodes / testRun / runMode / runPlanId / pricingMode /
18971898
// activeRunNumber are read directly off _liveState.
18981899
// - baselineMbps / baselineHistory feed computeBaselineAvg(_liveState) for

test/public-sse-fields.test.js

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,45 @@ function extractArrayLiteral(s, name) {
6161
return s.slice(start, j); // "[ ... ]"
6262
}
6363

64+
// The extractors above are naive char counters: a brace/bracket inside a
65+
// string or regex literal in the target would silently truncate the slice and
66+
// turn into a confusing "field missing" assertion downstream. Guard against
67+
// that by asserting the extracted block ends with its expected closing token
68+
// AND still contains a known last field — so a mis-extraction throws loudly.
69+
function mustExtract(extracted, what, endToken, sentinel) {
70+
const trimmed = extracted.trimEnd();
71+
if (!trimmed.endsWith(endToken)) {
72+
throw new Error(`mis-extracted ${what}: expected it to end with "${endToken}" ` +
73+
`but got "...${trimmed.slice(-40)}" (a brace/bracket inside a string or ` +
74+
`regex likely truncated the balanced-token scan)`);
75+
}
76+
if (!extracted.includes(sentinel)) {
77+
throw new Error(`mis-extracted ${what}: expected it to contain "${sentinel}" ` +
78+
`(extraction stopped early before the last field)`);
79+
}
80+
return extracted;
81+
}
82+
6483
// sanitizePublicResult closes over _redactPublicError; provide a stub so the
6584
// extracted fn runs in isolation.
6685
const sandbox = {
6786
_redactPublicError: (v, max = 200) => (v == null ? null : String(v).slice(0, max)),
6887
console,
6988
};
7089
vm.createContext(sandbox);
71-
vm.runInContext(extractFn(src, 'sanitizePublicResult'), sandbox);
72-
vm.runInContext('var PUBLIC_STATE_KEYS = ' + extractArrayLiteral(src, 'PUBLIC_STATE_KEYS') + ';', sandbox);
90+
vm.runInContext(
91+
mustExtract(extractFn(src, 'sanitizePublicResult'), 'sanitizePublicResult', '}', 'baselineAtTest'),
92+
sandbox);
93+
vm.runInContext('var PUBLIC_STATE_KEYS = ' +
94+
mustExtract(extractArrayLiteral(src, 'PUBLIC_STATE_KEYS'), 'PUBLIC_STATE_KEYS', ']', 'activeRunNumber') +
95+
';', sandbox);
7396
// sanitizeForPublic depends on sanitizePublicResult / sanitizePublicState /
7497
// _redactPublicError. We only exercise its top-level field forwarding here, so
7598
// provide a passthrough sanitizePublicState; sanitizePublicResult is real.
7699
vm.runInContext('var sanitizePublicState = (s) => s;', sandbox);
77-
vm.runInContext(extractFn(src, 'sanitizeForPublic'), sandbox);
100+
vm.runInContext(
101+
mustExtract(extractFn(src, 'sanitizeForPublic'), 'sanitizeForPublic', '}', 'next_in_ms'),
102+
sandbox);
78103

79104
const KEPT_RESULT_FIELDS = [
80105
'address', 'moniker', 'serviceType', 'countryCode', 'city', 'actualMbps',
@@ -127,6 +152,23 @@ console.log('[1] sanitizePublicResult keeps consumed fields, drops 6 unused');
127152
}
128153
}
129154

155+
// serviceType mapping is `r.type ?? r.serviceType` — exercise the fallback
156+
// branch (no type) and the `?? not ||` distinction (type: 0 is a real value).
157+
console.log('[1b] serviceType uses r.type ?? r.serviceType (fallback + nullish)');
158+
{
159+
// (a) no `type` at all → falls back to r.serviceType.
160+
sandbox._row = { address: 'sent1a', serviceType: 2 };
161+
const safe = vm.runInContext('sanitizePublicResult(_row)', sandbox);
162+
ok(safe.serviceType === 2, 'no type → falls back to r.serviceType (got ' + safe.serviceType + ')');
163+
}
164+
{
165+
// (b) type: 0 is a valid service type — `??` keeps it; `||` would wrongly
166+
// fall through to serviceType. Lock in `??` semantics.
167+
sandbox._row = { address: 'sent1b', type: 0, serviceType: 2 };
168+
const safe = vm.runInContext('sanitizePublicResult(_row)', sandbox);
169+
ok(safe.serviceType === 0, 'type:0 is kept (?? not ||) (got ' + safe.serviceType + ')');
170+
}
171+
130172
// ─── 2. PUBLIC_STATE_KEYS ─────────────────────────────────────────────────────
131173
console.log('[2] PUBLIC_STATE_KEYS is exactly the 10 consumed keys');
132174
{

0 commit comments

Comments
 (0)