Skip to content

Make VPN toggle actions match displayed VPN state#8887

Open
atavism wants to merge 2 commits into
mainfrom
atavism/issue-3636
Open

Make VPN toggle actions match displayed VPN state#8887
atavism wants to merge 2 commits into
mainfrom
atavism/issue-3636

Conversation

@atavism

@atavism atavism commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Resolves https://github.com/getlantern/engineering/issues/3636

Fixes a VPN toggle mismatch where the UI could show the VPN as off or errored, but tapping the toggle would try to disconnect instead of reconnect.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Improved VPN state handling with safer initialization and more reliable recovery from status/connection errors.
    • Added automated tests covering VPN initial hydration, stream error handling, and recovery behavior.
  • Bug Fixes

    • Made Lantern status parsing null-safe and more defensive when the status field is missing.
    • Improved VPN status stream error handling to log issues and surface a recoverable error state.
    • Refined the macOS VPN retry flow to await dialog results and only proceed when explicitly approved.

Copilot AI review requested due to automatic review settings June 25, 2026 22:19
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 029570fc-1e23-4790-94cb-28d3eeee015a

📥 Commits

Reviewing files that changed from the base of the PR and between 8321690 and f9dc1ec.

📒 Files selected for processing (4)
  • lib/features/vpn/provider/vpn_notifier.dart
  • lib/features/vpn/provider/vpn_status_notifier.dart
  • lib/features/vpn/vpn_switch.dart
  • test/features/vpn/provider/vpn_notifier_test.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/features/vpn/vpn_switch.dart
  • lib/features/vpn/provider/vpn_notifier.dart

📝 Walkthrough

Walkthrough

VPN status parsing now tolerates missing JSON values, the VPN status stream and notifier handle errors explicitly, the notifier hydrates initial state from the service, the macOS switch flow awaits dialog results, and tests cover hydration and error propagation.

Changes

VPN status flow

Layer / File(s) Summary
Status parsing and stream errors
lib/core/models/lantern_status.dart, lib/features/vpn/provider/vpn_status_notifier.dart
LanternStatus.fromJson accepts a missing status value, and VPNStatusNotifier reads the VPN status stream with error logging and VPNStatus.error emission.
Notifier hydration and listener handling
lib/features/vpn/provider/vpn_notifier.dart
VpnNotifier handles stream errors and empty emissions, and hydrates its initial VPN state from isVPNConnected().
Toggle routing and macOS retry
lib/features/vpn/provider/vpn_notifier.dart, lib/features/vpn/vpn_switch.dart
onVPNStateChange() now switches between stopVPN() and startVPN(), and vpn_switch awaits the macOS dialog result before retrying the toggle.
Notifier tests
test/features/vpn/provider/vpn_notifier_test.dart
VpnNotifier tests cover initial hydration, error-state toggling, and stream error propagation with a fake service.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #3636: Matches the VPN toggle/state alignment change by hydrating initial VPN state, handling stream errors, and making non-connected states reconnectable.

Poem

🐰 I hopped through the VPN glen,
Found errors, then returned again.
JSON stayed soft, streams sang bright,
Toggles bounced with cleaner light,
And tests went thump-thump: all is right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: aligning VPN toggle actions with the displayed VPN state.
Linked Issues check ✅ Passed The changes hydrate startup status, make error/unknown states reconnectable, and surface stream errors without null crashes, matching #3636.
Out of Scope Changes check ✅ Passed The added tests and vpn_switch updates are directly tied to the VPN toggle/state-handling fix, with no clear unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch atavism/issue-3636

Comment @coderabbitai help to get the list of available commands.

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

Adjusts VPN toggle behavior so user actions align with the VPN state shown in the UI, while hardening status handling against invalid/missing core data and stream failures.

Changes:

  • Fixes toggle action selection so “switch off” states (e.g. error) trigger startVPN() instead of stopVPN().
  • Adds initial VPN connection hydration via isVPNConnected() and improves resilience to malformed/missing status JSON.
  • Adds tests covering initial hydration, error-state toggling, and status-stream error handling.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/features/vpn/provider/vpn_notifier_test.dart Adds unit tests for initial hydration, error-state toggling, and stream error behavior.
