Skip to content

refactor(channels): remove dead unguarded channel upsert path (#3503)#3508

Merged
Yeraze merged 2 commits into
mainfrom
fix/3503-channel-sync-blank-name
Jun 16, 2026
Merged

refactor(channels): remove dead unguarded channel upsert path (#3503)#3508
Yeraze merged 2 commits into
mainfrom
fix/3503-channel-sync-blank-name

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Closes #3503.

Finding: not a live bug — dead code

#3503 (from the clobber audit) reported that channel names were silently blanked on SQLite because ChannelsRepository.upsertChannelSync lacked the #1567 blank-name guard the async path has. Reachability verification overturned that premise:

  • upsertChannelSync is referenced only by DatabaseService.upsertChannel (the legacy facade).
  • DatabaseService.upsertChannel (~85 lines) has zero callers anywhere in src/.
  • Every real channel upsert goes through databaseService.channels.upsertChannel — the async repo method, which has the [BUG] After upgrading to 3.1.x all channels on Meshtastic app show as "Channel Name" #1567 guard and runs on all backends (no dbType branch diverts SQLite to the sync method).

So the unguarded path is unreachable — there was no data-loss bug. (My initial audit confirmed the guard divergence but not reachability; the audit agent wrongly assumed the facade was the active SQLite path.)

Change

Remove both dead methods so the unguarded path can't be wired up by mistake later: DatabaseService.upsertChannel and ChannelsRepository.upsertChannelSync (−138 lines). The // Channel operations section header stays, with a note pointing to the live guarded method.

Validation

  • tsc --noEmit: clean (no dangling refs; DbChannel/getSqliteDb still used elsewhere)
  • Full Vitest suite: 6527 pass, 0 failunchanged count, confirming the removed code was unreachable.

🤖 Generated with Claude Code

#3503 reported channel names silently blanked on SQLite via an upsert that
lacked the #1567 blank-name guard. Verification showed the unguarded path is
DEAD CODE, not a live bug:
- ChannelsRepository.upsertChannelSync was called only by
  DatabaseService.upsertChannel.
- DatabaseService.upsertChannel (the ~85-line facade) has zero callers.
- Every real channel upsert goes through the async, #1567-guarded
  databaseService.channels.upsertChannel (which runs on all backends).

So there was no data-loss bug. Remove both dead methods so the unguarded path
can't be wired up by mistake later. No behavior change — full suite green (6527,
unchanged), confirming the removed code was unreachable.

Closes #3503.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: refactor(channels): remove dead unguarded channel upsert path (#3503)

  • Read changed files
  • Verify reachability / dead-code claims
  • Check for any remaining references
  • Assess code quality, test coverage, and correctness
  • Post review

Summary

This is a clean, well-motivated dead-code removal. The PR's core claims hold up under inspection.

Dead-code verification ✅

The reachability analysis is correct:

  • ChannelsRepository.upsertChannelSyncfully removed, no remaining references in src/. The only occurrence of the name in the codebase is now the explanatory comment at src/services/database.ts:3682.
  • DatabaseService.upsertChannel (the legacy sync facade) — fully removed, same story. No callers in src/ reference DatabaseService.upsertChannel or databaseService.upsertChannel as a top-level method. All live callers go through databaseService.channels.upsertChannel (the async repo method), confirmed across meshtasticManager.ts, meshcoreManager.ts, server.ts, and mqttIngestion.ts.

Code quality ✅

The removal is surgical. The section header at src/services/database.ts:3681-3684 was replaced with a clear tombstone comment explaining why the methods were removed and where the live path lives. That's the right pattern — better than a blank section.

One genuine issue: orphaned tests in database.test.ts ⚠️

src/services/database.test.ts defines a local TestDatabaseService class that still implements its own upsertChannel method (database.test.ts:220) and exercises it in three test cases (lines 616, 625-626, 633-634). This is a test-only reimplementation unrelated to the removed production code — those tests won't break, but they are now testing a fixture that mimics a method that no longer exists in production. The fixture uses raw SQL without the #1567 blank-name guard, and the tests pass a blank name to a non-existent production method.

This is a pre-existing issue (the test class was always a parallel reimplementation), but removing the production method makes the tests misleading: they appear to validate DatabaseService.upsertChannel semantics that are now gone. The tests should either be removed or repurposed to test the live ChannelsRepository.upsertChannel async path (which already has comprehensive tests in src/db/repositories/channels.test.ts).

Recommendation: Delete the describe('Channel Operations', ...) block (database.test.ts:608–639) and the upsertChannel method from TestDatabaseService (database.test.ts:220–243), since they test a removed API. This isn't a blocker for merging, but leaving them makes the test suite misleading.

mqttIngestion.ts call site — worth noting 🔍

src/server/mqttIngestion.ts:959 calls databaseService.channels?.upsertChannel(...) (with optional chaining). The result is then inspected for a .catch method to handle async rejection. This pattern works but is slightly unusual — it's defensive against the channels repo being undefined. This call site correctly reaches the async guarded path and is not affected by this PR.

Test coverage ✅

  • src/db/repositories/channels.test.ts has thorough coverage of ChannelsRepository.upsertChannel, including the blank-name preservation case (#1567 guard), role enforcement, source scoping, and multi-source isolation. The live path is well-tested.
  • The orphaned tests in database.test.ts noted above continue to pass (they test the internal fixture, not production code).
  • 6527 tests pass, 0 fail — confirmed.

Verdict

Approve. The removal is correct, the dead-code claim is verified, and the resulting code is cleaner. The orphaned test cleanup is a suggested follow-up, not a blocking issue.

claude-pr review on #3508: database.test.ts had a TestDatabaseService fixture
upsertChannel + create/update/retrieve tests that mirrored the now-removed
DatabaseService.upsertChannel — misleading now that the production method is
gone. Removed the fixture method and those 3 tests; kept the independent
'Primary channel by default' test. The live async path is covered by
channels.test.ts (incl. the #1567 guard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: refactor(channels): remove dead unguarded channel upsert path (#3503)

  • Read changed files
  • Verify dead-code claims and reachability
  • Check orphaned test cleanup (previous review flagged this)
  • Assess overall code quality and correctness
  • Post final review

Summary

This is a clean, well-executed dead-code removal. The previous review's orphaned test concern has been addressed in the follow-up commit cffc368.

Dead-code verification ✅

The reachability analysis holds:

  • ChannelsRepository.upsertChannelSync — removed, zero remaining references in src/.
  • DatabaseService.upsertChannel (the legacy sync facade) — removed, zero remaining references in src/.
  • All live callers (meshtasticManager.ts, meshcoreManager.ts, server.ts, mqttIngestion.ts) correctly go through databaseService.channels.upsertChannel (the async repo method with the #1567 blank-name guard).

Tombstone comment ✅

src/services/database.ts:3682–3684 has a clear tombstone explaining why the methods were removed and pointing to the live path. This is the right pattern.

Orphaned test cleanup ✅

The second commit (cffc368) cleanly resolved the previous review's concern:

  • The test-only upsertChannel fixture in TestDatabaseService is gone.
  • The describe('Channel Operations', ...) block now contains only one surviving test: "should have Primary channel by default" (database.test.ts:584), which tests getChannelById(0) — still a live method, so this test is valid.
  • A tombstone comment at database.test.ts:590–593 explains what was removed and redirects to src/db/repositories/channels.test.ts for coverage of the live async path.

Remaining sync methods — no action needed 🔍

ChannelsRepository still has getChannelByIdSync, getAllChannelsSync, getChannelCountSync, cleanupInvalidChannelsSync, and cleanupEmptyChannelsSync. These are called by DatabaseService.getChannelById, getAllChannels, and getChannelCount on the SQLite path. This is correct and intentional — only the upsert sync path was dead; the read/cleanup sync methods still serve the sync cache-bypass path on SQLite. No issue here.

mqttIngestion.ts call site — no regression ✅

src/server/mqttIngestion.ts:959 calls databaseService.channels?.upsertChannel(...) with optional chaining and inspects the result for .catch. This correctly reaches the async guarded path and is unaffected.

Test coverage ✅

  • src/db/repositories/channels.test.ts has thorough coverage of the live ChannelsRepository.upsertChannel, including the #1567 blank-name preservation case, role enforcement, source scoping, and multi-source isolation.
  • 6527 tests pass, 0 fail.

Verdict

Approve. The removal is correct, the dead-code claim is verified, the previous review's orphaned-test concern was addressed, and the resulting code is clean and accurate.

@Yeraze Yeraze merged commit e8e9316 into main Jun 16, 2026
20 checks passed
@Yeraze Yeraze deleted the fix/3503-channel-sync-blank-name branch June 16, 2026 23:03
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.

Bug: channel name/PSK silently blanked on SQLite (sync upsert missing #1567 guard)

1 participant