# Path: electrumx/server/controller.py
# class Notifications(object):
#     # hashX notifications come from two sources: new blocks and
#     # mempool refreshes.
#     #
#     # A user with a pending transaction is notified after the block it
#     # gets in is processed.  Block processing can take an extended
#     # time, and the prefetcher might poll the daemon after the mempool
#     # code in any case.  In such cases the transaction will not be in
#     # the mempool after the mempool refresh.  We want to avoid
#     # notifying clients twice - for the mempool refresh and when the
#     # block is done.  This object handles that logic by deferring
#     # notifications appropriately.
# 
#     def __init__(self):
#         self._touched_mp = {}
#         self._touched_bp = {}
#         self._highest_block = -1
# 
#     async def _maybe_notify(self):
#         tmp, tbp = self._touched_mp, self._touched_bp
#         common = set(tmp).intersection(tbp)
#         if common:
#             height = max(common)
#         elif tmp and max(tmp) == self._highest_block:
#             height = self._highest_block
#         else:
#             # Either we are processing a block and waiting for it to
#             # come in, or we have not yet had a mempool update for the
#             # new block height
#             return
#         touched = tmp.pop(height)
#         for old in [h for h in tmp if h <= height]:
#             del tmp[old]
#         for old in [h for h in tbp if h <= height]:
#             touched.update(tbp.pop(old))
#         await self.notify(height, touched)
# 
#     async def notify(self, height, touched):
#         pass
# 
#     async def start(self, height, notify_func):
#         self._highest_block = height
#         self.notify = notify_func
#         await self.notify(height, set())
# 
#     async def on_mempool(self, touched, height):
#         self._touched_mp[height] = touched
#         await self._maybe_notify()
# 
#     async def on_block(self, touched, height):
#         self._touched_bp[height] = touched
#         self._highest_block = height
#         await self._maybe_notify()


import pytest
from electrumx.server.controller import Notifications



@pytest.mark.asyncio
async def test_simple_mempool():
    n = Notifications()
    notified = []
    async def notify(height, touched):
        notified.append((height, touched))
    await n.start(5, notify)

    mtouched = {'a', 'b'}
    btouched = {'b', 'c'}
    await n.on_mempool(mtouched, 6)
    assert notified == [(5, set())]
    await n.on_block(btouched, 6)
    assert notified == [(5, set()), (6, set.union(mtouched, btouched))]


@pytest.mark.asyncio
async def test_enter_mempool_quick_blocks_2():

# Path: electrumx/server/controller.py
# class Notifications(object):
#     # hashX notifications come from two sources: new blocks and
#     # mempool refreshes.
#     #
#     # A user with a pending transaction is notified after the block it
#     # gets in is processed.  Block processing can take an extended
#     # time, and the prefetcher might poll the daemon after the mempool
#     # code in any case.  In such cases the transaction will not be in
#     # the mempool after the mempool refresh.  We want to avoid
#     # notifying clients twice - for the mempool refresh and when the
#     # block is done.  This object handles that logic by deferring
#     # notifications appropriately.
# 
#     def __init__(self):
#         self._touched_mp = {}
#         self._touched_bp = {}
#         self._highest_block = -1
# 
#     async def _maybe_notify(self):
#         tmp, tbp = self._touched_mp, self._touched_bp
#         common = set(tmp).intersection(tbp)
#         if common:
#             height = max(common)
#         elif tmp and max(tmp) == self._highest_block:
#             height = self._highest_block
#         else:
#             # Either we are processing a block and waiting for it to
#             # come in, or we have not yet had a mempool update for the
#             # new block height
#             return
#         touched = tmp.pop(height)
#         for old in [h for h in tmp if h <= height]:
#             del tmp[old]
#         for old in [h for h in tbp if h <= height]:
#             touched.update(tbp.pop(old))
#         await self.notify(height, touched)
# 
#     async def notify(self, height, touched):
#         pass
# 
#     async def start(self, height, notify_func):
#         self._highest_block = height
#         self.notify = notify_func
#         await self.notify(height, set())
# 
#     async def on_mempool(self, touched, height):
#         self._touched_mp[height] = touched
#         await self._maybe_notify()
# 
#     async def on_block(self, touched, height):
#         self._touched_bp[height] = touched
#         self._highest_block = height
#         await self._maybe_notify()


# Path: tests/server/test_notifications.py
import pytest
from electrumx.server.controller import Notifications




@pytest.mark.asyncio
async def test_simple_mempool():
    n = Notifications()
    notified = []
    async def notify(height, touched):
        notified.append((height, touched))
    await n.start(5, notify)

    mtouched = {'a', 'b'}
    btouched = {'b', 'c'}
    await n.on_mempool(mtouched, 6)
    assert notified == [(5, set())]
    await n.on_block(btouched, 6)
    assert notified == [(5, set()), (6, set.union(mtouched, btouched))]


@pytest.mark.asyncio
async def test_enter_mempool_quick_blocks_2():

    n = Notifications()