Skip to content

fix(cicd): freeze Date constructor in qa-stuck-check smoke tests - #37169

Merged
erickgonzalez merged 1 commit into
mainfrom
nicobytes/fix-qa-stuck-check-date-freeze
Aug 24, 2026
Merged

fix(cicd): freeze Date constructor in qa-stuck-check smoke tests#37169
erickgonzalez merged 1 commit into
mainfrom
nicobytes/fix-qa-stuck-check-date-freeze

Conversation

@nicobytes

Copy link
Copy Markdown
Member

Problem

The QA Stuck Check — PR validate job fails on main with:

assert.ok(!byTeam['Team : Maintenance'])
    at testMainScenario (.github/scripts/qa-stuck-check/test-find-stuck-issues.js:125)

This is a pre-existing time bomb, not a regression from any particular PR. It reproduces on a clean checkout of main:

git checkout main
node .github/scripts/qa-stuck-check/test-find-stuck-issues.js   # fails today

It went unnoticed because the validate workflow only runs on PRs touching .github/scripts/qa-stuck-check/**, its two workflow files, or .claude/triage-config.json — which is rare. It surfaced on #37150, which edits .claude/triage-config.json.

Root cause

The test pins the clock:

const FIXED_NOW = Date.UTC(2026, 4, 25, 13, 0, 0);  // Monday 2026-05-25
Date.now = () => FIXED_NOW;

But find-stuck-issues.js:73 reads the clock as:

const now = new Date();

new Date() does not consult Date.now() — it reads the system clock directly. So the stub never reached the code under test: fixtures were frozen at 2026-05-25 while the script compared them against the real clock.

Once the real date drifted past FIXED_NOW, every fixture aged past STUCK_DAYS=3. The fixture deliberately labelled "Too fresh" — 1 day old, Team : Maintenance, which must not be reported — was measured at 66 business days stuck:

  "title": "Too fresh",
  "itemUpdatedAt": "2026-05-24T13:00:00.000Z",
  "daysStuck": 66

so it was grouped as stuck and the negative assertion blew up. The suite passed only while real time stayed within ~3 business days of FIXED_NOW, i.e. it has been failing since roughly late May 2026.

Fix

Freeze the whole Date constructor, not just Date.now(), so the pin the test already intended actually applies to the script under test:

const RealDate = Date;
class MockDate extends RealDate {
  constructor(...args) {
    super(...(args.length ? args : [FIXED_NOW]));
  }
  static now() { return FIXED_NOW; }
}
global.Date = MockDate;

new Date(someString) and new Date(someNumber) still pass through to the real constructor, so new Date(item.updatedAt) (line 99) and new Date(d) (line 175) keep working; only the zero-arg form is pinned.

Also derives the temp-config filename from the real clock plus process.pid, since a frozen Date.now() would otherwise make that name a constant:

const tmp = path.join(os.tmpdir(), `triage-${RealDate.now()}-${process.pid}.json`);

No production code changesfind-stuck-issues.js is untouched. Test file only, +17/−2.

Verification

All 12 scenarios pass, and the result no longer depends on the date the suite is run:

=== testMainScenario ===            OK
=== testEmptyProject ===            OK
=== testCrossTeamIssue ===          OK
=== testMissingStatusFailsLoudly === OK
=== testSomeMissingStatusWarnsButContinues === OK
=== testNullProjectFailsLoudly ===  OK
=== testTeamWithoutSlackChannelIgnored === OK
=== testInvalidStuckDaysFailsLoudly === OK
=== testNonIssueContentDoesNotTripRatio === OK
=== testWeekdayCounting ===         OK
=== testClosedQaIssueIsSkipped ===  OK
=== testLabelsOverflowSkipsIssue === OK

All tests passed.

Note on the scheduled run

Only the smoke tests were affected. The Mon/Thu scheduled job (cicd_scheduled_qa-stuck-check.yml) runs against real project data with a real clock, so its behaviour was always correct — this bug was confined to the test harness.

Unblocks #37150.

🤖 Generated with Claude Code

The QA Stuck Check PR-validate job was failing on #37150 with

  assert.ok(!byTeam['Team : Maintenance'])

This is a pre-existing time bomb on main, not a regression from this PR.
This PR only surfaced it: the validate workflow triggers on changes to
.claude/triage-config.json, which the dead-code removal touches, and that
workflow rarely runs otherwise.

Root cause: the test pinned the clock with

  Date.now = () => FIXED_NOW;   // Monday 2026-05-25

but find-stuck-issues.js:73 reads the clock as `new Date()`, which does
not consult Date.now(). So the fixtures were frozen at 2026-05-25 while
the script compared them against the real clock. Once the real date drifted
past FIXED_NOW, every fixture aged past STUCK_DAYS: the "Too fresh" item
(1 day old, Team : Maintenance) was measured at 66 business days, so it
was reported as stuck and the negative assertion failed. The suite passed
only while real time stayed within ~3 business days of FIXED_NOW, so it
has been broken since roughly late May 2026.

Fix: freeze the whole Date constructor, not just Date.now(), so the pin
the test already intended actually applies to the script under test. No
production code changes — find-stuck-issues.js is untouched.

Also derives the temp-config filename from the real clock plus pid, since
a frozen Date.now() would otherwise make that name a constant.

All 12 scenarios pass, and the result no longer depends on the date the
suite is run.

Refs: #36950
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nicobytes
nicobytes requested a review from a team as a code owner August 24, 2026 13:52
Copilot AI lite review requested due to automatic review settings August 24, 2026 13:52
@github-actions

Copy link
Copy Markdown
Contributor

❌ Issue Linking Required

This PR could not be linked to an issue. All PRs must be linked to an issue for tracking purposes.

How to fix this:

Option 1: Add keyword to PR body (Recommended - auto-removes this comment)
Edit this PR description and add one of these lines:

  • Fixes #123 or Closes #123 — same-repo issue
  • Fixes org/repo#123 or Closes org/repo#123 — cross-repo or private issue
  • Closes https://github.com/org/repo/issues/123 — full GitHub URL
  • Other supported keywords: fix, fixed, close, closed, resolve, resolved

Option 2: Link via GitHub UI (Note: won't clear the failed check)

  1. Go to the PR → Development section (right sidebar)

  2. Click "Link issue" and select an existing issue

  3. Push a new commit or re-run the workflow to clear the failed check
    Option 3: Use branch naming
    Create a new branch with one of these patterns:

  • 123-feature-description (number at start)

  • issue-123-feature-description (issue-number at start)

  • feature-issue-123 (issue-number anywhere)

Why is this required?

Issue linking ensures proper tracking, documentation, and helps maintain project history. It connects your code changes to the problem they solve.
---This comment was automatically generated by the issue linking workflow

@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Claude finished @nicobytes's task in 1m 4s —— View job


Review: freeze Date constructor in qa-stuck-check smoke tests

  • Read the diff
  • Analyze test harness change against find-stuck-issues.js
  • Post review

I traced the MockDate against every clock read in find-stuck-issues.js. The fix is sound:

  • new Date() → pinnedfind-stuck-issues.js:73 (const now = new Date()) hits the zero-arg branch (args.length is 0), so super(FIXED_NOW) pins it. This is exactly what the old Date.now = ... stub failed to reach. ✅
  • Parametrized forms pass throughnew Date(item.updatedAt) (line 99) and new Date(d) (line 175) have args.length ≥ 1, so they forward to the real constructor unchanged. ✅
  • Date.UTC still worksfind-stuck-issues.js:171-172 calls Date.UTC(...); MockDate inherits the static via the extends RealDate prototype chain. ✅
  • instanceof Date still holdscountWeekdaysBetween guards with from instanceof Date (line 169). Both operands are MockDate instances and the global Date is MockDate, so the check passes. ✅
  • Temp filenameRealDate.now() + process.pid keeps the name unique despite the frozen Date.now(). ✅ (uniqueness is belt-and-suspenders here since the test writes and unlinkSyncs within one call, but it's correct.)

New Issues

No issues found.

Minor nits (non-blocking, no change required):

  • The PR description lists the 3rd scenario as testCrossTeamIssue, but the actual function is testMultipleTeamLabels — description-only mismatch, code is fine.
  • MockDate can't be invoked as Date() without new (classes throw), whereas real Date() returns a string. find-stuck-issues.js only ever uses new Date(), so this is not a problem in practice.

I was unable to execute node ...test-find-stuck-issues.js in this environment (command not permitted), but the static trace above confirms the stub now reaches every clock read, and the previously-failing testMainScenario negative assertion (!byTeam['Team : Maintenance']) is satisfied because the "Too fresh" fixture (daysAgo(1)) is now measured against the pinned Monday rather than the real clock.

Test-only change (+17/−2), no production code touched. Looks good to merge.
· nicobytes/fix-qa-stuck-check-date-freeze

Copilot AI 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.

Pull request overview

This PR stabilizes the .github/scripts/qa-stuck-check smoke tests by properly freezing time so the tests remain deterministic regardless of the real execution date, addressing a latent failure in the “QA Stuck Check — PR validate” job.

Changes:

  • Replaces a Date.now-only stub with a global.Date constructor override so new Date() in find-stuck-issues.js is actually pinned to the intended fixed timestamp.
  • Updates the temporary triage-config filename generation to use the real clock (RealDate.now()) plus process.pid to avoid collisions under a frozen clock.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@erickgonzalez
erickgonzalez added this pull request to the merge queue Aug 24, 2026
Merged via the queue into main with commit 88af0ba Aug 24, 2026
47 of 48 checks passed
@erickgonzalez
erickgonzalez deleted the nicobytes/fix-qa-stuck-check-date-freeze branch August 24, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants