Skip to content

test: harden critical runtime coverage before RC #166

Description

@iriswolf

Goal

Harden test coverage around TeleFlow contracts where a defect would affect public DX, runtime reliability, or failure semantics before the first release candidate.

This work is risk-based. The target is not 100% coverage and not a larger test count for its own sake. Tests must describe observable contracts, reveal defects, and identify dead compatibility paths that should be removed instead of painted green.

Current State

GitHub-native coverage reporting and pull request gates were introduced by #18 and #164.

The trusted main baseline is:

  • 86.35% line coverage (10,268 / 11,890);
  • 76.10% branch coverage (4,347 / 5,712);
  • 1,622 uncovered executable lines;
  • 1,365 uncovered control-flow branches.

The uncovered lines are concentrated rather than evenly distributed:

Assembly Uncovered lines Line coverage
TeleFlow.Framework 809 83.81%
TeleFlow.Generators 407 88.85%
TeleFlow.Telegram.Client 121 90.99%
TeleFlow.Framework.I18n.Fluent 92 76.47%
TeleFlow.Framework.Core 57 90.52%
TeleFlow.Telegram.LongPolling 36 84.62%
TeleFlow.Telegram.Webhooks 29 74.56%

Generated schema and client files are intentionally excluded. A reported 100% for an assembly with no included executable lines is not evidence that generated DTOs are behaviorally covered.

Priority Model

Coverage work must be ordered by defect impact:

  1. request retry, cancellation, error propagation, and callback failure semantics;
  2. public helper methods that translate user intent into Telegram API requests;
  3. localization escaping and formatting boundaries;
  4. update classification parity across framework and transport paths;
  5. generator and analyzer diagnostics for supported public contracts;
  6. internal compatibility plumbing only when it represents a supported execution path.

Low coverage alone is not sufficient justification for a test. A test must protect an intentional contract.

Stage 1: Failure And Callback Contracts

Cover the failure paths most likely to produce dropped updates, duplicate work, invalid retry behavior, or misleading exception handling.

Retry and backoff

Add deterministic tests for:

  • null and invalid raw polling backoff configuration;
  • disabled backoff;
  • exponential progression and maximum-delay clamping;
  • reset semantics;
  • zero and maximum jitter boundaries without asserting random values;
  • Retry-After delta-seconds values;
  • Retry-After HTTP-date values in the past and future;
  • malformed, empty, negative, and overflowing header values;
  • envelope retry metadata taking precedence over headers;
  • cancellation while waiting through the configured TimeProvider.

Error handlers

Extend executable specifications for:

  • generated sync, Task<T>, and ValueTask<T> handlers;
  • supported handled and unhandled results;
  • unsupported result values and missing invokers;
  • exception type mismatch and context type mismatch diagnostics;
  • missing, null, nullable, and incompatible route values;
  • reflection exception unwrapping while the reflection path remains supported;
  • generated/reflection parity for behavior supported by both paths;
  • cancellation bypassing error handling where cancellation is not an application failure.

Callback data and message targets

Cover:

  • prefix validation and UTF-8 length limits;
  • constructor-to-property mapping failures;
  • property-based payload construction requirements;
  • every supported compact field type;
  • unsupported and nullable field types;
  • null values, incompatible payload instances, and invalid enum values;
  • escaping and round-trip behavior for : and %;
  • exact prefix and separator-count matching;
  • accessible, inaccessible, ephemeral, and absent callback message targets;
  • custom/raw callback formats continuing to bypass compact metadata when selected.

Stage 2: Public DX And Localization Contracts

Media helpers

Create table-driven contract tests for every supported media family rather than duplicating one test body per overload.

The matrix must cover:

  • photo, document, video, animation, audio, voice, sticker, and video note;
  • Answer* versus Reply* semantics;
  • caption and parse mode propagation where supported;
  • raw and typed reply markup paths;
  • update cancellation versus explicit cancellation;
  • ephemeral receiver and reply parameters;
  • file-id/URL payloads and at least one multipart upload contract;
  • null argument validation;
  • exact generated Telegram client method and parameter mapping.

The goal is to detect forwarding mistakes between visually similar overloads. Reflection-only assertions that methods exist are not sufficient.

