Skip to content

[jsweep] Clean add_labels.cjs#33208

Merged
pelikhan merged 1 commit into
mainfrom
signed/jsweep-clean-add-labels-20260519-f600c2ada689b32c
May 19, 2026
Merged

[jsweep] Clean add_labels.cjs#33208
pelikhan merged 1 commit into
mainfrom
signed/jsweep-clean-add-labels-20260519-f600c2ada689b32c

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 19, 2026

Summary

Code modernization and type safety improvements for the add_labels.cjs module, replacing legacy isNaN() with Number.isNaN(Number()) for more precise numeric validation and adding test coverage for numeric string handling.

Changes

Modified Files

actions/setup/js/add_labels.cjs (Modified, 7 lines: +5/-2)

  • Type Safety Improvement: Replaced isNaN(itemNumber) with Number.isNaN(Number(itemNumber)) for better type coercion behavior
    • Old: isNaN() coerces non-numeric strings to NaN (e.g., isNaN("abc") returns true)
    • New: Number.isNaN(Number()) provides explicit type conversion and safer validation
  • Code Style: Added blank lines for improved readability around validation logic
  • Error Handling: Refined the numeric validation check at line 104 to handle both missing values and invalid numeric strings

actions/setup/js/add_labels.test.cjs (Modified, 46 lines: +46/-0)

  • New Test Cases: Added 2 comprehensive test cases to verify numeric string handling (brings total to 40 tests)
    • Test for valid numeric strings from context payload (e.g., "123" from webhook data)
    • Test for rejection of invalid non-numeric values (e.g., "not-a-number")
  • Coverage Enhancement: Ensures the modernized Number.isNaN(Number()) validation correctly handles edge cases

Impact

  • Scope: Low risk, localized to input validation logic
  • Breaking Changes: None - behavior is preserved for valid inputs
  • Type Safety: Improved - eliminates subtle bugs from legacy isNaN() type coercion
  • Test Coverage: Enhanced - validates both happy path (numeric strings) and error path (invalid inputs)

Testing

  • ✅ All 40 tests pass (38 existing + 2 new)
  • ✅ Format validation passed
  • ✅ Lint checks passed
  • ✅ New tests confirm correct handling of numeric strings and rejection of invalid values

Technical Context

This is part of a broader code modernization effort (jsweep) to replace legacy JavaScript validation patterns with modern, type-safe alternatives. The change aligns with best practices for numeric validation in Node.js environments where webhook payloads may contain numeric values as strings.

Generated by PR Description Updater for issue #33208 · ● 3.6M ·

- Replace isNaN() with Number.isNaN(Number()) for better type safety
- Improve code formatting consistency
- Add 2 new test cases for numeric string validation (40 total tests)
- All validation checks pass: format ✓, lint ✓, tests ✓

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pelikhan pelikhan marked this pull request as ready for review May 19, 2026 05:11
Copilot AI review requested due to automatic review settings May 19, 2026 05:11
@pelikhan pelikhan merged commit 6727385 into main May 19, 2026
9 of 10 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 modernizes the add_labels safe-output handler’s issue/PR number validation (notably for values coming from the GitHub Actions context.payload) and adds tests for numeric-string and invalid-string payload cases.

Changes:

  • Updated unresolved-temp-id error fallback to use nullish coalescing (??) in add_labels.cjs.
  • Replaced isNaN() validation with Number.isNaN(Number(...)) when deriving the issue/PR number from context.payload.
  • Added 2 new tests covering numeric-string and non-numeric string values coming from context.payload.issue.number.
Show a summary per file
File Description
actions/setup/js/add_labels.cjs Adjusts item number validation logic and minor formatting/fallback handling.
actions/setup/js/add_labels.test.cjs Adds tests for string-based context.payload issue numbers (valid numeric string + invalid string).

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

actions/setup/js/add_labels.cjs:108

  • itemNumber can come from context.payload as a string (as the new tests demonstrate), but it's passed through to issues.addLabels({ issue_number: itemNumber }) and returned in result.number without normalization. That can result in issue_number/number being a string and diverging from the ManifestEntry.number contract (documented as number) and Octokit expectations. Consider coercing once (e.g., const n = Number(itemNumber)) and validating Number.isInteger(n) && n > 0, then use the numeric n for API calls and returned results.
        itemNumber = context.payload?.issue?.number ?? context.payload?.pull_request?.number;
      }

      if (!itemNumber || Number.isNaN(Number(itemNumber))) {
        const error = "No issue/PR number available";
        core.warning(error);
        return { success: false, error };
      }
  • Files reviewed: 2/2 changed files
  • Comments generated: 1

);

expect(result.success).toBe(true);
expect(addLabelsCalls).toHaveLength(1);
@github-actions github-actions Bot mentioned this pull request May 19, 2026
Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd (test-driven development) and /zoom-out (architectural consistency) based on this refactor + test enhancement PR.

Key Themes

  • Test assertion depth: New tests verify behavior but could validate type coercion more explicitly
  • Operator consistency: Mixed use of ?? vs || for default values across the file
  • Boundary case coverage: Missing test for itemNumber = 0 edge case

