Skip to content

chore(sonar): burn down S3776/S6964/S1994 smell clusters (#243) - #392

Merged
thomasluizon merged 2 commits into
mainfrom
chore/sonar-smell-cluster-243
Jul 13, 2026
Merged

chore(sonar): burn down S3776/S6964/S1994 smell clusters (#243)#392
thomasluizon merged 2 commits into
mainfrom
chore/sonar-smell-cluster-243

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Behavior-preserving SonarCloud smell burn-down (orbit-api, key thomasluizon_orbit-api). Drives three rule clusters to ~0 at source, no suppressions.

Rules fixed

Rule Count Fix
csharpsquid:S3776 (cognitive complexity) 13 methods Extract private helpers / guard clauses so each method drops under the 15 threshold
csharpsquid:S6964 (value-type under-posting) 14 properties [property: JsonRequired] on non-nullable request-DTO properties, matching the existing ProfileController precedent
csharpsquid:S1994 (loop counter not tested) 14 retry loops Convert for (var attempt = n; ; n++) infinite loops to while (true) / counter-testing for, incrementing on every loop-back path

S3776 methods refactored

SendCodeCommand (test-account seed helper), CreateHabitCommand (pay-gate helper), BulkSkipHabitsCommand (flexible-skip helper), ApplyOnboardingCommand (transaction body → method), UserStreakService.CalendarFallback (Math.Max + flattened if/else), HandlePlayNotificationCommand (user-resolution + subscription-apply helpers), GetCalendarSyncSuggestionsQuery (per-suggestion filter helper), SyncCleanupService (generic PurgeAsync<T>), ProcessUserChatCommand (response-card builder), AiIntentService (streaming delta/tool-call helpers), ResendEmailService (per-attempt send helper), PushNotificationService x2 (RecordBatchOutcomes, TryDeliverWebPushAsync/ClassifyTransientPushFailure).

Contract safety

S6964 uses [property: JsonRequired] (Sonar's recommended annotation) — the JSON wire shape and field types are unchanged, so no consumer/@orbit/shared change is needed. Only affects strictness for genuinely-always-sent mandatory inputs; append-only contract respected (no field retyped/renamed/removed).

Verification

  • dotnet build — 0 errors, 0 new warnings for targeted rules.
  • dotnet test — 5284 tests green (Domain 512, Application 2773, Infrastructure 1992, Analyzers 7).
  • No ORBIT0001 narration comments; no ORBIT0002 redundant rollbacks; no #pragma/NOSONAR suppressions.

Refs thomasluizon/orbit-ui-mobile#243

thomasluizon and others added 2 commits July 13, 2026 19:40
…-binding, and loop smells (#243)

Behavior-preserving refactors that drive three SonarCloud rule clusters to zero:

- csharpsquid:S3776 (cognitive complexity, 13 methods): extract private
  helpers / guard clauses so each method is under the 15 threshold —
  SendCodeCommand, CreateHabitCommand, BulkSkipHabitsCommand,
  ApplyOnboardingCommand, UserStreakService, HandlePlayNotificationCommand,
  GetCalendarSyncSuggestionsQuery, SyncCleanupService, ProcessUserChatCommand,
  AiIntentService, ResendEmailService, and PushNotificationService (x2).
- csharpsquid:S6964 (14 value-type request properties): annotate with
  [property: JsonRequired] to prevent under-posting, matching the existing
  ProfileController precedent. Wire contract unchanged (append-only safe).
- csharpsquid:S1994 (14 retry loops): convert `for (var attempt = n; ; n++)`
  infinite loops to `while (true)` / counter-testing `for`, incrementing on
  every loop-back path so behavior is identical.

Full dotnet build (0 new warnings) + dotnet test (5284 tests) green.

Refs thomasluizon/orbit-ui-mobile#243

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ster-243

# Conflicts:
#	src/Orbit.Application/Profile/Commands/ApplyOnboardingCommand.cs
@sonarqubecloud

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review: PR #392 — chore(sonar): burn down S3776/S6964/S1994 smell clusters

Summary: Behavior-preserving SonarCloud smell burn-down — 13 methods de-complexified via private-helper extraction (S3776), 14 non-nullable value-type DTO properties annotated [property: JsonRequired] (S6964), 14 infinite for loops converted to while(true) with explicit trailing attempt++ (S1994). Every extraction/loop-conversion was traced against its pre-refactor logic. No correctness regression found. An independent security-focused pass over the same file set also found no regression.

Findings:

  • Critical / High: None.
  • InfoJsonRequired strictness is a real (intentional) behavior change for malformed callers, not a pure refactor: fields like ChallengeType Type, Guid BuddyUserId, Guid RecipientId previously silently deserialized to default(T) when omitted; now they 400. Each annotated field was checked against its handler (e.g. UpdatePublicProfileCommand does a full-overwrite with no partial-update semantics, so all four booleans were already logically mandatory) — this reads as a bug fix (silent corruption → clean validation error), not a contract break. No ⚠️ breaks old mobile clients marker warranted; no field found where a shipped client could plausibly omit it.
  • Info — confirmed JsonRequired violations surface as clean ASP.NET Core 400 model-binding errors, not unhandled-exception stack traces.

Cross-repo check: NOT VERIFIABLE IN CI — orbit-ui-mobile is not checked out in this job. JsonRequired additions are strictness-only (no field renamed/removed/retyped), so per the PR's own note no packages/shared change should be required, but this could not be independently confirmed against the mobile repo.

Validation: Build/Tests — skipped per CI adaptation (Build / Unit Tests / SonarCloud run as separate required checks). PR body states 5284 tests green.

What's good: every loop conversion correctly preserves its retry-bound exception-filter and increments only on the swallowed-retry path; the WebPush retry loop gained a defense-in-depth bound that's provably never the actual exit path; the streak-calendar-fallback simplification is a correct De Morgan transform; the sync-cleanup purge collapse is a clean DRY reduction of near-identical blocks; no narration comments, no suppressions, no TODOs introduced.

Recommendation: APPROVE. Non-blocking follow-up suggestion: watch 400-rate metrics on the touched endpoints for a few days post-deploy in case any client build omits a newly-required field.

@thomasluizon
thomasluizon merged commit f2f9454 into main Jul 13, 2026
19 checks passed
@thomasluizon
thomasluizon deleted the chore/sonar-smell-cluster-243 branch July 13, 2026 23:02
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