Skip to content

Commit 870ab00

Browse files
committed
fix(brief): address 2 Greptile P2 inline comments on PR #3751
1. shared/brief-filter.js:115-134 — titleCase JSDoc accuracy. The function returns input unchanged when v is non-string or empty (preserving type), not always `string`. Updated `@param` to `unknown` and `@returns` to `string | unknown`. Clarified the guard-comment framing: the `|| 'General'` fallback at :384 runs BEFORE titleCase is invoked at out.push, so the type-preserving branch is never reached at the current call site — kept as defense-in-depth for future callers. 2. tests/digest-buildDigest-category-passthrough.test.mjs:55-78 — replaced brittle `indexOf('});')` with a brace-depth-tracking scanner that finds the matching close at the same depth. Old approach would silently truncate the search slice if a future refactor added a nested object literal ending in `});` inside stories.push (e.g. a `.map()` callback) — Greptile P2 concern. Also tightened the per-line assertion from substring `includes` to regex `match` so coercion or alt defensive patterns would fail the test rather than silently pass on partial match. Both nits surfaced by Greptile's review of commit 6f40264 (last reviewed commit pre-cache-prefix-bump). Test posture: 284 tests pass across the touched + neighboring surfaces.
1 parent e998644 commit 870ab00

2 files changed

Lines changed: 45 additions & 14 deletions

File tree

shared/brief-filter.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,18 @@ function clip(v, cap) {
119119
* AND space-bearing legacy categories that other `filterTopStories`
120120
* callers pass through (e.g. `composeBriefForRule` with `'world politics'
121121
* \u2192 'World Politics'`). First-letter-only would corrupt the multi-word
122-
* case (`'world politics' \u2192 'World politics'`). Empty / non-string
123-
* inputs are returned unchanged so the existing `|| 'General'` fallback
124-
* stays intact.
122+
* case (`'world politics' \u2192 'World politics'`).
125123
*
126-
* @param {string} v
127-
* @returns {string}
124+
* Defense-in-depth: non-string and empty-string inputs are returned
125+
* unchanged (preserving the input type). At the only current call site
126+
* (`out.push` below), `category` has already been resolved to a
127+
* non-empty string via the `asTrimmedString(raw.category) || 'General'`
128+
* line above, so the type-preserving branch is never reached in
129+
* practice \u2014 it exists so a future caller passing `null`/`undefined`
130+
* doesn't throw.
131+
*
132+
* @param {unknown} v
133+
* @returns {string | unknown} Title-Cased string when input is non-empty string; input unchanged otherwise.
128134
*/
129135
function titleCase(v) {
130136
if (typeof v !== 'string' || v.length === 0) return v;

tests/digest-buildDigest-category-passthrough.test.mjs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,43 @@ describe('U2: buildDigest carries track.category through to the envelope', () =>
5050

5151
it('T7: the passthrough is in stories.push, not in the isOpinion/isFeelGood filter blocks', () => {
5252
// Find the stories.push site. The category passthrough must appear
53-
// inside the object literal (between `stories.push({` and the
54-
// matching `});`).
53+
// by scanning forward from `stories.push({` and matching braces at
54+
// the same depth to find the literal's closing `});`. Earlier this
55+
// test used `indexOf('});')` which would silently truncate the
56+
// search slice if a future change introduced a nested object literal
57+
// ending in `});` inside stories.push (e.g. a `.map()` callback) —
58+
// Greptile P2 review of PR #3751. Brace-depth tracking is robust to
59+
// that without locking the test to the current flat-literal shape.
5560
const storiesPushIdx = buildDigestBody.indexOf('stories.push({');
5661
assert.ok(storiesPushIdx !== -1, 'stories.push site must exist in buildDigest');
57-
// Find the matching `});` — pragmatic: search forward for the next
58-
// `});` (the object literal here is shallow, no nested object).
59-
const closeIdx = buildDigestBody.indexOf('});', storiesPushIdx);
60-
assert.ok(closeIdx !== -1, 'stories.push must have a closing `});`');
62+
let depth = 1; // we start AFTER the opening `{` of `stories.push({`
63+
let closeIdx = -1;
64+
const scanStart = storiesPushIdx + 'stories.push({'.length;
65+
for (let i = scanStart; i < buildDigestBody.length; i += 1) {
66+
const ch = buildDigestBody[i];
67+
if (ch === '{') depth += 1;
68+
else if (ch === '}') {
69+
depth -= 1;
70+
if (depth === 0) {
71+
// Expect the matching `}` to be followed by `);` (the
72+
// function-call close). Confirm before recording the slice end.
73+
if (buildDigestBody.slice(i, i + 3) === '});') {
74+
closeIdx = i + 3;
75+
}
76+
break;
77+
}
78+
}
79+
}
80+
assert.ok(closeIdx !== -1, 'stories.push must have a matched `});` at the same brace depth');
6181
const pushBlock = buildDigestBody.slice(storiesPushIdx, closeIdx);
62-
assert.ok(
63-
pushBlock.includes('category: typeof track.category'),
64-
'category passthrough must live inside the stories.push object literal',
82+
83+
// Full-pattern assertion (not just substring containment): lock the
84+
// exact defensive-typeof shape so a coercion or different defensive
85+
// pattern would fail this test rather than passing on partial match.
86+
assert.match(
87+
pushBlock,
88+
/category:\s*typeof\s+track\.category\s*===\s*'string'\s*\?\s*track\.category\s*:\s*''/,
89+
'category passthrough must live inside the stories.push object literal with the exact defensive-typeof shape',
6590
);
6691

6792
// Negative-space: the opinion / feel-good filter blocks `continue`

0 commit comments

Comments
 (0)