Fluent localization

Cover the complete argument and rendering boundary:

  • null, strings, chars, all integral types, floating-point values, decimal values, and arbitrary IFormattable values;
  • DateTime, DateTimeOffset, DateOnly, and TimeOnly;
  • short and long DATETIME option combinations;
  • invalid date/time style combinations and unsupported option values;
  • plain, HTML, and MarkdownV2 escaping exactly once;
  • trusted TelegramFormattedText fragments with matching and mismatching parse modes;
  • custom emoji and formatted fragments inside Fluent messages;
  • message attributes and malformed message identifiers;
  • duplicate arguments, missing variables, missing messages, function failures, and empty formatted output;
  • locale fallback and cross-file references within one locale;
  • startup-loaded immutable catalogs serving repeated and concurrent reads without filesystem access.

Update classification parity

Use one shared case matrix to verify that framework, raw long polling, framework long polling, and webhook diagnostics classify every supported Telegram Update field consistently.

The test must include the unknown fallback and must make future schema additions visible as an explicit coverage or parity decision.

Stage 3: Generator, Analyzer, And Compatibility Audit

Extend tests only around supported compile-time contracts:

  • route and handler diagnostics must assert concrete diagnostic IDs;
  • source generation must cover emitted branches for supported handler, filter, callback, state, role, and error-handler shapes;
  • generated registration and supported reflection registration must have parity tests where both promise the same behavior;
  • unsupported or deprecated reflection-only paths must not gain direct constructor tests merely to increase coverage;
  • internal descriptor constructor overloads and legacy route mappings must be classified as supported, transitional, or dead;
  • dead paths discovered by coverage must be removed in a separate behavior-neutral commit with architecture tests proving the supported replacement path.

Large generator files with coverage above the aggregate floor must not receive implementation-trivia tests solely because their absolute uncovered-line count is high.

Delivery Plan

Implement this issue through three focused pull requests:

  1. failure, retry, backoff, error-handler, and callback contracts;
  2. media helpers, Fluent localization, and update classification parity;
  3. generator/analyzer gaps, generated/reflection parity, dead-path audit, and final threshold ratchet.

Each pull request must add focused semantic test files where practical instead of expanding the existing catch-all files further. Existing tests may be moved only when movement directly supports the behavior being tested; the broader physical split remains #44.

Production behavior must not change silently. If a new specification exposes a defect, add a failing regression test first and fix the defect in a separate, clearly named commit within the same pull request or a linked bug issue when the scope is larger.

Coverage Gate Policy

Keep the current required floors during Stages 1 and 2:

  • 85% aggregate lines;
  • 75% aggregate branches;
  • 80% changed executable lines on pull requests.

The hardening target is a measured baseline above:

  • 90% aggregate line coverage;
  • 82% aggregate branch coverage.

After Stage 3, raise required aggregate floors only when the measured baseline leaves a deliberate safety margin. The initial ratchet should target approximately 88% lines and 80% branches. Do not set a required floor equal to the exact observed percentage.

Do not introduce per-assembly gates in this issue. Small assemblies and diagnostic-only formatters make such gates noisy and easy to game.

