From 8e0a4bddabc253ae148ef8b58571cd28e312dae5 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Thu, 17 Sep 2026 09:53:59 +0200 Subject: [PATCH] Refresh the phone weight from run_sync, where the PC's event-driven sync actually passes Co-Authored-By: Claude Opus 5 (1M context) --- app/pubspec.yaml | 2 +- diet_guard/_cli_sync.py | 7 ------- diet_guard/_sync.py | 6 ++++++ diet_guard/tests/conftest.py | 5 +++++ diet_guard/tests/test_cli_sync.py | 16 +--------------- diet_guard/tests/test_sync_budget.py | 18 ++++++++++++++++++ 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/app/pubspec.yaml b/app/pubspec.yaml index e92b060..a973a92 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -2,7 +2,7 @@ name: diet_guard_app description: "Companion phone app for diet_guard: log meals on the go." publish_to: 'none' -version: 1.0.84+1 +version: 1.0.85+1 environment: sdk: ^3.12.2 diff --git a/diet_guard/_cli_sync.py b/diet_guard/_cli_sync.py index 6b496f6..e28f387 100644 --- a/diet_guard/_cli_sync.py +++ b/diet_guard/_cli_sync.py @@ -27,10 +27,6 @@ _LAZY_ATTRS = { "run_sync": ("diet_guard._sync", "run_sync"), "SyncError": ("diet_guard._sync", "SyncError"), "RemoteSyncError": ("crdt_sync", "RemoteSyncError"), - "refresh_weight_from_phone": ( - "diet_guard._phone_weight", - "refresh_weight_from_phone", - ), } @@ -82,7 +78,4 @@ def cmd_sync(emit: Callable[[str], None]) -> int: return 1 total_entries = sum(len(entries) for entries in merged.values()) emit(f"synced: {total_entries} entries across {len(merged)} day(s).") - # After the merge, never before: the local record is then already the - # merged winner, so refreshing its ``t`` re-asserts nothing stale. - module.refresh_weight_from_phone(emit) return 0 diff --git a/diet_guard/_sync.py b/diet_guard/_sync.py index b0d9dd5..727419d 100644 --- a/diet_guard/_sync.py +++ b/diet_guard/_sync.py @@ -37,6 +37,7 @@ from diet_guard._constants import ( ) from diet_guard._device import device_identity from diet_guard._foodbank_rebuild import rebuild_food_bank +from diet_guard._phone_weight import refresh_weight_from_phone from diet_guard._state_sync import ( read_raw_log, resign_entry, @@ -202,6 +203,11 @@ def run_sync() -> DayLog: message="diet_guard sync: revision", ) state_store.save(SyncState(pushed_rev=revision, peer_revs=seen_revs)) + # Last, after the budget merge above: the local record is then already + # the merged winner, so the refreshed ``t`` re-asserts nothing stale. + # Here rather than in the ``sync`` subcommand because on the PC a full + # tick runs from ``publish_after_log``, never from that command. + refresh_weight_from_phone(lambda line: _logger.info("%s", line)) return resigned diff --git a/diet_guard/tests/conftest.py b/diet_guard/tests/conftest.py index 9dd9a67..65b773b 100644 --- a/diet_guard/tests/conftest.py +++ b/diet_guard/tests/conftest.py @@ -162,6 +162,11 @@ def _isolate_state(tmp_path: Path) -> Iterator[None]: "diet_guard._sync_client.CONFIG_FILE", tmp_path / "nonexistent-firebase.json", ), + # Every full tick ends by reading the phone's weigh-in through + # `wake_alarm._weight`, which is installed here and reads the live + # Firebase database. `test_sync_budget` asserts the call; nothing + # else may pay for it. + patch("diet_guard._sync.refresh_weight_from_phone", return_value=None), # Logging a meal now publishes immediately (`_sync_events`), so every # test that logs one would otherwise open a real connection through a # path that has nothing to do with what it is asserting. Patched at the diff --git a/diet_guard/tests/test_cli_sync.py b/diet_guard/tests/test_cli_sync.py index 4126914..38a11d8 100644 --- a/diet_guard/tests/test_cli_sync.py +++ b/diet_guard/tests/test_cli_sync.py @@ -19,23 +19,9 @@ class TestCmdSync: "2026-06-21": [{"id": "c"}], } lines: list[str] = [] - with ( - patch.object(_cli_sync, "run_sync", return_value=merged), - patch.object(_cli_sync, "refresh_weight_from_phone") as refresh, - ): + with patch.object(_cli_sync, "run_sync", return_value=merged): assert _cli_sync.cmd_sync(lines.append) == 0 assert lines == ["synced: 3 entries across 2 day(s)."] - # The phone's weigh-in is applied after the merge, through the same - # sink, so its one line lands under the sync summary. - refresh.assert_called_once_with(lines.append) - - def test_a_failed_sync_does_not_touch_the_weight(self) -> None: - with ( - patch.object(_cli_sync, "run_sync", side_effect=SyncError("no token")), - patch.object(_cli_sync, "refresh_weight_from_phone") as refresh, - ): - assert _cli_sync.cmd_sync(lambda _line: None) == 1 - refresh.assert_not_called() def test_reports_sync_error_as_not_configured(self) -> None: lines: list[str] = [] diff --git a/diet_guard/tests/test_sync_budget.py b/diet_guard/tests/test_sync_budget.py index a9a4c3f..7e50f3f 100644 --- a/diet_guard/tests/test_sync_budget.py +++ b/diet_guard/tests/test_sync_budget.py @@ -38,6 +38,24 @@ class TestSyncBudget: pushed_paths = {call.args[0] for call in client.put_file_text.call_args_list} assert f"diet-guard-sync/devices/{device_id()}/budget.json" in pushed_paths + def test_refreshes_the_weight_from_the_phone_after_the_merge(self) -> None: + """The hook runs last, on every successful tick, into the sync log.""" + _write_token() + write_budget(2000) + client = _mock_client(devices=()) + with ( + patch.object(_sync_client, "GitHubSyncClient", return_value=client), + patch.object(_sync, "refresh_weight_from_phone") as refresh, + ): + _sync.run_sync() + refresh.assert_called_once() + (sink,) = refresh.call_args.args + with patch.object(_sync._logger, "info") as info: + sink("weight: 72.4 kg from the phone (2026-09-17).") + info.assert_called_once_with( + "%s", "weight: 72.4 kg from the phone (2026-09-17)." + ) + def test_nothing_pushed_when_no_budget_ever_set(self) -> None: """An uninitialized device contributes nothing -- no push, no crash.""" _write_token()