import unittest from tests import mock from ..health import * class HealthChecksTest(unittest.TestCase): def test_check_accum_empty(self): # health checks accum initially empty reports empty h = HealthCheckAccumulator() self.assertEqual(h.checks(), {}) h = HealthCheckAccumulator({}) self.assertEqual(h.checks(), {}) def _get_init_checks(self): return HealthCheckAccumulator({ "C0": { "S0": { "summary": ["s0", "s1"], "detail": ("d0", "d1") } } }) def test_check_init(self): # initialization with lists and tuples is OK h = self._get_init_checks() self.assertEqual(h.checks(), { "C0": { "S0": { "summary": set(["s0", "s1"]), "detail": set(["d0", "d1"]) } } }) def _get_merged_checks(self): h = self._get_init_checks() h.merge(HealthCheckAccumulator({ "C0": { "S0": { "summary": ["s0", "s1", "s2"], "detail": ("d2",) }, "S1": { "summary": ["s0", "s1", "s2"], "detail": () } }, "C1": { "S0": { "summary": [], "detail": ("d0", "d1", "d2") } } })) return h def test_check_merge(self): # merging combines and de-duplicates h = self._get_merged_checks() self.assertEqual(h.checks(), { "C0": { "S0": { "summary": set(["s0", "s1", "s2"]), "detail": set(["d0", "d1", "d2"]) }, "S1": { "summary": set(["s0", "s1", "s2"]), "detail": set([]) } }, "C1": { "S0": { "summary": set([]), "detail": set(["d0", "d1", "d2"]) } } }) def test_check_add_no_change(self): # returns false when nothing changes h = self._get_merged_checks() self.assertFalse(h.add({})) self.assertFalse(h.add({ "C0": { "severity": "S0", "summary": {"message": "s0"}, "detail": [] } })) self.assertFalse(h.add({ "C0": { "severity": "S0", "summary": {"message": "s1"}, "detail": [{"message": "d1"}] } })) self.assertFalse(h.add({ "C0": { "severity": "S0", "summary": {"message": "s0"}, "detail": [{"message": "d1"}, {"message": "d2"}] } })) def test_check_add_changed(self): # new checks report change h = self._get_merged_checks() self.assertTrue(h.add({ "C0": { "severity": "S0", "summary": {"message": "s3"}, "detail": [] } })) self.assertTrue(h.add({ "C0": { "severity": "S0", "summary": {"message": "s1"}, "detail": [{"message": "d4"}] } })) self.assertTrue(h.add({ "C0": { "severity": "S2", "summary": {"message": "s0"}, "detail": [{"message": "d0"}] } })) self.assertTrue(h.add({ "C2": { "severity": "S0", "summary": {"message": "s0"}, "detail": [{"message": "d0"}, {"message": "d1"}] } })) self.assertEqual(h.checks(), { "C0": { "S0": { "summary": set(["s0", "s1", "s2", "s3"]), "detail": set(["d0", "d1", "d2", "d4"]) }, "S1": { "summary": set(["s0", "s1", "s2"]), "detail": set([]) }, "S2": { "summary": set(["s0"]), "detail": set(["d0"]) } }, "C1": { "S0": { "summary": set([]), "detail": set(["d0", "d1", "d2"]) } }, "C2": { "S0": { "summary": set(["s0"]), "detail": set(["d0", "d1"]) } } }) class HealthHistoryTest(unittest.TestCase): def _now(self): # return some time truncated at 30 minutes past the hour. this lets us # fiddle with time offsets without worrying about accidentally landing # on exactly the top of the hour which is the edge of a time slot for # tracking health history. dt = datetime.datetime.utcnow() return datetime.datetime( year=dt.year, month=dt.month, day=dt.day, hour=dt.hour, minute=30) def test_empty_slot(self): now = self._now() HealthHistorySlot._now = mock.Mock(return_value=now) h = HealthHistorySlot() # reports no historical checks self.assertEqual(h.health(), {"checks": {}}) # an empty slot doesn't need to be flushed self.assertFalse(h.need_flush()) def test_expires(self): now = self._now() HealthHistorySlot._now = mock.Mock(return_value=now) h = HealthHistorySlot() self.assertFalse(h.expired()) # an hour from now it would be expired future = now + datetime.timedelta(hours=1) HealthHistorySlot._now = mock.Mock(return_value=future) self.assertTrue(h.expired()) def test_need_flush(self): now = self._now() HealthHistorySlot._now = mock.Mock(return_value=now) h = HealthHistorySlot() self.assertFalse(h.need_flush()) self.assertTrue(h.add(dict(checks={ "C0": { "severity": "S0", "summary": {"message": "s0"}, "detail": [{"message": "d0"}] } }))) # no flush needed, yet... self.assertFalse(h.need_flush()) # after persist period time elapses, a flush is needed future = now + PERSIST_PERIOD HealthHistorySlot._now = mock.Mock(return_value=future) self.assertTrue(h.need_flush()) # mark flush resets h.mark_flushed() self.assertFalse(h.need_flush()) def test_need_flush_edge(self): # test needs flush is true because it has expired, not because it has # been dirty for the persistence period dt = datetime.datetime.utcnow() now = datetime.datetime( year=dt.year, month=dt.month, day=dt.day, hour=dt.hour, minute=59, second=59) HealthHistorySlot._now = mock.Mock(return_value=now) h = HealthHistorySlot() self.assertFalse(h.expired()) self.assertFalse(h.need_flush()) # now it is dirty, but it doesn't need a flush self.assertTrue(h.add(dict(checks={ "C0": { "severity": "S0", "summary": {"message": "s0"}, "detail": [{"message": "d0"}] } }))) self.assertFalse(h.expired()) self.assertFalse(h.need_flush()) # advance time past the hour so it expires, but not past the persistence # period deadline for the last event that set the dirty bit self.assertTrue(PERSIST_PERIOD.total_seconds() > 5) future = now + datetime.timedelta(seconds=5) HealthHistorySlot._now = mock.Mock(return_value=future) self.assertTrue(h.expired()) self.assertTrue(h.need_flush())