6.8 KiB
Batch Report
Batch: 21
Tasks: AZ-374 (C21 — typed/named HttpClient for Google Maps in DI)
Date: 2026-05-11
Run: 03-code-quality-refactoring
Cycle: 1
Task Results
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|---|---|---|---|---|---|
| AZ-374_refactor_typed_httpclient_googlemaps | Done | 3 src/test = 3 files | 175 unit + 5 smoke pass | 3/3 covered | None |
Design note: registration location
The task spec recommended adding AddHttpClient("GoogleMapsTiles", ...) in Program.cs. I placed it inside TileDownloaderServiceCollectionExtensions.AddTileDownloader() instead. Rationale (per coderule.mdc SRP guidance):
- The named-client name, User-Agent, and timeout are all TileDownloader-internal concerns. Their owner is the component that resolves them —
GoogleMapsDownloaderV2. The DI extension already encapsulates everything else TileDownloader needs (memory cache,ISatelliteDownloader,ITileService). - Placing this in
Program.cswould have spread component-internal knowledge into the application entry-point file. The extension was created precisely to avoid that. - The bare
builder.Services.AddHttpClient()call inProgram.cs:36was kept untouched — it is now structurally redundant (the named-client registration also wires upIHttpClientFactory), but removing it is outside this task's scope.
The named-client name itself ("GoogleMapsTiles"), the User-Agent header value, and the 100-second default timeout are now internal consts on GoogleMapsDownloaderV2 so the extension method and the downloader resolve from the same source of truth.
Changes
Modified production code
SatelliteProvider.Services.TileDownloader/GoogleMapsDownloaderV2.cs- Promoted
private const USER_AGENT→internal const UserAgent(PascalCase; visibility to allow the extension method in the same assembly to reference it). - Added
internal const string GoogleMapsTilesClientName = "GoogleMapsTiles";. - Added
internal const int DefaultHttpClientTimeoutSeconds = 100;(matchesHttpClient's implicit default; preserves prior behavior). - Three
CreateClient()call sites updated toCreateClient(GoogleMapsTilesClientName):GetSessionToken(session-token POST)DownloadSingleTileAsyncretry lambda (single-tile GET)PerformDownloadAsyncretry lambda (batch-tile GET)
- Removed the two per-call
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(USER_AGENT);lines — the header is now set once at registration.
- Promoted
SatelliteProvider.Services.TileDownloader/TileDownloaderServiceCollectionExtensions.cs- Added
services.AddHttpClient(GoogleMapsDownloaderV2.GoogleMapsTilesClientName, c => { c.DefaultRequestHeaders.UserAgent.ParseAdd(GoogleMapsDownloaderV2.UserAgent); c.Timeout = TimeSpan.FromSeconds(GoogleMapsDownloaderV2.DefaultHttpClientTimeoutSeconds); });.
- Added
Unchanged (deliberate)
SatelliteProvider.Api/Program.cs—builder.Services.AddHttpClient();kept. See "Design note" above; cleanup deferred.- Per-call
User-Agentsetup is removed; the named client carries the header on everyCreateClient(...)resolution.
Modified tests
SatelliteProvider.Tests/TileServiceTests.cs- Added
Microsoft.Extensions.DependencyInjectionusing. - New
TileDownloaderRegistrationTests.AddTileDownloader_RegistersNamedGoogleMapsTilesHttpClient_AZ374_AC1— builds aServiceCollection, callsAddTileDownloader(), resolvesIHttpClientFactory, asks for"GoogleMapsTiles", asserts the returned client carries aMozilla/5.0...-style User-Agent and a 100-secondTimeout. Covers AC-1 directly and AC-2 transitively (the only consumer ofCreateClient(GoogleMapsTilesClientName)isGoogleMapsDownloaderV2).
- Added
AC Test Coverage
| AC | Covered by |
|---|---|
| AC-1 (single registration with User-Agent + Timeout) | AddTileDownloader_RegistersNamedGoogleMapsTilesHttpClient_AZ374_AC1 |
AC-2 (downloader uses CreateClient("GoogleMapsTiles") everywhere) |
Source-level: the only CreateClient( occurrences in GoogleMapsDownloaderV2.cs reference GoogleMapsTilesClientName. Behavioral: smoke tests GetTileByLatLon + region/route tile downloads succeed end-to-end against the real Google Maps API, exercising all three call sites (session token, single-tile, batch-tile). |
| AC-3 (tests stay green) | scripts/run-tests.sh --smoke reports 175 unit + 5 smoke. |
Test Run
| Suite | Result | Count |
|---|---|---|
Unit (SatelliteProvider.Tests) |
All passed | 175 (was 174; +1 new test for the DI registration) |
| Smoke integration (Docker) | All passed | 5 scenarios |
Code Review Verdict: PASS
Inline review covered:
- Architecture: Named-client registration co-located with the consumer in
AddTileDownloader(); no leakage into the WebApi entry point. SRP improvement over the task-spec recommendation, with rationale recorded above and verified againstcoderule.mdc. - Behavior preservation: User-Agent value byte-identical (
Mozilla/5.0 (Windows NT 10.0; …)); timeout preserved atHttpClient's implicit default (100 s); retry/backoff unchanged; session-token / single-tile / batch-tile code paths all confirmed via the smoke tile-download scenarios. - AC coverage: 3/3 with at least one running test per AC; AC-2 also relies on the structural fact that there is no other
CreateClient(inGoogleMapsDownloaderV2. - Code quality: SRP preserved; no narrative comments; consts named per C# conventions; A/A/A test pattern; no suppressed errors; argument validation unaffected.
- Cross-batch consistency: extends the AZ-371 (C18 magic-numbers-to-config) theme by adding a third configuration-style constant cluster; consistent with the AZ-364 collaborator-extraction pattern (small, well-named extension surfaces); AZ-370 enums (batch 19) and AZ-373 MapsVersion drop (batch 20) untouched.
Findings: None.
Auto-Fix Attempts: 0
Stuck Agents: None
Cumulative review — K=3 trigger
Batches 19, 20, 21 have completed since the last cumulative review (cumulative_review_batches_16-18_cycle1_report.md). The K=3 trigger now fires. Cumulative review will be run as the next step (autodev sub_step cumulative-review), targeting the union of files changed in batches 19–21.
Next Batch
After the cumulative review passes, Phase 4 continues with the remaining 7 tasks in todo/:
AZ-375— C22 O(N) existing-tile lookup (2 SP, needs AZ-371 ✓)AZ-376— C23 delete unusedFindExistingTileAsync(1 SP)AZ-377— C24 consolidate Earth-geometry constants (2 SP, needs AZ-371 ✓)AZ-378— C25 repo_loggerfields (1 SP)AZ-379— C26 repo SELECT column-list constants (2 SP)AZ-380— C27 deleteCalculatePolygonDiagonalDistance(1 SP)AZ-372— C19 dotnet format + analyzers + Coverlet (3 SP)