Positive Highlights

  • ✅ Excellent proactive test coverage — 40 comprehensive test cases total
  • ✅ Modernized type-safe number validation with Number.isNaN(Number())
  • ✅ Added tests for numeric string handling from context payload
  • ✅ Clean formatting improvements throughout

Verdict

This is solid modernization work with strong test coverage. The inline comments are minor suggestions for improved test quality and consistency — not blocking issues. Nice cleanup! 🎯

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 3M

success: false,
deferred: true,
error: resolvedTarget.errorMessage || `Unresolved temporary ID: ${explicitItemNumber}`,
error: resolvedTarget.errorMessage ?? `Unresolved temporary ID: ${explicitItemNumber}`,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[/zoom-out] Inconsistent nullish coalescing operator usage. Line 104 uses || but this line uses ??. These operators have different semantics:

  • ?? - nullish coalescing (only null/undefined)
  • || - logical OR (any falsy value)

For consistency and correctness with the numeric validation below, consider using ?? throughout:

resolvedTarget.errorMessage ?? `Unresolved temporary ID: ${explicitItemNumber}`

This aligns with the pattern of explicit null/undefined checks in this codebase.

expect(result.labelsAdded[0].length).toBe(64);
});

it("should handle numeric string from context payload correctly", async () => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[/tdd] This test verifies numeric strings are accepted, but doesn't validate that the type coercion actually works. Consider asserting the converted number is used:

expect(addLabelsCalls[0].issue_number).toBe(123); // Verify number, not string

This ensures Number.isNaN(Number(itemNumber)) correctly converts the string to a number before validation.

expect(addLabelsCalls).toHaveLength(1);
});

it("should reject invalid non-numeric value from context", async () => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[/tdd] Missing boundary test for itemNumber = 0. GitHub issue #0 doesn't exist, but the current logic would reject it as falsy (!itemNumber).

Consider adding a test case:

it("should reject issue number 0 as invalid", async () => {
  mockContext.payload = { issue: { number: 0 } };
  const result = await handler({ labels: ["bug"] }, {});
  expect(result.success).toBe(false);
  expect(result.error).toContain("No issue/PR number available");
});

This documents the intended behavior for edge case numeric values.

@github-actions
Copy link
Copy Markdown
Contributor Author

🧪 Test Quality Sentinel Report

Test Quality Score: 75/100

⚠️ Acceptable, with suggestions

Metric Value
New/modified tests analyzed 2
✅ Design tests (behavioral contracts) 2 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 1 (50%)
Duplicate test clusters 0
Test inflation detected Yes (9.2:1 ratio)
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Issues Detected
should handle numeric string from context payload correctly actions/setup/js/add_labels.test.cjs:816 ✅ Design Test inflation contributor
should reject invalid non-numeric value from context actions/setup/js/add_labels.test.cjs:842 ✅ Design Test inflation contributor

🚨 Test Inflation Warning

The test file grew significantly faster than the production code:

  • Production file (add_labels.cjs): +5 lines
  • Test file (add_labels.test.cjs): +46 lines
  • Ratio: 9.2:1 (threshold: 2:1)

While both new tests provide valuable behavioral coverage, the disproportionate growth suggests that the tests may be more verbose than necessary. The production changes are minimal:

  1. Changed isNaN(itemNumber)Number.isNaN(Number(itemNumber)) (more robust type checking)
  2. Changed ||?? operator for error message (nullish coalescing)
  3. Added blank lines for formatting

These small changes resulted in 46 lines of test code because:

  • Each test sets up a complete mock context and handler (boilerplate)
  • Tests are comprehensive and well-documented
  • The existing test file already has extensive infrastructure

Recommendation: Consider extracting common setup patterns into shared helper functions to reduce boilerplate in future tests.


Analysis Summary

Both new tests verify behavioral contracts rather than implementation details:

  1. should handle numeric string from context payload correctly

    • Design invariant: Function correctly converts numeric string issue numbers from context payload to numbers
    • Value if deleted: HIGH - A regression where numeric strings from GitHub webhooks fail would go undetected
    • Contract vs implementation: Tests observable behavior (successful processing of string numbers)
  2. should reject invalid non-numeric value from context

    • Design invariant: Function rejects invalid non-numeric issue numbers with proper error message
    • Value if deleted: HIGH - Invalid inputs could be silently accepted or cause crashes
    • Contract vs implementation: Tests error handling boundary condition

Both tests align with the code change: the switch from isNaN() to Number.isNaN(Number()) provides more robust validation of numeric strings, and the tests verify this behavior.


Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs, *.test.js): 2 tests (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). Both tests provide genuine behavioral coverage.

Score breakdown:

  • Behavioral coverage: 40/40 pts (100% design tests)
  • Error/edge case coverage: 15/30 pts (50% with edge cases)
  • Low duplication: 20/20 pts (no duplicate patterns)
  • Proportional growth: 0/10 pts (test inflation detected)

Why not higher? The score is 75/100 primarily due to:

  1. Test inflation penalty (-10 pts): Test-to-production ratio of 9.2:1 significantly exceeds the 2:1 guideline
  2. Partial edge case coverage (-15 pts): Only 1 of 2 tests covers error/edge cases (the other tests the happy path)

Both penalties are minor concerns that don't affect the core quality of the tests.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · ● 6M ·

Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 75/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants