Mirror of https://github.com/roostorg/osprey github.com/roostorg/osprey
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix label tests (#44)

authored by

ayu and committed by
GitHub
eb8f3a37 5baecfbf

+72 -47
+32 -16
osprey_worker/src/osprey/engine/stdlib/udfs/tests/test_labels.py
··· 1 1 import json 2 - from datetime import datetime, timedelta 2 + from datetime import datetime, timedelta, timezone 3 3 from typing import Any, Callable, Dict, List, Optional, Sequence, Set 4 4 5 5 import gevent ··· 99 99 'added', 100 100 None, 101 101 LabelStatus.ADDED, 102 - LabelReasons({'ExpiredReason': LabelReason(expires_at=(datetime.now() - timedelta(hours=1)))}), 102 + LabelReasons({'ExpiredReason': LabelReason(expires_at=(datetime.now(timezone.utc) - timedelta(hours=1)))}), 103 103 False, 104 104 ), 105 105 ( ··· 108 108 LabelStatus.ADDED, 109 109 LabelReasons( 110 110 { 111 - 'ExpiredReason': LabelReason(expires_at=(datetime.now() - timedelta(hours=1))), 111 + 'ExpiredReason': LabelReason(expires_at=(datetime.now(timezone.utc) - timedelta(hours=1))), 112 112 'TestReason': LabelReason(), 113 113 } 114 114 ), ··· 120 120 LabelStatus.ADDED, 121 121 LabelReasons( 122 122 { 123 - 'ExpiredReason': LabelReason(expires_at=(datetime.now() - timedelta(hours=1))), 124 - 'ExpiringReason': LabelReason(expires_at=(datetime.now() + timedelta(hours=1))), 123 + 'ExpiredReason': LabelReason(expires_at=(datetime.now(timezone.utc) - timedelta(hours=1))), 124 + 'ExpiringReason': LabelReason(expires_at=(datetime.now(timezone.utc) + timedelta(hours=1))), 125 125 } 126 126 ), 127 127 True, ··· 130 130 'added', 131 131 None, 132 132 LabelStatus.ADDED, 133 - LabelReasons({'ExpiringReason': LabelReason(expires_at=(datetime.now() + timedelta(hours=1)))}), 133 + LabelReasons({'ExpiringReason': LabelReason(expires_at=(datetime.now(timezone.utc) + timedelta(hours=1)))}), 134 134 True, 135 135 ), 136 136 ('added', None, LabelStatus.MANUALLY_ADDED, LabelReasons({'TestReason': LabelReason()}), True), ··· 201 201 'added', 202 202 None, 203 203 LabelStatus.ADDED, 204 - LabelReasons({'TestReason': LabelReason(created_at=(datetime.now() - timedelta(days=1)))}), 204 + LabelReasons({'TestReason': LabelReason(created_at=(datetime.now(timezone.utc) - timedelta(days=1)))}), 205 205 timedelta(days=1), 206 206 True, 207 207 ), ··· 209 209 'added', 210 210 None, 211 211 LabelStatus.ADDED, 212 - LabelReasons({'TestReason': LabelReason(created_at=(datetime.now()))}), 212 + LabelReasons({'TestReason': LabelReason(created_at=(datetime.now(timezone.utc)))}), 213 213 timedelta(days=1), 214 214 False, 215 215 ), ··· 413 413 @pytest.mark.parametrize( 414 414 'entity_type, label_name, label_udf, entity_label_mutation', 415 415 ( 416 - ('EntityA', 'label_a', 'LabelAdd', 'EntityA/label_a/1'), 417 - ('EntityB', 'label_b', 'LabelRemove', 'EntityB/label_b/0'), 416 + ('EntityA', 'label_a', 'LabelAdd', f'EntityA/label_a/{LabelStatus.ADDED.value}'), 417 + ('EntityB', 'label_b', 'LabelRemove', f'EntityB/label_b/{LabelStatus.REMOVED.value}'), 418 418 ), 419 419 ) 420 420 def test_label_effects_are_exported_to_extracted_features( ··· 445 445 @pytest.mark.parametrize( 446 446 'entity_type, label_name, label_udf, entity_label_mutation', 447 447 ( 448 - ('EntityA', 'label_a', 'LabelAdd', 'EntityA/label_a/1'), 449 - ('EntityB', 'label_b', 'LabelRemove', 'EntityB/label_b/0'), 448 + ('EntityA', 'label_a', 'LabelAdd', f'EntityA/label_a/{LabelStatus.ADDED.value}'), 449 + ('EntityB', 'label_b', 'LabelRemove', f'EntityB/label_b/{LabelStatus.REMOVED.value}'), 450 450 ), 451 451 ) 452 452 def test_label_effects_are_exported_to_extracted_features_multi_rule( ··· 484 484 @pytest.mark.parametrize( 485 485 'entity_type, label_name, entity_label_mutation', 486 486 ( 487 - ('EntityA', 'label_a', ['EntityA/label_a/1', 'EntityA/label_a/0']), 488 - ('EntityB', 'label_b', ['EntityB/label_b/1', 'EntityB/label_b/0']), 487 + ( 488 + 'EntityA', 489 + 'label_a', 490 + [f'EntityA/label_a/{LabelStatus.ADDED.value}', f'EntityA/label_a/{LabelStatus.REMOVED.value}'], 491 + ), 492 + ( 493 + 'EntityB', 494 + 'label_b', 495 + [f'EntityB/label_b/{LabelStatus.ADDED.value}', f'EntityB/label_b/{LabelStatus.REMOVED.value}'], 496 + ), 489 497 ), 490 498 ) 491 499 def test_label_effects_are_exported_to_extracted_features_multi_rule_add_and_remove( ··· 522 530 @pytest.mark.parametrize( 523 531 'entity_type, label_name, entity_label_mutation', 524 532 ( 525 - ('EntityA', 'label_a', ['EntityA/label_a/0', 'EntityA/other_label/0']), 526 - ('EntityB', 'label_b', ['EntityB/label_b/0', 'EntityB/other_label/0']), 533 + ( 534 + 'EntityA', 535 + 'label_a', 536 + [f'EntityA/label_a/{LabelStatus.ADDED.value}', f'EntityA/other_label/{LabelStatus.ADDED.value}'], 537 + ), 538 + ( 539 + 'EntityB', 540 + 'label_b', 541 + [f'EntityB/label_b/{LabelStatus.ADDED.value}', f'EntityB/other_label/{LabelStatus.ADDED.value}'], 542 + ), 527 543 ), 528 544 ) 529 545 def test_label_effects_are_exported_to_extracted_features_multi_add(
+9 -11
osprey_worker/src/osprey/worker/lib/osprey_shared/labels.py
··· 109 109 is considered expired, too. 110 110 """ 111 111 112 + def __post_init__(self) -> None: 113 + self.created_at = _guarantee_utc_timezone_awareness(self.created_at) 114 + self.expires_at = _guarantee_utc_timezone_awareness(self.expires_at) 115 + 112 116 def is_expired(self) -> bool: 113 117 return bool(self.expires_at is not None and self.expires_at + timedelta(seconds=5) < datetime.now(timezone.utc)) 114 118 ··· 117 121 serialize LabelReason to a JSON-compatible dict. 118 122 converts datetime objects to ISO format strings. 119 123 """ 120 - created_at = _guarantee_utc_timezone_awareness(self.created_at) 121 - expires_at = _guarantee_utc_timezone_awareness(self.expires_at) 122 124 return { 123 125 'pending': self.pending, 124 126 'description': self.description, 125 127 'features': self.features, 126 - 'created_at': created_at.isoformat() if created_at else None, 127 - 'expires_at': expires_at.isoformat() if expires_at else None, 128 + 'created_at': self.created_at.isoformat() if self.created_at else None, 129 + 'expires_at': self.expires_at.isoformat() if self.expires_at else None, 128 130 } 129 131 130 132 @classmethod ··· 133 135 deserialize a dict into a LabelReason object. 134 136 converts ISO format strings back to datetime objects. 135 137 """ 136 - created_at = _guarantee_utc_timezone_awareness( 137 - datetime.fromisoformat(d['created_at']) if d.get('created_at') else None 138 - ) 139 - expires_at = _guarantee_utc_timezone_awareness( 140 - datetime.fromisoformat(d['expires_at']) if d.get('expires_at') else None 141 - ) 138 + created_at = datetime.fromisoformat(d['created_at']) if d.get('created_at') else None 139 + expires_at = datetime.fromisoformat(d['expires_at']) if d.get('expires_at') else None 142 140 return cls( 143 141 pending=d.get('pending', False), 144 142 description=d.get('description', ''), ··· 425 423 description=self.description, 426 424 features=self.features, 427 425 created_at=datetime.now(timezone.utc), 428 - expires_at=_guarantee_utc_timezone_awareness(self.expires_at), 426 + expires_at=self.expires_at, 429 427 ) 430 428 431 429 def serialize(self) -> dict[str, Any]:
+31 -20
osprey_worker/src/osprey/worker/lib/storage/tests/test_labels.py
··· 1 - from datetime import datetime, timedelta 1 + from datetime import datetime, timedelta, timezone 2 2 from typing import Any 3 3 4 4 import pytest ··· 78 78 assert len(result.dropped_mutations) == 0 79 79 80 80 81 - def test_compute_new_labels_from_mutations_removes_existing_label(labels_provider: LabelsProvider, now: datetime): 82 - """Test removing an existing label""" 81 + def test_compute_new_labels_from_mutations_cannot_remove_unexpired_manual_label( 82 + labels_provider: LabelsProvider, now: datetime 83 + ): 84 + """Test removing an unexpired manually-added label""" 83 85 old_labels = EntityLabels( 84 86 labels={ 85 87 'test_label': LabelState( 86 - status=LabelStatus.ADDED, 87 - reasons=LabelReasons({'reason1': LabelReason(description='original', created_at=now)}), 88 + status=LabelStatus.MANUALLY_ADDED, 89 + reasons=LabelReasons( 90 + { 91 + 'reason1': LabelReason( 92 + description='original', 93 + created_at=now, 94 + expires_at=datetime.now(timezone.utc) + timedelta(days=5), 95 + ) 96 + } 97 + ), 88 98 ) 89 99 } 90 100 ) 91 - mutations = [ 92 - EntityLabelMutation( 93 - label_name='test_label', 94 - reason_name='removal_reason', 95 - status=LabelStatus.REMOVED, 96 - pending=False, 97 - description='Removing label', 98 - features={}, 99 - expires_at=None, 100 - ) 101 - ] 101 + removal_mut = EntityLabelMutation( 102 + label_name='test_label', 103 + reason_name='removal_reason', 104 + status=LabelStatus.REMOVED, 105 + pending=False, 106 + description='Removing label', 107 + features={}, 108 + expires_at=None, 109 + ) 110 + 111 + mutations = [removal_mut] 102 112 103 113 result = labels_provider._compute_new_labels_from_mutations(old_labels, mutations) 104 114 105 - assert result.new_entity_labels.labels['test_label'].status == LabelStatus.REMOVED 106 - assert 'test_label' in result.labels_removed 115 + assert result.new_entity_labels.labels['test_label'].status == LabelStatus.MANUALLY_ADDED 116 + assert len(result.labels_removed) == 0 107 117 assert len(result.labels_added) == 0 108 118 assert len(result.labels_updated) == 0 109 - assert len(result.dropped_mutations) == 0 119 + assert len(result.dropped_mutations) == 1 120 + assert result.dropped_mutations[0].mutation == removal_mut 110 121 111 122 112 123 def test_compute_new_labels_from_mutations_updates_existing_label_same_status( ··· 172 183 assert len(result.dropped_mutations) == 1 173 184 assert result.dropped_mutations[0].reason == MutationDropReason.CONFLICTING_MUTATION 174 185 # The higher priority status (ADDED > REMOVED) should win 175 - assert result.new_entity_labels.labels['test_label'].status == LabelStatus.REMOVED 186 + assert result.new_entity_labels.labels['test_label'].status == LabelStatus.ADDED 176 187 177 188 178 189 def test_compute_new_labels_from_mutations_manual_blocks_automatic(labels_provider: LabelsProvider, now: datetime):