lib/features/vpn/vpn_switch.dart Updates macOS extension dialog flow and removes BuildContext dependency from notifier toggle call.
lib/features/vpn/provider/vpn_status_notifier.dart Adds stream error handling for VPN status updates.
lib/features/vpn/provider/vpn_notifier.dart Hydrates initial status asynchronously and updates toggle logic to match displayed state.
lib/core/models/lantern_status.dart Makes fromJson tolerant of missing/null status values.

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

Comment thread lib/features/vpn/vpn_switch.dart Outdated
Comment thread lib/features/vpn/provider/vpn_status_notifier.dart Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/features/vpn/provider/vpn_notifier_test.dart (1)

118-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert recovery after a stream error.

This test proves the first error is converted, but not that the notifier keeps processing later status events. Add a follow-up event assertion to catch regressions where the status stream stops after addError.

Suggested test extension
         expect(status.hasValue, isTrue);
         expect(status.value?.status, VPNStatus.error);
         expect(status.value?.error, contains('status stream failed'));
+
+        service.statusController.add(
+          LanternStatus(status: VPNStatus.connected),
+        );
+        await _pumpProviderQueue();
+
+        expect(container.read(vpnProvider), VPNStatus.connected);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/features/vpn/provider/vpn_notifier_test.dart` around lines 118 - 142,
The vpn_notifier_test case currently verifies that a status stream error is
converted to VPNStatus.error, but it does not confirm recovery afterward; extend
the test around vPNStatusProvider and vpnProvider by emitting a later valid
status event from _FakeLanternService after addError and asserting the notifier
updates to that new status. This should be added in the existing “status stream
errors become VPNStatus.error instead of crashing” test so it catches
regressions where the stream stops processing after an error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/vpn/provider/vpn_notifier.dart`:
- Around line 23-41: The VPN notifier hydration flow can overwrite a real
stream-driven status with a stale initial result. Update VPNNotifier’s hydration
logic around _hydrateInitialStatus and the ref.listen handler so the first
status emitted from vPNStatusProvider is treated as authoritative and any later
isVPNConnected() fallback only applies if the stream has not emitted yet. Add a
guard in VPNNotifier to track whether the stream has produced a value before
assigning state from hydration, and ensure disconnected fallback does not
replace an already observed connected/disconnected status.
- Around line 101-119: Catch exceptions thrown by isVPNConnected() inside
_hydrateInitialStatus() before the result.fold handling, since unawaited callers
won’t observe them and the provider can stay stuck in the fallback state. Update
vpn_notifier.dart’s _hydrateInitialStatus method to wrap the
await/ref.read(lanternServiceProvider).isVPNConnected() call in error handling,
log the thrown failure with appLogger.error, and transition state to
VPNStatus.error when appropriate, while still preserving the existing Left/right
fold behavior for normal results.

In `@lib/features/vpn/provider/vpn_status_notifier.dart`:
- Around line 13-24: The outer try/catch around the await for in
vpn_status_notifier.dart causes the stream subscription to terminate on the
first error, so update vpnStatusNotifier/VPN status handling to keep listening
after recoverable failures. Handle errors inside the statusStream consumption
path or re-establish the subscription after logging in appLogger.error, and keep
yielding LanternStatus error states without exiting the notifier loop.

---

Nitpick comments:
In `@test/features/vpn/provider/vpn_notifier_test.dart`:
- Around line 118-142: The vpn_notifier_test case currently verifies that a
status stream error is converted to VPNStatus.error, but it does not confirm
recovery afterward; extend the test around vPNStatusProvider and vpnProvider by
emitting a later valid status event from _FakeLanternService after addError and
asserting the notifier updates to that new status. This should be added in the
existing “status stream errors become VPNStatus.error instead of crashing” test
so it catches regressions where the stream stops processing after an error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a9466ca5-415d-494d-aa2d-19acaa713859

📥 Commits

Reviewing files that changed from the base of the PR and between d7564c8 and 8321690.

📒 Files selected for processing (5)
  • lib/core/models/lantern_status.dart
  • lib/features/vpn/provider/vpn_notifier.dart
  • lib/features/vpn/provider/vpn_status_notifier.dart
  • lib/features/vpn/vpn_switch.dart
  • test/features/vpn/provider/vpn_notifier_test.dart

Comment thread lib/features/vpn/provider/vpn_notifier.dart
Comment thread lib/features/vpn/provider/vpn_notifier.dart
Comment thread lib/features/vpn/provider/vpn_status_notifier.dart Outdated
@atavism atavism requested review from jigar-f and myleshorton June 26, 2026 00:35
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.

2 participants