fix(advice): the no-token guard must cover all three shapes of "no token"

The guard matches on the absence of a token.  `{"$exists": False}` covers
a reading with no token field, but NOT one whose token field is present
and empty.

Verified against the live database, before the fix:

    token present but empty -> write matched: 0   <-- missed it

`{"$in": [None, ""]}` covers all three - a missing field, an empty
field, and a null one - because `$in` with null also matches a missing
field.

    no token field          -> write matched: 1
    token present but empty -> write matched: 1
    token is null           -> write matched: 1

183 tests (up from 182).
This commit is contained in:
2026-11-09 19:10:16 +10:00
parent d18d037bb9
commit 4bedd23c24
2 changed files with 31 additions and 1 deletions
+4 -1
View File
@@ -278,7 +278,10 @@ class BeerRepository:
# Falling back to the gravity here would re-open the hole
# this guard exists to close - a comment-only correction
# leaves the gravity unchanged.
query["advice_token"] = {"$exists": False}
# `$in` with null also matches a MISSING field, so this
# covers a reading with no token AND one whose token field
# is present but empty.
query["advice_token"] = {"$in": [None, ""]}
elif expected_sg is not None:
# A direct call with a gravity to check against.
query["sg"] = str(expected_sg)
+27
View File
@@ -826,6 +826,33 @@ def test_a_pre_token_reading_is_still_guarded_against_a_stale_write(repo):
assert after["comment"] == "corrected comment"
def test_the_advice_is_written_for_a_reading_with_no_token_field(repo):
"""All three shapes of "no token" must work.
A reading read from the database has no token at all; one written by
an older version could have the field present but empty, or null.
The guard matches on the absence of a token, so it has to cover all
three - otherwise the advice is silently never written.
"""
for index, extra in enumerate(({}, {"advice_token": ""}, {"advice_token": None})):
reading_id = f"noToken{index:04d}"
repo.readings.insert_one({
"_id": reading_id,
"beer_run_id": str(index),
"beer_type": "Ale",
"sg": "1050",
"date": "2026-01-01",
"final_reading": "False",
"comment": "c",
**extra,
})
written = repo.set_reading_advice(reading_id, "THE ADVICE", True,
expected_sg=None,
expected_token="")
assert written == 1, f"the advice was not written for {extra!r}"
def test_a_reply_cut_off_at_the_token_ceiling_is_marked(repo):
"""A reply that stopped at the token limit is incomplete.