fix FCM push notifications
CI / Gateway plugin tests (push) Failing after 6m27s
CI / Kotlin tests (android host + desktop) (push) Successful in 7m2s

This commit is contained in:
ARIA committed 2026-08-23 14:32:40 +02:00
1 parent 560d9c19b3
commit d801a18db5
4 files changed
+177 -6

No files matched your search

+73
View File
@@ -1729,6 +1729,79 @@ async def test_push_skipped_when_backend_unconfigured(adapter):
assert fake.calls == []
@pytest.mark.asyncio
async def test_push_deferred_while_turn_active_flushed_on_stop_typing(adapter):
"""Turn-aware push: while the agent's turn is in flight (typing on) and
the device is offline, intermediate status messages park without a push;
the turn-end (stop_typing) flushes ONE push with the latest (final)
message. Regression: a long research turn pushed every status report."""
fake = _FakePush()
adapter._push = fake
adapter._devices.upsert(DEVICE_ID, "Test", {}, fcm_token="tok-1")
await adapter.send_typing("default") # turn starts
await adapter.send("default", "researching XXXX", metadata={"notify": True})
assert fake.calls == [] # intermediate: held back
await adapter.send(
"default", "found XXX, cross-referencing", metadata={"notify": True}
)
assert fake.calls == [] # still held back; latest replaces the pending
await adapter.send("default", "done, findings ready", metadata={"notify": True})
assert fake.calls == []
await adapter.stop_typing("default") # turn ends
assert len(fake.calls) == 1
call = fake.calls[0]
assert call["chat_id"] == "default"
assert call["data"]["kind"] == "message"
assert "done, findings ready" in call["body"]
# All frames are parked in the outbox for sync catch-up.
assert adapter._outbox.latest_cursor() == 3
@pytest.mark.asyncio
async def test_deferred_push_dropped_when_device_comes_online(adapter, ws_client):
"""If the device reconnects mid-turn it syncs the parked frames, so the
turn-end flush must not push a duplicate."""
ws, _ = ws_client
fake = _FakePush()
adapter._push = fake
adapter._devices.upsert(DEVICE_ID, "Test", {}, fcm_token="tok-1")
await adapter.send_typing("default")
await adapter.send("default", "intermediate", metadata={"notify": True})
assert fake.calls == []
# Device reconnects mid-turn (SSE open -> on_device_online clears the
# pending push); the parked frame arrives via the event stream.
frames = await recv_until(ws, lambda f: f.get("type") == "message")
assert frames[-1]["payload"]["text"] == "intermediate"
await adapter.stop_typing("default")
assert fake.calls == []
@pytest.mark.asyncio
async def test_high_priority_notification_pushes_immediately_during_turn(plugin, adapter):
"""Approval/clarify/cron notifications need user action: they push
immediately even while a turn is in flight and the device is offline."""
fake = _FakePush()
adapter._push = fake
adapter._devices.upsert(DEVICE_ID, "Test", {}, fcm_token="tok-1")
await adapter.send_typing("default")
await adapter._broadcast_or_log(
"default",
plugin.protocol.notification(
"default", plugin.protocol.NOTIF_APPROVAL, "Approve", "run rm -rf?"
),
)
assert len(fake.calls) == 1
assert fake.calls[0]["priority"] == "high"
assert fake.calls[0]["data"]["kind"] == "approval"
# ── M5: fcm.register ───────────────────────────────────────────────────────