Skip to content

Sixteen live-test finally blocks still clean up on the body's connection and throw, so a failing test can report the cleanup's error instead of its own #1896

Closed

Description

@erikdarlingdata

Found by the project-wide cleanup audit done for #1873, and deliberately left out of that PR: it is a different defect from the one #1873 repairs, and fixing it means editing sixteen finally blocks whose tests I would be changing the failure semantics of.

What #1873 fixed, and what it did not

#1873 was about cleanup that fails silently — an empty catch in TimescaleSupportTests and a lone Console.WriteLine in PayloadDimensionLiveTests. Those are gone: removals are verified against the catalog, residue is recorded with the owning test's name, and the live-postgres collection fixture fails the run at teardown.

The audit also found the opposite shape, and it is still there. Sixteen finally blocks run their cleanup loudly — through ExecAsync/ExecuteAsync, which throw — but they run it on the body's connection, and they throw from a finally:

  • TimescaleSupportTests — 10 finally blocks
  • DarlingSecuritySplitLiveTests — 6 finally blocks (:152, :157, :211, :252, :358, :412, :460)

Why that is a defect and not just a style

This is exactly the shape #1794 was filed for, and LiveStoreCleanup was written to end. A throw from a finally replaces the body's in-flight exception, so when the test has genuinely failed, the reported failure is a cleanup error instead of the real one — and the remaining cleanup statements after the throwing one never run, so the store is left dirty by the very path that was supposed to tidy it. The failure that chased #1794 was InvalidOperationException: Connection is not open standing in front of a real diagnosis, and the body's connection is precisely the thing a failing test may have destroyed.

LiveStoreCleanup.RunAsync already solves all of it: a freshly-opened connection with its own lifetime, CancellationToken.None so a cancelled run still restores, and catch when (!bodySucceeded) — silent when the body already failed and the body's exception is the story, loud when the body succeeded and a cleanup that cannot do its job must not be quiet. Roughly twenty call sites across the suite are on it already (PayloadDimensionLiveTests ×11, DarlingSelfAlertTests, DarlingEmptyEnumerationInventoryTests, DarlingCollectionHealthLatestNoteTests, DarlingMcpJobToolsTests).

Not urgent, and here is the honest reason

#1873's collection-teardown residue check now catches the consequence: anything these blocks abandon shows up as a leaked relation at the end of the run, with the run failing and the relation named. So the debris no longer goes unnoticed. What remains uncovered is the masking — a failing test in one of these classes can still report the wrong exception, which is a diagnosis cost rather than a correctness one.

The work

Convert the sixteen blocks to the bodySucceeded + LiveStoreCleanup.RunAsync shape the file's own siblings use. It is mechanical, but it is sixteen tests' failure semantics, so it wants its own PR and its own gated-live verification rather than riding along with an unrelated change.

Activity

  1. added 2 commits that reference this issue on Jul 31, 2026
  2. erikdarlingdata commented on Jul 31, 2026

    @erikdarlingdata
    OwnerAuthor

    Shipped in #1903: all sixteen converted (ten TimescaleSupportTests, six DarlingSecuritySplitLiveTests), the two session-scoped sites via a new RunOwnedAsync that keeps the owning connection (a fresh one would report false success while leaving the real session altered), and LiveStoreCleanup itself - six lines dozens of tests ride on - finally has tests, mutation-checked both directions. The reported-error proof: a forced body+cleanup double failure reports the BODY's error after, the cleanup's before. The ~124 row-level siblings found by the sweep are #1902, batched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions