feat: Connectivity pre-check on weather API failures - #63
Conversation
When an API call to open-meteo.com fails (non-success status or exception), probe connectivitycheck.gstatic.com with a 2s timeout HEAD request to distinguish 'user is offline' from 'weather API unreachable/blocked'. - Add ProbeConnectivityAsync helper with 2s timeout - Probe on all 6 failure paths (3 status + 3 exception) - Add internal constructor for test injection - Rethrow OperationCanceledException instead of swallowing - Add 6 connectivity probe tests - Add resource strings for diagnostic messages Closes #58 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Murdock — xUnit Test Engineer 🔴 One blocking test bug, otherwise LGTMOpenMeteoService.cs ✅Probe implementation is correct.
Shared HttpClient — no risk. The probe uses Minor (non-blocking): The probe adds Minor (non-blocking): The bare ConnectivityProbeTests.cs — 5 of 6 tests are correct🔴 Blocking: The test pre-cancels the token and expects Assert.AreEqual(1, callCount, "Cancelled request should not trigger probe");But Fix: Change to // Was: Assert.AreEqual(1, callCount, "Cancelled request should not trigger probe");
Assert.AreEqual(0, callCount, "Pre-cancelled request: handler never reached, probe should not fire");Remaining 5 tests ✅
Non-blocking gap: Resource strings ✅Five new strings: One-line fix needed: |
Pre-cancelled token throws before handler lambda runs, so callCount is 0, not 1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Team Lead Review — Amy AllenMurdock's blocker (callCount 0 vs 1 in cancel test) fixed in eebdb4a. My review:Architecture: Clean additive change. ProbeConnectivityAsync is private, 2s timeout via linked CTS, HEAD request to well-known neutral endpoint. No impact on happy path. Safety: OperationCanceledException rethrow is the right call — all callers (WeatherListPage, CurrentWeatherBand, WeatherBandCard) already catch OCE. The old behavior silently returned null, hiding cancellation from callers. Shared HttpClient: Safe — probe uses per-request headers, timeout via CTS not client-level. Non-blocking note about duplicate User-Agent is cosmetic. Test coverage: 6 tests cover all paths — 500 status, exception, all 3 endpoints, success (no probe), cancel (no probe). The callCount=0 fix is correct because CountingHttpHandler checks cancellation before invoking the factory. Non-blocking: The bare ✅ Approved from the team lead. |
|
Murdock — re-review after commit eebdb4a Blocker resolved. ✅
All 6 tests are now correct. PR is clean. 🚬 |
There was a problem hiding this comment.
Pull request overview
Adds a lightweight connectivity probe (HEAD to connectivitycheck.gstatic.com/generate_204 with a 2s timeout) on every failure path in OpenMeteoService so logs can distinguish a fully offline user from one whose access to open-meteo is blocked/unreachable. Also introduces an internal test-friendly constructor on OpenMeteoService and adds tests exercising the probe across the three weather endpoints.
Changes:
- Add
ProbeConnectivityAsyncand wire it into all six non-success / exception paths inOpenMeteoService; rethrowOperationCanceledExceptioninstead of swallowing it. - Add five new diagnostic resource strings (probe outcome messages + endpoint labels).
- Add
ConnectivityProbeTestscovering probe-on-500, probe-on-exception, probe-on-success (no probe), and cancellation behavior.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| WeatherExtension/Services/OpenMeteoService.cs | Probe helper + invocation on all failure paths and a test-only constructor |
| WeatherExtension/Properties/Resources.resx | New diagnostic strings for probe outcomes and endpoint labels |
| WeatherExtension/Properties/Resources.Designer.cs | Generated accessors for the new strings (hand-edited, with malformed XML doc structure) |
| WeatherExtension.Tests/ConnectivityProbeTests.cs | New tests for probe behavior across endpoints and on success/cancel paths |
Files not reviewed (1)
- WeatherExtension/Properties/Resources.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…esigner.cs - Fix malformed XML doc comments in Resources.Designer.cs (duplicate <summary> before connectivity_api_blocked; missing <summary> before celsius) - Validate probe returns HTTP 204 (NoContent) before logging API-blocked — captive portals returning 200/302 now treated as no-internet - Split bare catch in ProbeConnectivityAsync: caller cancellation (ct fired) returns silently; probe timeout and network errors log no-internet Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous run reran at the original SHA before the fix was pushed. This commit triggers a fresh CI run against b114b42 which adds the missing using System.Threading.Tasks directive.
Ensures CI picks up the using System.Threading.Tasks fix (b114b42). Previous gh run rerun executed at the old failing SHA.
|
|
||
| using System.Net; |
|
Temporarily closing to force CI re-trigger on latest commits (CS0246 fix present on branch but CI not triggering for new pushes) |
…oth) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds using System.Net.Http and using System.Threading to resolve CS0246/CS0103 build errors for HttpResponseMessage, CancellationToken, CancellationTokenSource, HttpRequestException, StringContent, and OperationCanceledException. Also reverts workflow_dispatch trigger added as a temporary CI workaround — no longer needed now that all usings are explicit and self-sufficient.
| Assert.IsNull(result); | ||
| // 1 failed API call + 1 probe attempt (also fails since same handler throws) = 2 | ||
| Assert.AreEqual(2, callCount, "Expected API call + connectivity probe attempt"); | ||
| } |
OperationCanceledException is in System namespace — not available without explicit using in this project (no implicit usings configured).
Assert.ThrowsExceptionAsync<T> checks exact type, but HttpClient wraps OperationCanceledException as TaskCanceledException (a subtype) when propagating through async Task plumbing. Switch to try/catch with catch (OperationCanceledException) to accept any cancellation subtype, which correctly matches the semantic intent of the test.
Summary
Adds a lightweight connectivity probe when weather API calls fail, so we can distinguish "user is offline" from "weather API unreachable/blocked in region".
How it works
connectivitycheck.gstatic.comwith a 2-second timeoutWeather API unreachable — may be blocked in your region(Error level)No internet connection(Warning level)Changes
OpenMeteoService.cs: AddedProbeConnectivityAsynchelper, called on all 6 failure paths. Addedinternalconstructor for test injection.OperationCanceledExceptionnow rethrown instead of swallowed (callers already handle it).Resources.resx/Designer.cs: 5 new resource strings for diagnostic messagesConnectivityProbeTests.cs: 6 tests covering probe-on-500, probe-on-exception, no-probe-on-success, no-probe-on-cancel, all 3 endpointsRisk
OperationCanceledExceptionrethrow is a behavior change but all callers already catch itCloses #58