diff --git a/src/flask/mongo/repository.py b/src/flask/mongo/repository.py index e223f35..f781ada 100644 --- a/src/flask/mongo/repository.py +++ b/src/flask/mongo/repository.py @@ -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) diff --git a/tests/test_brew_advice.py b/tests/test_brew_advice.py index 0a74a15..3879299 100644 --- a/tests/test_brew_advice.py +++ b/tests/test_brew_advice.py @@ -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.