Code Pointers

  • src/TeleFlow.Framework/MessageActions.Media.cs:8-685 — public media helper overloads and their common request forwarding; currently 15.4% line coverage.
  • src/TeleFlow.Framework.I18n.Fluent/Internal/FluentValues.cs:12-181 — argument conversion, Telegram escaping, trusted fragments, and temporal formatting; currently 59.3% lines and 56.0% branches.
  • src/TeleFlow.Framework.I18n.Fluent/Internal/FluentTextFormatter.cs:13-169 — Fluent resolution and public failure semantics; currently 73.7% lines and 72.7% branches.
  • src/TeleFlow.Framework/Internal/CallbackDataMetadata.cs:38-320 — compact callback metadata validation, packing, parsing, escaping, and matching; currently 73.0% lines and 56.2% branches.
  • src/TeleFlow.Framework/Internal/CallbackQueryMessageTargetResolver.cs:7-39 — accessible, inaccessible, ephemeral, and absent callback message targets; currently 57.1% lines and 50.0% branches.
  • src/TeleFlow.Framework/Internal/Handlers/TelegramErrorHandlerInvoker.cs:13-165 — generated/reflection invocation, parameter binding, route values, result validation, and exception unwrapping; currently 69.3% lines and 65.1% branches.
  • src/TeleFlow.Telegram.LongPolling/Internal/TelegramRawLongPollingBackoff.cs:10-81 — backoff validation, progression, jitter, clamp, and reset; currently 65.5% lines and 50.0% branches.
  • src/TeleFlow.Telegram.Client/Internal/TelegramRetryAfterDelayResolver.cs:7-67 — envelope and header retry-delay interpretation; currently 68.0% lines and 72.7% branches.
  • src/TeleFlow.Telegram.Webhooks/Internal/TelegramWebhookUpdateLogFormatter.cs:10-40 — update type diagnostics; currently 7.4% lines and 2.0% branches.
  • src/TeleFlow.Framework/Internal/Handlers/TelegramRouteDescriptor.cs:5-177 — legacy constructor and route-kind paths requiring a support/deletion decision, not cosmetic tests.
  • tests/TeleFlow.ArchitectureTests/TelegramRuntimeIntegrationTests.cs:1429-1639 — existing retry-after integration specifications and missing header/date edge cases.
  • tests/TeleFlow.ArchitectureTests/TelegramRuntimeIntegrationTests.cs:2734-3306 — existing photo-centric media tests to replace or extend with a media-family matrix.
  • tests/TeleFlow.ArchitectureTests/FluentI18nTests.cs:16-385 — current Fluent contract coverage.
  • tests/TeleFlow.ArchitectureTests/TelegramHandlerDispatcherTests.cs:402-641 — current error-handler behavior.
  • tests/TeleFlow.ArchitectureTests/TelegramHandlerDispatcherTests.cs:3229-3260 — current compact callback metadata coverage.
  • tests/TeleFlow.ArchitectureTests/TelegramUpdateClassificationTests.cs:8 — current framework update classification matrix and proposed parity case ownership.
  • tests/TeleFlow.ArchitectureTests/RawLongPollingTests.cs — target ownership for deterministic raw polling backoff specifications.

Acceptance Criteria

  • Every Stage 1 failure and callback scenario is represented by an observable contract test.
  • Every supported media family has request-mapping coverage for answer/reply behavior and relevant optional parameters.
  • Fluent conversion and rendering branches have explicit tests for supported values, escaping, trusted fragments, temporal values, and documented failures.
  • Update type classification is verified through one shared parity matrix across all applicable runtime paths.
  • Generator and analyzer additions assert concrete public diagnostics and generated behavior rather than implementation details.
  • Supported generated/reflection behavior has parity coverage while both paths exist.
  • Dead compatibility paths discovered during the audit are removed or explicitly documented as supported.
  • No test exists solely to execute a trivial accessor, attribute property, or private constructor.
  • The measured baseline exceeds 90% lines and 82% branches, or the final pull request documents the specific risk-based reason a lower result is acceptable.
  • Required aggregate floors are ratcheted only after the final measured baseline is stable.
  • The full suite, fast coverage suite, strict analyzers, formatting checks, and package smoke suite pass.
  • Coverage interpretation documentation is updated if scope, exclusions, or thresholds change.

Non-Goals

  • Requiring 100% coverage.
  • Covering generated Telegram schema DTO accessors.
  • Adding tests that assert internal implementation trivia.
  • Performing the full physical test-suite split tracked by Split large architecture test files by semantic area #44.
  • Removing reflection registration without the separate deprecation/removal decision.
  • Adding per-assembly percentage gates.
  • Weakening package smoke, cross-platform tests, minimum-SDK checks, or changed-line coverage.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETPull requests that update .NET codearea:ciCI, workflows, packaging, and release infrastructurearea:clientTelegram Bot API client, transport, defaults, and request executionarea:frameworkHandler framework, dispatcher, filters, callbacks, and contextsarea:generatorSource generator and analyzer behaviorarea:transportLong polling, webhooks, and raw transport adaptersenhancementNew feature or requestpriority:p1High priority workstatus:readyReady for implementation or review

    Type

    No type

    Projects

    Status
    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions