Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 10:47:30

0001 """Ratios, incident comparisons and history replacement; no database writes."""
0002 from datetime import date, timedelta
0003 from unittest.mock import patch
0004 
0005 from django.test import SimpleTestCase
0006 
0007 from monitor_app.compute_efficiency import build, build_current, dashboard_data, display_data, merge_days
0008 
0009 
0010 class ComputeEfficiencyTests(SimpleTestCase):
0011     def test_dashboard_months_sum_hours_and_keep_unmeasured_months(self):
0012         result = dashboard_data({'daily': [
0013             ['2026-05-01', 'a', False, '', 90, 100, 10, 0],
0014             ['2026-05-02', 'b', False, '', 0, 900, 30, 1],
0015             ['2026-06-01', 'a', False, '', 0, 60, 2, 0],
0016         ]}, None)
0017         may, june, july = result['months'][:3]
0018         self.assertEqual((may['efficiency'], may['jobs'], may['missing']), (9, 40, 1))
0019         self.assertEqual(june['efficiency'], 0)
0020         self.assertIsNone(july['efficiency'])
0021         self.assertIsNone(result['current'])
0022 
0023     def test_current_window_is_exactly_24_hours_and_sums_core_hours(self):
0024         with patch('monitor_app.compute_efficiency.query_days', return_value=[
0025                 ['2026-10-24', 'a', False, '', 90, 100, 10, 0],
0026                 ['2026-10-25', 'b', False, '', 0, 900, 30, 0],
0027         ]) as query:
0028             result = build_current()
0029         start, end = query.call_args.args
0030         self.assertEqual(end - start, timedelta(hours=24))
0031         self.assertIsNotNone(start.tzinfo)
0032         self.assertEqual((result['efficiency'], result['jobs']), (9, 40))
0033         self.assertEqual(result['core_hours_per_day'], 1000)
0034         self.assertEqual(result['productive_cores'], 90 / 24)
0035 
0036     def test_site_delivery_counts_failed_hours_and_uses_elapsed_calendar_days(self):
0037         rows = [
0038             ['2026-05-01', 'BNL_OSG_EPIC_PROD_1', True, 'site:A', 240, 480, 10, 0],
0039             ['2026-05-02', 'BNL_OSG_PanDA_1', True, 'site:B', 120, 240, 5, 0],
0040             ['2026-05-02', 'NERSC_Perlmutter_epic_test', False, '', 240, 960, 20, 0],
0041             ['2026-05-02', 'UM_GREX_PanDA_1', False, '', 480, 480, 20, 0],
0042         ]
0043         result = dashboard_data({'daily': rows, 'through': '2026-05-16T12:00:00+00:00'}, None)
0044         sites = {site['id']: site['months'][0] for site in result['sites']}
0045         self.assertEqual(sites['osg']['ended'], 720)
0046         self.assertEqual(sites['perlmutter']['efficiency'], 25)
0047         self.assertEqual(sites['grex']['efficiency'], 100)
0048         for row in [result['months'][0], *sites.values()]:
0049             self.assertEqual(row['days'], 15.5)
0050             self.assertAlmostEqual(row['core_hours_per_day'], row['ended'] / 15.5)
0051             self.assertAlmostEqual(row['productive_cores'], row['finished'] / (15.5 * 24))
0052             self.assertAlmostEqual(row['productive_cores'],
0053                                    row['core_hours_per_day'] * row['efficiency'] / 100 / 24)
0054 
0055     def data(self, rows, **options):
0056         with patch('monitor_app.compute_efficiency._queue_labels', return_value={
0057                 'grex': 'GREX', 'NERSC_Perlmutter_epic': 'Perlmutter'}):
0058             return display_data({'daily': rows}, date(2026, 5, 1), date(2026, 6, 30), **options)
0059 
0060     def test_ratio_uses_summed_hours_and_preserves_real_zero(self):
0061         result = self.data([
0062             ['2026-05-01', 'grex', False, '', 90, 100, 10, 0, 0, 0, 0, 0],
0063             ['2026-05-02', 'grex', False, '', 0, 900, 30, 0, 0, 0, 0, 0],
0064             ['2026-06-02', 'grex', False, '', 0, 60, 2, 0, 0, 0, 0, 0],
0065         ])
0066         may, june = result['cells']
0067         self.assertEqual(may['efficiency'], 9)
0068         self.assertEqual(may['jobs'], 40)
0069         self.assertEqual(june['efficiency'], 0)
0070 
0071     def test_incident_exclusion_subtracts_hours_and_attempt_counts(self):
0072         row = ['2026-05-03', 'grex', False, '', 200, 1000, 120, 4, 100, 800, 100, 3]
0073         cell = self.data([row], exclude=True)['cells'][0]
0074         self.assertEqual((cell['finished'], cell['ended'], cell['jobs'], cell['missing']),
0075                          (100, 200, 20, 1))
0076         self.assertEqual(cell['efficiency'], 50)
0077 
0078     def test_unknown_execution_site_and_low_volume_are_visible(self):
0079         cell = self.data([
0080             ['2026-05-04', 'BNL_OSG_EPIC_PROD_1', True, '', 20, 40, 5, 1, 0, 0, 0, 0],
0081         ])
0082         self.assertIn('unresolved', cell['sites'][0]['label'].lower())
0083         self.assertIsNone(cell['cells'][0]['efficiency'])
0084         self.assertEqual(cell['cells'][0]['missing'], 1)
0085 
0086     def test_weeks_start_monday_and_same_osg_site_combines_queues(self):
0087         rows = [
0088             ['2026-05-04', 'BNL_OSG_EPIC_PROD_1', True, 'site:ComputeCanada-Fir',
0089              70, 100, 2, 0, 0, 0, 0, 0],
0090             ['2026-05-10', 'BNL_OSG_PanDA_1', True, 'site:ComputeCanada-Fir',
0091              30, 100, 2, 0, 0, 0, 0, 0],
0092         ]
0093         result = self.data(rows, bucket='week')
0094         self.assertEqual(len(result['sites']), 1)
0095         self.assertEqual(result['sites'][0]['label'], 'OSG ยท Fir')
0096         self.assertEqual(result['cells'][0]['period'], '2026-05-04')
0097         self.assertEqual(result['cells'][0]['efficiency'], 50)
0098 
0099     def test_historical_perlmutter_queues_form_one_site(self):
0100         result = self.data([
0101             ['2026-06-01', 'NERSC_Perlmutter_epic', False, '', 10, 100, 10, 0, 0, 0, 0, 0],
0102             ['2026-06-02', 'NERSC_Perlmutter_epic_test', False, '', 900, 1000, 50, 0, 0, 0, 0, 0],
0103         ])
0104         self.assertEqual(result['sites'], [{'id': 'queue:Perlmutter', 'label': 'Perlmutter'}])
0105         self.assertAlmostEqual(result['cells'][0]['efficiency'], 100 * 910 / 1100)
0106 
0107     def test_refresh_removes_obsolete_recent_rows_but_retains_history(self):
0108         old = [['2026-05-01', 'historic'], ['2026-06-01', 'removed']]
0109         recent = [['2026-06-02', 'new']]
0110         self.assertEqual(merge_days(old, recent, date(2026, 6, 1)), [old[0], recent[0]])
0111         self.assertEqual(merge_days(old, recent, date(2026, 5, 1)), recent)
0112 
0113     def test_full_reconciliation_retains_expired_archive_periods(self):
0114         old = ['2026-05-01', 'historic']
0115         recent = ['2026-06-02', 'new']
0116         with patch('monitor_app.compute_efficiency.CachedProduct') as store, \
0117                 patch('monitor_app.compute_efficiency.query_days', return_value=[recent]):
0118             store.objects.filter.return_value.first.return_value.value = {'daily': [old]}
0119             self.assertEqual(build(full=True)['daily'], [old, recent])
0120 
0121     def test_empty_full_source_cannot_erase_stored_history(self):
0122         with patch('monitor_app.compute_efficiency.CachedProduct') as store, \
0123                 patch('monitor_app.compute_efficiency.query_days', return_value=[]):
0124             store.objects.filter.return_value.first.return_value.value = {'daily': [['2026-05-01']]}
0125             with self.assertRaisesRegex(RuntimeError, 'no source history'):
0126                 build(full=True)