Skip to content

Raise unit test coverage from 52% to 95% across SDK modules #112

@zeevdr

Description

@zeevdr

Description

The Python SDK averages 52.2% test coverage against a 95% target. The test infrastructure is solid (280 functions across 15 files), but coverage of the public client APIs, compatibility checking, and core types is critically low. The largest gaps are client.py and async_client.py at ~44%, missing constructor variants, write methods, context manager protocol, and lazy version checking.

Coverage by module:

Module Coverage Key untested functionality
__init__.py 21.9% Public API exports, version constants
_compat.py 39.3% fetch_server_version(), check_version_compatible(), _parse_version(), _satisfies()
types.py 39.7% Change, FieldUpdate, ServerVersion dataclass invariants
async_client.py 43.2% Constructor variants, check_compatibility(), watch(), version-check path
client.py 44.4% Constructor variants, check_compatibility(), watch(), version-check path
_stubs.py 48.5% ensure_stubs(), make_string_typed_value(), process_get_response()
async_watcher.py 51.2% Async watch lifecycle, error recovery
watcher.py 51.8% Sync watch lifecycle, error recovery
errors.py 60.8% _parse_retry_after(), error mapping paths
_retry.py 64.2% Timeout budget exhaustion, jitter randomness
_channel.py 64.2% Token callback, credential composition
_convert.py 64.6% _parse_timedelta() edge cases, JSON/dict conversion
_interceptors.py 64.6% Metadata injection edge cases
_watcher_base.py 66.2% Callback error handling, type conversion edge cases

client.py + async_client.py (44% — highest impact)

  • Constructor matrix: TLS with token (composite credentials), TLS without token, insecure with token (UserWarning), insecure without token — both sync and async variants
  • Lazy version checking: check_version=True triggers check on first RPC, result is cached, incompatible version raises IncompatibleServerError
  • Context manager protocol: __enter__/__exit__ (sync) and __aenter__/__aexit__ (async)
  • All get() type variants: int, float, bool, timedelta, nullable str
  • Write methods: set() with and without idempotency_key, set_many(), set_null()
  • RPC error mapping for all gRPC status codes

_compat.py (39.3%)

  • _parse_version() with non-semver inputs ("dev", "main", "", "v1.2.3", pre-releases)
  • _satisfies() with single and compound constraints
  • fetch_server_version() / async_fetch_server_version() with mocked stub
  • check_version_compatible() — compatible versions pass, incompatible raise IncompatibleServerError

types.py (39.7%)

Frozen/slotted immutability (FrozenInstanceError on mutation), equality and hashing, optional field defaults for Change, FieldUpdate, ServerVersion.

Cross-cutting gaps

  • Timeout budget exhaustion in _retry.pytotal_timeout path not exercised
  • Async lifecycle edge cases: cancellation and resource cleanup on exception
  • Duration parsing compound forms in _convert.py ("1h30m", microsecond/nanosecond units)

Acceptance criteria

  • client.py + async_client.py ≥ 80%: constructor matrix (TLS/insecure × token/no-token), context manager protocol, all get() type variants, set()/set_many()/set_null() with and without idempotency_key, lazy version checking lifecycle, RPC error mapping for all gRPC status codes
  • _compat.py ≥ 80%: _parse_version() with non-semver inputs, _satisfies() with compound constraints, fetch_server_version() with mocked stub, check_version_compatible() raising IncompatibleServerError
  • types.py ≥ 80%: frozen/slotted immutability (FrozenInstanceError on mutation), equality and hashing, optional field defaults
  • __init__.py ≥ 80%: all __all__ items importable, version constants are correct strings
  • _retry.py: timeout budget exhaustion path covered
  • async_watcher.py + watcher.py: error recovery and lifecycle paths covered
  • _convert.py: compound duration parsing ("1h30m") and JSON/dict edge cases covered
  • make test passes with no regressions; overall SDK coverage ≥ 85% (stepping stone toward 95%)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: P2Nice-to-havesize: LLarger effort — multiple days, design decisions needed

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions