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

Configure Feed

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

fix more label tests (#49)

authored by

ayu and committed by
GitHub
3c02214d eb8f3a37

+108 -11
+4
osprey_worker/src/osprey/worker/lib/osprey_shared/labels.py
··· 32 32 CONFLICTING_MUTATION = 0 33 33 # If the existing label status was manual and the attempted mutation was not 34 34 CANNOT_OVERRIDE_MANUAL = 1 35 + # if the label name was an empty string 36 + INVALID_LABEL_NAME = 2 37 + # if the entity id was an empty string 38 + INVALID_ENTITY_ID = 3 35 39 36 40 37 41 class LabelStatus(IntEnum):
+47 -9
osprey_worker/src/osprey/worker/lib/storage/labels.py
··· 118 118 dropped_mutations: list[DroppedEntityLabelMutation] = [] 119 119 for mutation in mutations: 120 120 label_name = mutation.label_name 121 + if label_name == '': 122 + # guard against empty label names 123 + dropped_mutations.append( 124 + DroppedEntityLabelMutation(mutation=mutation, reason=MutationDropReason.INVALID_LABEL_NAME) 125 + ) 126 + continue 121 127 if label_name in mutations_by_label_name: 122 128 other_mutation = mutations_by_label_name[label_name][0] 123 129 if mutation.status.value > other_mutation.status.value: ··· 168 174 **this method WILL modify the labels object that is passed into it**. 169 175 it will also return the pre-modification labels in EntityLabelMutationsResult.old_labels 170 176 """ 177 + 171 178 (mutations_by_label_name, dropped_mutations) = self._get_mutations_by_label_name_and_drop_conflicts(mutations) 172 179 desired_states_by_label_name: dict[str, LabelStateInner] = self._get_desired_states_by_label_name( 173 180 mutations_by_label_name ··· 179 186 removed: list[str] = [] 180 187 updated: list[str] = [] 181 188 old_labels = copy.deepcopy(labels) 189 + 190 + def _append_result(new_status: LabelStatus) -> None: 191 + match new_status.effective_label_status(): 192 + case LabelStatus.ADDED: 193 + added.append(label_name) 194 + case LabelStatus.REMOVED: 195 + removed.append(label_name) 196 + 182 197 for label_name, desired_state in desired_states_by_label_name.items(): 183 198 if label_name not in labels.labels: 184 - labels.labels[label_name] = LabelState.from_inner(desired_state) 185 - added.append(label_name) 199 + new_state = LabelState.from_inner(desired_state) 200 + labels.labels[label_name] = new_state 201 + _append_result(new_state.status) 186 202 continue 187 203 current_state = labels.labels[label_name] 188 204 prev_status = current_state.status ··· 197 213 if prev_status == new_status: 198 214 updated.append(label_name) 199 215 continue 200 - match new_status.effective_label_status(): 201 - case LabelStatus.ADDED: 202 - added.append(label_name) 203 - continue 204 - case LabelStatus.REMOVED: 205 - removed.append(label_name) 206 - continue 216 + _append_result(new_status) 207 217 208 218 # finally, return the result! duhh :D 209 219 return EntityLabelMutationsResult( ··· 219 229 def apply_entity_label_mutations_with_retry( 220 230 self, entity: EntityT[Any], mutations: Sequence[EntityLabelMutation] 221 231 ) -> EntityLabelMutationsResult: 232 + if str(entity.id) == '': 233 + labels = EntityLabels() 234 + dropped_mutations = [ 235 + DroppedEntityLabelMutation(mutation=mut, reason=MutationDropReason.INVALID_ENTITY_ID) 236 + for mut in mutations 237 + ] 238 + return EntityLabelMutationsResult( 239 + old_entity_labels=labels, 240 + new_entity_labels=labels, 241 + labels_added=[], 242 + labels_updated=[], 243 + labels_removed=[], 244 + dropped_mutations=dropped_mutations, 245 + ) 222 246 return self.apply_entity_label_mutations(entity=entity, mutations=mutations) 223 247 224 248 def apply_entity_label_mutations( 225 249 self, entity: EntityT[Any], mutations: Sequence[EntityLabelMutation] 226 250 ) -> EntityLabelMutationsResult: 251 + if str(entity.id) == '': 252 + labels = EntityLabels() 253 + dropped_mutations = [ 254 + DroppedEntityLabelMutation(mutation=mut, reason=MutationDropReason.INVALID_ENTITY_ID) 255 + for mut in mutations 256 + ] 257 + return EntityLabelMutationsResult( 258 + old_entity_labels=labels, 259 + new_entity_labels=labels, 260 + labels_added=[], 261 + labels_updated=[], 262 + labels_removed=[], 263 + dropped_mutations=dropped_mutations, 264 + ) 227 265 try: 228 266 with self._labels_service.read_modify_write_labels_atomically(entity) as entity_labels: 229 267 result = self._compute_new_labels_from_mutations(entity_labels, mutations)
+22
osprey_worker/src/osprey/worker/lib/storage/tests/test_bulk_action_task.py
··· 26 26 yield session 27 27 28 28 29 + # TODO: Stop skipping these tests once bulk action capability is supported 30 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 29 31 def test_create_job(sqlalchemy_session): 30 32 user_id = '123' 31 33 gcs_path = 'gcs://bucket/path' ··· 59 61 assert job.status == BulkActionJobStatus.PENDING_UPLOAD 60 62 61 63 64 + # TODO: Stop skipping these tests once bulk action capability is supported 65 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 62 66 def test_get_one_bulk_action_job(sqlalchemy_session): 63 67 job = BulkActionJob.create_job( 64 68 job_id=generate_snowflake().to_int(), ··· 77 81 assert result == job 78 82 79 83 84 + # TODO: Stop skipping these tests once bulk action capability is supported 85 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 80 86 def test_create_bulk_action_task(sqlalchemy_session): 81 87 job = BulkActionJob.create_job( 82 88 job_id=generate_snowflake().to_int(), ··· 112 118 assert task.created_at is not None 113 119 114 120 121 + # TODO: Stop skipping these tests once bulk action capability is supported 122 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 115 123 def test_create_bulk_action_task_persists_to_db(sqlalchemy_session): 116 124 job = BulkActionJob.create_job( 117 125 job_id=generate_snowflake().to_int(), ··· 146 154 assert persisted_task.status == BulkActionTaskStatus.PENDING 147 155 148 156 157 + # TODO: Stop skipping these tests once bulk action capability is supported 158 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 149 159 def test_get_one_task(sqlalchemy_session): 150 160 job = BulkActionJob.create_job( 151 161 job_id=generate_snowflake().to_int(), ··· 173 183 assert result.chunk_number == task.chunk_number 174 184 175 185 186 + # TODO: Stop skipping these tests once bulk action capability is supported 187 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 176 188 def test_get_one_task_not_found(sqlalchemy_session): 177 189 result = BulkActionTask.get_one(999999) 178 190 179 191 assert result is None 180 192 181 193 194 + # TODO: Stop skipping these tests once bulk action capability is supported 195 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 182 196 def test_get_all_by_job_id(sqlalchemy_session): 183 197 job = BulkActionJob.create_job( 184 198 job_id=generate_snowflake().to_int(), ··· 216 230 assert {task.chunk_number for task in result} == {1, 2, 3} 217 231 218 232 233 + # TODO: Stop skipping these tests once bulk action capability is supported 234 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 219 235 def test_update_task(sqlalchemy_session): 220 236 job = BulkActionJob.create_job( 221 237 job_id=generate_snowflake().to_int(), ··· 248 264 assert persisted_task.error == 'test error' 249 265 250 266 267 + # TODO: Stop skipping these tests once bulk action capability is supported 268 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 251 269 def test_get_next_pending_task(sqlalchemy_session): 252 270 job = BulkActionJob.create_job( 253 271 job_id=generate_snowflake().to_int(), ··· 272 290 assert next_task.status == BulkActionTaskStatus.PENDING 273 291 274 292 293 + # TODO: Stop skipping these tests once bulk action capability is supported 294 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 275 295 def test_get_next_pending_task_no_pending_tasks(sqlalchemy_session): 276 296 job = BulkActionJob.create_job( 277 297 job_id=generate_snowflake().to_int(), ··· 292 312 assert next_task is None 293 313 294 314 315 + # TODO: Stop skipping these tests once bulk action capability is supported 316 + @pytest.mark.skip(reason='Bulk actions are not yet supported') 295 317 def test_get_next_pending_task_different_jobs(sqlalchemy_session): 296 318 job1 = BulkActionJob.create_job( 297 319 job_id=generate_snowflake().to_int(),
+14
osprey_worker/src/osprey/worker/lib/storage/tests/test_bulk_label_task.py
··· 52 52 return task 53 53 54 54 55 + # TODO: Stop skipping these tests once bulk label capability is supported 56 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 55 57 def test_claim__empty() -> None: 56 58 assert BulkLabelTask.claim() is None 57 59 58 60 61 + # TODO: Stop skipping these tests once bulk label capability is supported 62 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 59 63 def test_enqueue() -> None: 60 64 task = enqueue() 61 65 db_task = _query_get_one(task.id) 62 66 assert db_task.id == task.id 63 67 64 68 69 + # TODO: Stop skipping these tests once bulk label capability is supported 70 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 65 71 def test_claim__one() -> None: 66 72 task = enqueue() 67 73 claimed = BulkLabelTask.claim() ··· 70 76 assert claimed.attempts == 1 71 77 72 78 79 + # TODO: Stop skipping these tests once bulk label capability is supported 80 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 73 81 def test_claim__many() -> None: 74 82 first = enqueue() 75 83 second = enqueue() ··· 81 89 assert second_claimed.id == second.id 82 90 83 91 92 + # TODO: Stop skipping these tests once bulk label capability is supported 93 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 84 94 def test_release() -> None: 85 95 enqueue() 86 96 task = BulkLabelTask.claim() ··· 94 104 assert updated.updated_at > updated.created_at 95 105 96 106 107 + # TODO: Stop skipping these tests once bulk label capability is supported 108 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 97 109 def test_claim_time() -> None: 98 110 enqueue() 99 111 # Get the database time to avoid any future timezone issues ··· 104 116 assert task.claim_until >= now + timedelta(seconds=BASE_DELAY_SECONDS) 105 117 106 118 119 + # TODO: Stop skipping these tests once bulk label capability is supported 120 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 107 121 def test_get_one() -> None: 108 122 task = enqueue() 109 123 assert task is not None
+2
osprey_worker/src/osprey/worker/ui_api/osprey/views/tests/test_bulk_history.py
··· 51 51 assert res.data.decode('utf-8') == "User `local-dev@localhost` doesn't have ability `CAN_BULK_LABEL`" 52 52 53 53 54 + # TODO: Stop skipping these tests once bulk label capability is supported 55 + @pytest.mark.skip(reason='Bulk labelling is not yet supported') 54 56 @pytest.mark.use_rules_sources(config_b) 55 57 @patch.object(BulkLabelTask, 'get_one') 56 58 def test_get_bulk_label_task(
+19 -2
osprey_worker/src/osprey/worker/ui_api/osprey/views/tests/test_entities.py
··· 1 1 import json 2 + from unittest import mock 2 3 3 4 import pytest 4 5 from flask import Flask, Response, url_for 5 6 from flask.testing import FlaskClient 6 7 from osprey.worker.lib.osprey_shared.labels import LabelStatus 8 + from osprey.worker.lib.singleton import Singleton 9 + from osprey.worker.lib.singletons import LABELS_PROVIDER 7 10 from osprey.worker.lib.snowflake import generate_snowflake 11 + from osprey.worker.lib.storage.labels import LabelsProvider 12 + from osprey.worker.lib.storage.tests.test_labels import MockLabelsService 8 13 9 14 config = { 10 15 'main.sml': '', ··· 25 30 } 26 31 27 32 33 + def _mocked_init_labels_provider() -> LabelsProvider: 34 + return LabelsProvider(MockLabelsService()) 35 + 36 + 37 + MOCK_LABELS_PROVIDER = Singleton(_mocked_init_labels_provider) 38 + 39 + 40 + # @mock.patch('osprey.worker.lib.singletons.LABELS_PROVIDER', new_callable=_mocked_init_labels_provider, spec_set=True) 41 + # @mock.patch('osprey.worker.adaptor.plugin_manager.bootstrap_labels_provider', new_callable=_mocked_init_labels_provider) 42 + @mock.patch.object(LABELS_PROVIDER, 'instance', new=_mocked_init_labels_provider) 28 43 @pytest.mark.use_rules_sources(config) 29 44 def test_mutate_entity_labels(app: Flask, client: 'FlaskClient[Response]') -> None: 30 45 res = client.post( ··· 39 54 content_type='application/json', 40 55 ) 41 56 assert res.status_code == 200, res.data 42 - assert res.json['mutation_result'] == {'added': [], 'dropped': [], 'removed': ['test_label'], 'unchanged': []} 57 + print(res.json) 58 + assert res.json['mutation_result'] == {'added': [], 'unchanged': [], 'removed': ['test_label'], 'updated': []} 43 59 label = res.json['labels']['test_label'] 44 60 reason = label['reasons']['_ManuallyUpdated'] 45 61 assert reason['description'] == 'Manual update by {AdminEmail}: {Reason}' ··· 47 63 assert label['status'] == LabelStatus.MANUALLY_REMOVED 48 64 49 65 66 + @mock.patch.object(LABELS_PROVIDER, 'instance', new=_mocked_init_labels_provider) 50 67 @pytest.mark.use_rules_sources(config) 51 68 def test_mutate_entity_labels_empty_entity_str(app: Flask, client: 'FlaskClient[Response]') -> None: 52 69 res = client.post( ··· 58 75 ) 59 76 60 77 assert res.status_code == 200, res.data 61 - assert res.json['mutation_result'] == {'added': [], 'dropped': [], 'removed': [], 'unchanged': ['test_label']} 78 + assert res.json['mutation_result'] == {'added': [], 'updated': [], 'removed': [], 'unchanged': ['test_label']} 62 79 63 80 64 81 def test_mutate_entity_labels_auth_reject(app: Flask, client: 'FlaskClient[Response]') -> None: