Skip to content

fix: a history purge reports what it removed - #878

Open
blaipr wants to merge 2 commits into
mainfrom
fix/a-history-purge-reports-what-it-removed
Open

fix: a history purge reports what it removed#878
blaipr wants to merge 2 commits into
mainfrom
fix/a-history-purge-reports-what-it-removed

Conversation

@blaipr

@blaipr blaipr commented Aug 24, 2026

Copy link
Copy Markdown
Member

AccountHistory::delete() throws when the row it was given did not exist. The batch
beside it returned the affected count and left it there, and both callers — the history
manager's delete and the bulk edit's "delete history" option — discard the return and
answer success regardless.

So selecting a set of history entries to destroy, where some of them no longer matched —
a stale id, a row another session had already removed — was reported as a purge that had
happened. History is where an account's previous passwords live, so the thing the operator
believes they destroyed is precisely the thing that is still there.

deleteByIdBatch() is given history row ids, so every one of them should have matched, and
it now throws when the count differs — the check the single delete has always made and
the one PublicLink::deleteByIdBatch() makes for the same reason.

deleteByAccountIdBatch() is deliberately left alone, and now says so: those are account
ids, and an account may have no history at all, so removing nothing from one is the right
answer rather than a failure. There is no count there to compare against, and holding it
to the same rule would fail a purge that had done exactly what it was asked.

Checked by making the comparison always false: the new test fails and the one pinning the
account-id case does not.

blaipr added 2 commits August 24, 2026 09:05
`AccountHistory::delete()` throws when the row it was given did not exist. The batch
beside it returned the affected count and left it there, and both callers — the history
manager's delete and the bulk edit's "delete history" option — discard the return and
answer success regardless.

So selecting a set of history entries to destroy, where some of them no longer matched —
a stale id, a row another session had already removed — was reported as a purge that had
happened. History is where an account's previous passwords live, so the thing the operator
believes they destroyed is precisely the thing that is still there.

deleteByIdBatch() is given history row ids, so every one of them should have matched, and
it now throws when the count differs — the check the single delete has always made and
the one PublicLink::deleteByIdBatch() makes for the same reason.

deleteByAccountIdBatch() is deliberately left alone, and now says so: those are *account*
ids, and an account may have no history at all, so removing nothing from one is the right
answer rather than a failure. There is no count there to compare against, and holding it
to the same rule would fail a purge that had done exactly what it was asked.

Checked by making the comparison always false: the new test fails and the one pinning the
account-id case does not.
The integration harness answers every statement with one affected row, so a delete given
three history ids reported one — which the service now reads, correctly, as a purge that
did not remove what it was asked to.

The double answers three for the delete in that test. This is the harness modelling the
server rather than the test being bent around the change: a real DELETE matching three
rows reports three, and the whole point of the check is that anything less is not a purge
that happened.
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.

1 participant