""" Tests for the Redis-backed stall dedup claim (replaces the old in-memory set). - The first claim for a booking wins; a second within the window is refused. - The consumer-side handled-claim behaves the same (redelivery guard). - A Redis error fails OPEN (returns True) so a genuine stall is never muted. Run: python -m unittest discover -s tests """ import unittest from agents.exception_agent import ExceptionAgent class FakeRedisNX: """Minimal Redis with SET NX + TTL semantics.""" def __init__(self, raise_on_set=False): self.store = {} self.raise_on_set = raise_on_set self.set_calls = [] async def set(self, key, val, ex=None, nx=False): if self.raise_on_set: raise RuntimeError("redis down") self.set_calls.append((key, ex, nx)) if nx and key in self.store: return None # already claimed self.store[key] = val return True def make_agent(redis): agent = ExceptionAgent.__new__(ExceptionAgent) # skip heavy __init__ agent._redis = redis return agent class TestStallDedup(unittest.IsolatedAsyncioTestCase): async def test_first_alert_claim_wins_second_refused(self): agent = make_agent(FakeRedisNX()) self.assertTrue(await agent._claim_stall("b1")) # first wins self.assertFalse(await agent._claim_stall("b1")) # duplicate refused self.assertTrue(await agent._claim_stall("b2")) # different booking ok async def test_claim_sets_ttl_and_nx(self): redis = FakeRedisNX() await make_agent(redis)._claim_stall("b9") key, ex, nx = redis.set_calls[0] self.assertEqual(key, "stall_notified:b9") self.assertTrue(nx) self.assertEqual(ex, 21600) async def test_handled_claim_guards_redelivery(self): agent = make_agent(FakeRedisNX()) self.assertTrue(await agent._claim_stall_handled("b1")) self.assertFalse(await agent._claim_stall_handled("b1")) async def test_redis_error_fails_open(self): agent = make_agent(FakeRedisNX(raise_on_set=True)) self.assertTrue(await agent._claim_stall("b1")) # must not mute a real stall self.assertTrue(await agent._claim_stall_handled("b1")) if __name__ == "__main__": unittest.main()