Warning, file /swf-monitor/src/monitor_app/tests/test_live_notices.py was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 from copy import deepcopy
0002 from types import SimpleNamespace
0003 from unittest import TestCase
0004
0005 from monitor_app.live_notices import ROUTINE_SUCCESSES, select_notice
0006 from monitor_app.notice_router import _matches
0007
0008
0009 class LiveNoticeSelectionTests(TestCase):
0010 def setUp(self):
0011 self.row = SimpleNamespace(app_name='epicprod', instance_name='ops-agent',
0012 message='declared state sync failed: CRIC unavailable')
0013 self.failures = {}
0014 self.extra = dict(action='catalog_sync', outcome='ok',
0015 username='nightly_cron', sublevel='high', live_default=True)
0016
0017 def select(self, **changes):
0018 return select_notice(self.row, dict(self.extra, **changes), self.failures, {})
0019
0020 def test_routine_successes_are_quiet(self):
0021 for action in ROUTINE_SUCCESSES:
0022 with self.subTest(action=action):
0023 self.assertIsNone(self.select(action=action))
0024
0025 def test_maintenance_is_quiet_including_failures_and_recoveries(self):
0026 for action in ('stash_drain', 'storage_sweep', 'log_rescue'):
0027 with self.subTest(action=action):
0028 self.assertIsNone(self.select(action=action, summary='moved=2'))
0029 self.assertIsNone(self.select(action=action, outcome='partial', reason='x'))
0030 self.assertIsNone(self.select(action=action))
0031
0032 def test_maintenance_live_override_still_publishes(self):
0033 notice = select_notice(self.row, dict(self.extra, action='stash_drain'),
0034 self.failures, {'stash_drain': True})
0035 self.assertIsNotNone(notice)
0036
0037 def test_actual_changes_inside_routine_passes_remain_visible(self):
0038 for action, summary in (
0039 ('catalog_import', 'csv: 2 new, 405 updated'),
0040 ('past_import', 'created=1 updated=5541 errors=0'),
0041 ('questionnaire_import', 'request questionnaire: 0 new, 1 updated, 76 unchanged'),
0042 ('association_sweep', 'checked=22 new=0 intaken=1 unmatched=3'),
0043 ('segfault_inventory', '3 crashed jobs, 3 rows added, 147 signatures (1 new)'),
0044 ('segfault_dig', 'automatic: 1 dug, 1 traced'),
0045 ('segfault_study', '1 studies queued (key); 0 traced signatures marked covered'),
0046 ('segfault_notice', '1 need a reproduction, 0 traced unread')):
0047 with self.subTest(action=action):
0048 self.assertIsNotNone(self.select(action=action, summary=summary))
0049
0050 def test_rewritten_import_rows_are_not_new_information(self):
0051 self.assertIsNone(self.select(action='past_import', summary='created=0 updated=5541 errors=0'))
0052 self.assertIsNone(self.select(action='catalog_import', summary='csv: 0 new, 405 updated'))
0053
0054 def test_operator_requested_operations_are_not_quieted(self):
0055 self.assertIsNotNone(self.select(username='wenaus'))
0056
0057 def test_real_production_actions_and_assessments_remain_visible(self):
0058 for action in ('rucio_arrivals', 'es_closeout', 'assessment_register',
0059 'dataset_expected_events_set', 'production_finding'):
0060 self.assertIsNotNone(self.select(action=action))
0061
0062 def test_shadow_decisions_are_quiet_but_actual_exclusions_are_not(self):
0063 self.assertIsNone(self.select(action='node_guard_decision', outcome='would_exclude'))
0064 self.assertIsNotNone(self.select(action='node_guard_decision', outcome='excluded'))
0065
0066 def test_first_failure_then_changed_cause_then_recovery(self):
0067 failure = dict(action='declared_state_sync', outcome='error',
0068 username='declared_cron', reason='CRIC unavailable')
0069 self.assertIsNotNone(self.select(**failure))
0070 self.assertIsNone(self.select(**failure))
0071 self.assertIsNotNone(self.select(**dict(failure, reason='proxy expired')))
0072 recovered = self.select(action='declared_state_sync', username='declared_cron',
0073 live_default=False, sublevel='low')
0074 self.assertEqual(recovered['operation'], 'declared_state_sync_recovered')
0075 self.assertIn('proxy expired', recovered['summary'])
0076 self.assertTrue(recovered['live_default'])
0077 self.assertEqual(self.failures, {})
0078
0079 normal = self.select(action='declared_state_sync', username='declared_cron',
0080 live_default=False, sublevel='low')
0081 sub = SimpleNamespace(event='*', filters={'live': True, 'sublevel': ['high', 'normal']})
0082 self.assertFalse(_matches(sub, self.row, 'declared_state_sync', normal, {}))
0083
0084 def test_failure_state_survives_a_publisher_restart(self):
0085 self.select(outcome='timeout', reason='timed out')
0086 state = deepcopy(self.failures)
0087 self.assertIsNone(select_notice(self.row, dict(self.extra, outcome='timeout',
0088 reason='timed out'), state, {}))
0089
0090 def test_problem_identity_separates_subjects(self):
0091 self.assertIsNotNone(self.select(outcome='error', subject_key='a', reason='failed'))
0092 self.assertIsNotNone(self.select(outcome='error', subject_key='b', reason='failed'))
0093
0094 def test_reason_falls_back_to_record_message(self):
0095 notice = self.select(outcome='error')
0096 self.assertIn('CRIC unavailable', notice['reason'])
0097
0098 def test_original_record_is_unchanged(self):
0099 original = dict(self.extra, outcome='error')
0100 before = dict(original)
0101 select_notice(self.row, original, self.failures, {})
0102 self.assertEqual(original, before)
0103
0104 def test_explicit_force_live_override_keeps_routine_posts(self):
0105 self.assertIsNotNone(select_notice(self.row, self.extra, {}, {'catalog_sync': True}))
0106
0107 def test_force_quiet_override_also_applies_to_recovery(self):
0108 self.select(outcome='error', reason='failed')
0109 recovered = self.select()
0110 sub = SimpleNamespace(event='*', filters={'live': True})
0111 self.assertFalse(_matches(sub, self.row, 'catalog_sync', recovered, {'catalog_sync': False}))
0112
0113 def test_other_logging_namespaces_are_unchanged(self):
0114 self.row.app_name = 'testbed'
0115 self.assertEqual(select_notice(self.row, self.extra, {}, {}), self.extra)