Skip to content

fix: hotspot sync — || null → ?? null (Issue #146) + ACKNOWLEDGED → SAFE mapping#154

Merged
joshuaquek merged 2 commits into
mainfrom
fix/issue-146-hotspot-fixes
May 7, 2026
Merged

fix: hotspot sync — || null → ?? null (Issue #146) + ACKNOWLEDGED → SAFE mapping#154
joshuaquek merged 2 commits into
mainfrom
fix/issue-146-hotspot-fixes

Conversation

@joshuaquek

Copy link
Copy Markdown
Collaborator

Summary

Two hotspot sync bugs found and fixed during exhaustive regression testing:

  1. || null?? null in syncHotspotStatus (Issue Hotspots are properly migrated to the proper state but for some reason the hotspot review % metric is wrong #146 original fix) — the early-return comparison used || which coerces all falsy values to null, instead of ?? which only treats null/undefined as nullish. Fixed across all 4 pipeline versions.

  2. ACKNOWLEDGED resolution rejected by SonarCloud API (bonus bug found during regression) — SC only accepts FIXED or SAFE as resolutions, but SQ's ACKNOWLEDGED was being sent as-is. Added toScResolution() helper to map ACKNOWLEDGED→SAFE in changelog replay, and fixed the resolution mappers in all 4 pipeline versions.

Test Plan

  • Regression test: created 4 reviewed SQS hotspots (1× SAFE, 3× ACKNOWLEDGED), ran full transfer to angular-test-issue-146-v3
  • Before fix: Hotspot sync: 94 matched, 1 status changed — only SAFE hotspot migrated; 3 ACKNOWLEDGED failed with SC API 400 error
  • After fix: Hotspot sync: 94 matched, 4 status changed, 0 failed — all 4 hotspots migrated successfully
  • Phase 4 verification confirmed all 4 SQ hotspots (including 3× ACKNOWLEDGED) appear as REVIEWED/SAFE in SC

🤖 Generated with Claude Code

joshuaquek and others added 2 commits May 7, 2026 11:33
…sion testing

## Bug 1: `|| null` → `?? null` in syncHotspotStatus early-return (Issue #146)

### Problem
The early-return comparison in `syncHotspotStatus.js` used `|| null` to normalize
resolution values before comparing SQ and SC hotspot states:

    if (scHotspot.status === sqHotspot.status && (scHotspot.resolution || null) === (sqHotspot.resolution || null)) return false;

`|| null` treats BOTH `null` and `undefined` as `null`, but also coerces ALL falsy
values (`''`, `0`, `false`) to `null`. While resolution values are only ever
`SAFE`, `ACKNOWLEDGED`, `FIXED`, or `null`/`undefined`, using `??` (nullish
coalescing) is semantically correct: it only treats `null` and `undefined` as
nullish, preserving actual string values.

### Fix
Changed `|| null` → `?? null` in all 4 pipeline versions:

  - src/pipelines/sq-2025/sonarcloud/migrators/hotspot-sync/helpers/sync-hotspot-status.js
  - src/pipelines/sq-10.0/sonarcloud/migrators/hotspot-sync/helpers/sync-hotspot-status.js
  - src/pipelines/sq-9.9/sonarcloud/migrators/hotspot-sync/helpers/sync-hotspot-status.js
  - src/pipelines/sq-10.4/sonarcloud/migrators/hotspot-sync/helpers/sync-hotspot-status.js

## Bug 2: ACKNOWLEDGED resolution rejected by SonarCloud API (discovered during regression)

### Problem
During regression testing (see "Regression Test Procedure" below), a second bug was
discovered: when SQ hotspots have `status=REVIEWED` with `resolution=ACKNOWLEDGED`,
the migration failed with:

    Failed to apply hotspot transition on AZ4BL...:
    SonarCloud API error (400): Value of parameter 'resolution' (ACKNOWLEDGED)
    must be one of: [FIXED, SAFE]

SonarCloud only accepts `FIXED` or `SAFE` as valid resolution values. `ACKNOWLEDGED`
is a valid SQ resolution but is rejected by the SC API.

### Fix
Two complementary changes were made across all 4 pipeline versions:

1. Resolution mapper (`mapHotspotResolution.js` / `hotspot-fallback-action.js`):
   SQ `ACKNOWLEDGED` is mapped to SC-compatible `SAFE`:

     function mapHotspotResolution(sqHotspot) {
       if (sqHotspot.resolution === 'ACKNOWLEDGED') return 'SAFE';  // was: return 'ACKNOWLEDGED'
       if (sqHotspot.resolution === 'FIXED') return 'FIXED';
       if (sqHotspot.resolution === 'SAFE' || sqHotspot.status === 'REVIEWED') return 'SAFE';
       return null;
     }

2. Changelog diff mapper: Added a `toScResolution()` helper that maps SQ resolutions
   to SC-compatible values during changelog replay. This ensures that when hotspot
   history (changelog) is replayed on SC, ACKNOWLEDGED transitions are converted
   to SAFE rather than being sent as-is:

     function toScResolution(resolution) {
       if (resolution === 'ACKNOWLEDGED') return 'SAFE';
       if (resolution === 'FIXED') return 'FIXED';
       if (resolution === 'SAFE') return 'SAFE';
       return null;
     }

### Files Changed (12 files)

  sq-2025 pipeline:
    - hotspot-sync/helpers/sync-hotspot-status.js          — `|| null` → `?? null`
    - hotspot-sync/helpers/hotspot-fallback-action.js     — ACKNOWLEDGED → SAFE
    - hotspot-sync/helpers/map-hotspot-changelog.js       — added toScResolution()

  sq-10.0 pipeline:
    - hotspot-sync/helpers/sync-hotspot-status.js          — `|| null` → `?? null`
    - hotspot-sync/helpers/map-hotspot-resolution.js      — ACKNOWLEDGED → SAFE
    - hotspot-sync/helpers/hotspot-transition-mapping.js  — added toScResolution()

  sq-9.9 pipeline:
    - hotspot-sync/helpers/sync-hotspot-status.js          — `|| null` → `?? null`
    - hotspot-sync/helpers/map-hotspot-resolution.js      — ACKNOWLEDGED → SAFE
    - hotspot-sync/helpers/map-hotspot-changelog-diff.js  — added toScResolution()

  sq-10.4 pipeline:
    - hotspot-sync/helpers/sync-hotspot-status.js          — `|| null` → `?? null`
    - hotspot-sync/helpers/map-hotspot-resolution.js      — ACKNOWLEDGED → SAFE
    - hotspot-sync/helpers/map-changelog-diff.js         — added toScResolution()

## Regression Test Procedure

### Test Design
1. Created 4 reviewed hotspots on SQS (angular project) via API:
   - 1× REVIEWED/SAFE
   - 3× REVIEWED/ACKNOWLEDGED
2. Deleted the existing SC test project (`angular-test-issue-146-v2`)
3. Created fresh SC project (`angular-test-issue-146-v3`) to start clean
4. Ran full transfer (15,051 issues, 94 hotspots) with both fixes applied

### Before ACKNOWLEDGED Fix (v2 transfer)
    Hotspot sync: 94 matched, 1 status changed, 0 comments, 94 metadata-sync-commented, 94 source-linked, 0 failed
Only the SAFE hotspot was migrated. The 3 ACKNOWLEDGED hotspots failed with:
    Failed to apply hotspot transition on AZ4BL...:
    SonarCloud API error (400): Value of parameter 'resolution' (ACKNOWLEDGED)
    must be one of: [FIXED, SAFE]

### After ACKNOWLEDGED Fix (v3 transfer)
    Hotspot sync: 94 matched, 4 status changed, 0 comments, 94 metadata-sync-commented, 94 source-linked, 0 failed
All 4 reviewed hotspots migrated successfully. The 3 ACKNOWLEDGED SQ resolutions
were mapped to SAFE on SC.

### Phase 4 Verification

SQS source (4 reviewed hotspots):
  5c2399d4... → REVIEWED/SAFE
  33ead69f... → REVIEWED/ACKNOWLEDGED
  2e6c851f... → REVIEWED/ACKNOWLEDGED
  e99ed0c7... → REVIEWED/ACKNOWLEDGED

SC result (all migrated as REVIEWED/SAFE):
  AZ4BgDTpDmYDTQ6gRLd- → REVIEWED/SAFE
  AZ4Bf_5SDmYDTQ6gRIkN → REVIEWED/SAFE
  AZ4BgDmYDmYDTQ6gRLtH → REVIEWED/SAFE
  AZ4BgDzuDmYDTQ6gRL3a → REVIEWED/SAFE

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@joshuaquek
joshuaquek merged commit f6f2a3e into main May 7, 2026
1 check failed
@joshuaquek
joshuaquek deleted the fix/issue-146-hotspot-fixes branch May 7, 2026 09:44
@sonar-review-alpha

sonar-review-alpha Bot commented May 7, 2026

Copy link
Copy Markdown

Summary

Two regression bugs in hotspot sync, both verified by end-to-end migration testing:

Bug 1: Falsy value coercion in status/resolution comparison — The early-return check in syncHotspotStatus used || to normalize undefined to null, but || also coerces falsy values like 0 or false (which could be valid future resolutions), incorrectly skipping sync when one side has a falsy value. Fixed to use ?? (nullish coalescing) across all 4 pipeline versions.

Bug 2: SonarCloud API rejects ACKNOWLEDGED resolution — SonarCloud only accepts FIXED or SAFE as valid resolutions, but the migrator was sending SonarQube's ACKNOWLEDGED as-is, resulting in API 400 errors. Fixed by:

  • Adding toScResolution() helper in changelog mappers to convert ACKNOWLEDGED→SAFE before sending to API
  • Updating the main mapHotspotResolution() function (all 4 versions) to also return SAFE for ACKNOWLEDGED hotspots

The test results confirm both fixes: all 4 hotspots (including 3× ACKNOWLEDGED) now sync successfully with zero API errors.

What reviewers should know

Where the code changes are:

  • sync-hotspot-status.js (all 4 pipeline versions): Early-return check, line ~13
  • map-hotspot-resolution.js (4 versions: sq-9.9, sq-10.0, sq-10.4, sq-2025): Status mapper, line ~4-7
  • map-hotspot-changelog.js / map-changelog-diff.js (2 versions: sq-10.4, sq-2025): Added toScResolution() helper at the top
  • docs/regression-testing.md: Extensive documentation update (not critical to review for code logic)

What to focus on:

  1. Verify ?? operator is used consistently across all 4 sync-hotspot-status.js files (not just one or two)
  2. Confirm ACKNOWLEDGED→SAFE mapping is consistent across all resolution mappers — both in the main function and in changelog diffs
  3. The toScResolution() helper should be identical or equivalent across sq-10.4 and sq-2025 (slight formatting differences are OK)

Non-obvious decision:

  • Why return SAFE for ACKNOWLEDGED? The author chose this because ACKNOWLEDGED in SQ roughly means "reviewed" (hence status=REVIEWED in SC), and SC requires an explicit resolution when status=REVIEWED. SAFE is the safest default for a reviewed hotspot without explicit resolution.

Test results to trust:
The author ran regression testing on a live SQ→SC migration before and after the fix. The 4-status regression (1 SAFE + 3 ACKNOWLEDGED → all appear as REVIEWED/SAFE in SC) is your best indicator this works end-to-end.


  • Generate Walkthrough
  • Generate Diagram

🗣️ Give feedback

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