Skip to content

feat(ci): check:test-masking flags inline-reimplemented prod conditions (#6348) - #6393

Merged
diegosouzapw merged 2 commits into
release/v3.8.46from
feat/6348-test-masking-inline
Jul 6, 2026
Merged

diegosouzapw merged 2 commits into
release/v3.8.46from
feat/6348-test-masking-inline

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Extends check:test-masking (v2, 6A.10) to catch the wrong-shape contract test — a test that recomputes the condition under test inline instead of importing/exercising the real function. Live precedent: PR #6216 (=== 500 → >= 500) stayed green because its tests re-implemented the branch inline; the regression only surfaced one CI round later.

What changed (scripts/check/check-test-masking.mjs)

  • New report-only subcheck: for each added/modified test file, flag when it (1) textually duplicates a ≥3-token conditional from a production file changed in the same PR AND (2) does not import the symbol/module owning that condition.
  • Pure, fixture-testable findReimplementedConditions() + an allowlist mirroring assertReductionAllowlist.
  • Report-only (warns, does not fail the gate) — to be promoted to blocking after a triage cycle, per the ratchet playbook.

Tests (Hard Rule #18)

tests/unit/check-test-masking.test.ts (45) — masked case flagged; imported-and-exercised case + <3-token trivial guard not flagged.

Closes #6348

…ns (#6348)

New report-only subcheck: for each added/modified test file, warn when it
textually duplicates a >=3-token conditional from a production file touched
in the same PR AND does not import the symbol/module owning it (the #6216
wrong-shape-contract-test class). Pure findReimplementedConditions() +
allowlist mirroring assertReductionAllowlist. Report-only for now.

Closes #6348
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new report-only subcheck to detect tests that inline-reimplement production conditions instead of importing their owning symbols, along with comprehensive unit tests. The reviewer provided highly constructive feedback to improve the heuristic's robustness, including: updating the declaration regex to support TypeScript type annotations and class declarations, grouping extracted conditions to prevent false positives when a condition is shared across multiple production files, and ensuring parentheses are properly balanced before extracting if conditions to avoid parsing errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +120 to +125
const declRe =
/(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)|(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g;
let dm;
while ((dm = declRe.exec(src))) {
decls.push({ index: dm.index, name: dm[1] || dm[2] });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In TypeScript, variable declarations often have type annotations (e.g., const isServerError: Handler = ...) and classes are frequently used. The current declRe fails to capture variable names with type annotations because of the strict \s*= check, and it completely ignores class declarations. This leads to false positives where conditions are flagged as reimplemented even when their owning symbols or classes are imported.

We can make the type annotation optional in the regex and add support for class declarations to ensure they are correctly resolved as owners.

Suggested change
const declRe =
/(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)|(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g;
let dm;
while ((dm = declRe.exec(src))) {
decls.push({ index: dm.index, name: dm[1] || dm[2] });
}
const declRe =
/(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)|(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)(?:\s*:[^=]+)?\s*=|(?:\bclass\s+([A-Za-z_$][\w$]*))/g;
let dm;
while ((dm = declRe.exec(src))) {
decls.push({ index: dm.index, name: dm[1] || dm[2] || dm[3] });
}

Comment on lines +204 to +223
export function findReimplementedConditions(prodSources, testSource, testImports) {
const flags = [];
if (!testSource) return flags;
const imports =
testImports instanceof Set ? testImports : new Set(testImports || []);
const squash = (s) => (s || "").replace(/\s+/g, "");
const testSq = squash(testSource);
const seen = new Set();
for (const prod of prodSources || []) {
for (const { condition, owner } of extractProdConditions(prod)) {
if (owner && imports.has(owner)) continue; // exercised through the real import
if (seen.has(condition)) continue;
if (testSq.includes(squash(condition))) {
seen.add(condition);
flags.push({ condition, owner: owner || null });
}
}
}
return flags;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If a common condition (such as status >= 500) is used in multiple changed production files, and the test imports the owner from one of them but not the other, the current implementation will still flag the condition as reimplemented.

To prevent this false positive, we should group all extracted conditions from all production files and map them to their set of owners. If the test imports at least one of the owners of that condition, we can safely exempt it.

export function findReimplementedConditions(prodSources, testSource, testImports) {
  const flags = [];
  if (!testSource) return flags;
  const imports =
    testImports instanceof Set ? testImports : new Set(testImports || []);
  const squash = (s) => (s || "").replace(/\s+/g, "");
  const testSq = squash(testSource);

  const condOwners = new Map();
  for (const prod of prodSources || []) {
    for (const { condition, owner } of extractProdConditions(prod)) {
      const sq = squash(condition);
      if (!condOwners.has(sq)) {
        condOwners.set(sq, { original: condition, owners: new Set() });
      }
      if (owner) {
        condOwners.get(sq).owners.add(owner);
      }
    }
  }

  for (const [sq, { original, owners }] of condOwners) {
    let importedAnyOwner = false;
    for (const owner of owners) {
      if (imports.has(owner)) {
        importedAnyOwner = true;
        break;
      }
    }
    if (importedAnyOwner) continue;

    if (testSq.includes(sq)) {
      const owner = owners.size > 0 ? Array.from(owners)[0] : null;
      flags.push({ condition: original, owner });
    }
  }
  return flags;
}

Comment on lines +146 to +155
while ((m = ifRe.exec(src))) {
let depth = 1;
let i = m.index + m[0].length;
for (; i < src.length && depth > 0; i++) {
const ch = src[i];
if (ch === "(") depth++;
else if (ch === ")") depth--;
}
pushCond(src.slice(m.index + m[0].length, i - 1), ownerAt(m.index));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the parentheses in an if statement are unbalanced (e.g., due to syntax errors, or strings/comments containing parentheses), depth will remain greater than 0 when the loop terminates. In this case, pushCond is called with a potentially huge slice of code extending to the end of the file, which can cause false matches or performance issues. We should only extract the condition if the parentheses were successfully balanced (depth === 0).

Suggested change
while ((m = ifRe.exec(src))) {
let depth = 1;
let i = m.index + m[0].length;
for (; i < src.length && depth > 0; i++) {
const ch = src[i];
if (ch === "(") depth++;
else if (ch === ")") depth--;
}
pushCond(src.slice(m.index + m[0].length, i - 1), ownerAt(m.index));
}
while ((m = ifRe.exec(src))) {
let depth = 1;
let i = m.index + m[0].length;
for (; i < src.length && depth > 0; i++) {
const ch = src[i];
if (ch === "(") depth++;
else if (ch === ")") depth--;
}
if (depth === 0) {
pushCond(src.slice(m.index + m[0].length, i - 1), ownerAt(m.index));
}
}

@diegosouzapw
diegosouzapw merged commit ecf3d3a into release/v3.8.46 Jul 6, 2026
2 of 3 checks passed
@diegosouzapw
diegosouzapw deleted the feat/6348-test-masking-inline branch July 7, 2026 19:20
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…ns (diegosouzapw#6348) (diegosouzapw#6393)

check:test-masking flags inline-reimplemented prod (diegosouzapw#6348) (net +1/-0, tests OK). Integrated into release/v3.8.46.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ns (diegosouzapw#6348) (diegosouzapw#6393)

check:test-masking flags inline-reimplemented prod (diegosouzapw#6348) (net +1/-0, tests OK). Integrated into release/v3.8.46.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant