Skip to content

Increase test coverage to 98%: add comprehensive unit tests for client/device, retries, and export paths#4

Merged
devinslick merged 1 commit intomainfrom
increase-code-test-coverage
Nov 6, 2025
Merged

Increase test coverage to 98%: add comprehensive unit tests for client/device, retries, and export paths#4
devinslick merged 1 commit intomainfrom
increase-code-test-coverage

Conversation

@devinslick
Copy link
Copy Markdown
Owner

Summary

  • Raises repository test coverage from ~81% to 98%
  • Adds 45+ targeted unit tests across fmd_api.client and fmd_api.device
  • Focuses on previously untested error paths, retry/backoff logic, and export edge cases
  • No production code changes; tests only

What changed

  • New tests in tests/unit/test_coverage_improvements.py:
    • Authentication flow: salt retrieval, password hashing, access token, private key decryption
    • Retry/backoff: 429 with Retry-After, 500/502/503/504, connection errors, jitter/no-jitter backoff
    • JSON parsing fallbacks: non-JSON responses, empty body, missing keys
    • Export ZIP: PNG/JPEG detection, default jpg fallback for unknown formats, decrypt errors
    • Device edge cases: photo download decode errors, internal guards
    • Utilities: token masking, Retry-After parsing, streaming responses
  • Coverage artifacts: coverage.xml generated for Codecov

Why

  • Address Codecov alerts and improve confidence in critical paths (auth, I/O, error handling)
  • Reduce regressions by covering negative and edge scenarios

Metrics

  • Before: ~81% total coverage (client.py ~77%, device.py ~94%)
  • After: 98% total coverage (client.py ~97%, device.py 100%)
  • Tests: 104 tests passing locally

Compatibility

  • Breaking changes: None
  • Public API changes: None
  • Dependencies: No new runtime dependencies

How to validate locally (optional)

# From repo root (Windows PowerShell)
python -m pytest tests/unit --cov=fmd_api --cov-report=term-missing --cov-branch

Notes and follow-ups

  • Remaining uncovered lines are hard-to-trigger branches in client.py (e.g., specific fallback/log-only segments): 353–358, 368, 392–393, 477–478, 563, 717–718
  • Reaching 100% would likely require deeper aiohttp response mocking or small refactors to expose those branches for deterministic testing; can be tackled in a follow-up if desired

Checklist

  • All unit tests pass locally
  • Coverage ≥ 95% (actual: 98%)
  • No production code changes
  • coverage.xml updated for CI/Codecov

…verage to 98%. The tests cover various edge cases and error handling in client.py and device.py, including authentication flows, connection error retries, JSON parsing, and response handling. Additional tests ensure robustness against invalid inputs and unexpected server responses.
Copilot AI review requested due to automatic review settings November 6, 2025 16:22
@devinslick devinslick merged commit dce625f into main Nov 6, 2025
25 of 29 checks passed
@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds comprehensive test coverage improvements to achieve 95%+ code coverage, focusing on uncovered branches and edge cases in client.py and device.py. The version is also bumped to 2.0.2.

Key Changes

  • Added 1,221 lines of new tests covering authentication helpers, JSON parsing fallback paths, connection error retry logic, export_data_zip edge cases, and device.py missing lines
  • Improved branch coverage from 0% to 96.23% and line coverage from 82.6% to 98.05%
  • Tests cover error handling paths, retry mechanisms, encryption/decryption edge cases, and helper function branches

Reviewed Changes

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

File Description
tests/unit/test_coverage_improvements.py New comprehensive test file covering previously untested branches and edge cases
fmd_api/_version.py Version bump to 2.0.2
coverage.xml Updated coverage metrics showing improved coverage
.coverage Binary coverage data file

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


@pytest.mark.asyncio
async def test_device_get_history_decrypt_error():
"""Test Device.get_history handles decrypt errors (line 99-101)."""
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

The comment references "line 99-101" but these line numbers don't correspond to the actual device.py file being tested. The get_history method starts at line 69 in device.py. Update the comment to reference the correct line numbers or remove specific line references for clarity.

Suggested change
"""Test Device.get_history handles decrypt errors (line 99-101)."""
"""Test Device.get_history handles decrypt errors."""

Copilot uses AI. Check for mistakes.

@pytest.mark.asyncio
async def test_device_download_photo_decode_error():
"""Test Device.download_photo handles decode errors (line 137-138)."""
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

The comment references "line 137-138" but the download_photo method in device.py starts at line 115. Update the comment to reference the correct line numbers or remove specific line references for clarity.

Suggested change
"""Test Device.download_photo handles decode errors (line 137-138)."""
"""Test Device.download_photo handles decode errors."""

Copilot uses AI. Check for mistakes.

# With enough samples, should have some variation (not all the same)
# (This might fail in rare cases but is statistically very unlikely)
assert len(set(delays)) > 1 or delays[0] == 0 # Allow all zeros as edge case
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

This assertion is testing randomness which can be flaky. While the comment acknowledges this ("This might fail in rare cases but is statistically very unlikely"), such tests can cause intermittent CI failures. Consider using a fixed seed for the random number generator or mocking the random function to make the test deterministic.

Copilot uses AI. Check for mistakes.
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp:
output_path = tmp.name

result = await client.export_data_zip(output_path, include_pictures=True)
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

Variable result is not used.

Suggested change
result = await client.export_data_zip(output_path, include_pictures=True)
await client.export_data_zip(output_path, include_pictures=True)

Copilot uses AI. Check for mistakes.
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp:
output_path = tmp.name

result = await client.export_data_zip(output_path, include_pictures=False)
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

Variable result is not used.

Suggested change
result = await client.export_data_zip(output_path, include_pictures=False)
await client.export_data_zip(output_path, include_pictures=False)

Copilot uses AI. Check for mistakes.
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.

3 participants