def test_put_without_update_timestamps(self) -> None: model = base_models.BaseModel() self.assertIsNone(model.created_on) self.assertIsNone(model.last_updated) # First `put` does not raise an Exception because it sets last_updated # automatically since it is None. model.put() # Immediately calling `put` again fails, because update_timestamps needs # to be called first. with self.assertRaisesRegex( # type: ignore[no-untyped-call] Exception, re.escape('did not call update_timestamps()') ): model.put() model = base_models.BaseModel.get_by_id(model.id) # Getting a fresh model requires update_timestamps too. with self.assertRaisesRegex( # type: ignore[no-untyped-call] Exception, re.escape('did not call update_timestamps()') ): model.put() model.update_timestamps() # OK, update_timestamps called before put. model.put()
def test_generic_query_put_get_and_delete_operations(self) -> None: model = base_models.BaseModel() all_models = base_models.BaseModel.get_all() self.assertEqual(all_models.count(), 0) model.update_timestamps() model.put() all_models = base_models.BaseModel.get_all() self.assertEqual(all_models.count(), 1) base_model = all_models.get() # Ruling out the possibility of None for mypy type checking. assert base_model is not None self.assertEqual(base_model, model) model_id = base_model.id self.assertEqual(model, base_models.BaseModel.get(model_id)) model.delete() all_models = base_models.BaseModel.get_all() self.assertEqual(all_models.count(), 0) self.assertEqual(model_id, 4) with self.assertRaisesRegex( base_models.BaseModel.EntityNotFoundError, 'Entity for class BaseModel with id 4 not found' ): model.get(model_id)
def test_put(self) -> None: model = base_models.BaseModel() self.assertIsNone(model.created_on) self.assertIsNone(model.last_updated) # Field last_updated will get updated anyway because it is None. model.update_timestamps(update_last_updated_time=False) model.put() model_id = model.id self.assertIsNotNone( base_models.BaseModel.get_by_id(model_id).created_on) self.assertIsNotNone( base_models.BaseModel.get_by_id(model_id).last_updated) last_updated = model.last_updated # Field last_updated won't get updated because update_last_updated_time # is set to False and last_updated already has some value. model.update_timestamps(update_last_updated_time=False) model.put() self.assertEqual( base_models.BaseModel.get_by_id(model_id).last_updated, last_updated) # Field last_updated will get updated because update_last_updated_time # is set to True (by default). model.update_timestamps() model.put() self.assertNotEqual( base_models.BaseModel.get_by_id(model_id).last_updated, last_updated)
def test_generic_query_put_get_and_delete_operations(self): # type: () -> None model = base_models.BaseModel() all_models = base_models.BaseModel.get_all() self.assertEqual(all_models.count(), 0) model.update_timestamps() model.put() all_models = base_models.BaseModel.get_all() self.assertEqual(all_models.count(), 1) base_model = cast(base_models.BaseModel, all_models.get()) self.assertEqual(base_model, model) model_id = base_model.id self.assertEqual(model, base_models.BaseModel.get(model_id)) model.delete() all_models = base_models.BaseModel.get_all() self.assertEqual(all_models.count(), 0) with self.assertRaisesRegexp( # type: ignore[no-untyped-call] base_models.BaseModel.EntityNotFoundError, 'Entity for class BaseModel with id 4 not found' ): model.get(model_id)
def test_get_new_id_method_returns_unique_ids(self) -> None: ids: Set[str] = set([]) for _ in range(100): new_id = base_models.BaseModel.get_new_id('') self.assertNotIn(new_id, ids) base_models.BaseModel(id=new_id).put() ids.add(new_id)
def test_clone_model(self) -> None: model = base_models.BaseModel(id='123', deleted=True) clone = job_utils.clone_model(model) self.assertEqual(model.id, clone.id) self.assertEqual(model, clone) self.assertIsNot(model, clone) self.assertIsInstance(clone, base_models.BaseModel)
def test_clone_with_changes(self) -> None: model = base_models.BaseModel(id='123', deleted=True) clone = job_utils.clone_model(model, deleted=False) self.assertNotEqual(model, clone) self.assertIsNot(model, clone) self.assertIsInstance(clone, base_models.BaseModel) self.assertTrue(model.deleted) self.assertFalse(clone.deleted)
def test_clone_with_changes_to_id(self) -> None: model = base_models.BaseModel(id='123') clone = job_utils.clone_model(model, id='124') self.assertNotEqual(model, clone) self.assertIsNot(model, clone) self.assertIsInstance(clone, base_models.BaseModel) self.assertEqual(model.id, '123') self.assertEqual(clone.id, '124')
def test_get_new_id_method_returns_unique_ids(self): # type: () -> None ids = set([]) # type: Set[Text] for _ in python_utils.RANGE(100): new_id = base_models.BaseModel.get_new_id('') self.assertNotIn(new_id, ids) base_models.BaseModel(id=new_id).put() ids.add(new_id)
def test_message(self) -> None: model = base_models.BaseModel(id='?!"', created_on=self.YEAR_AGO, last_updated=self.NOW) error = base_validation_errors.ModelIdRegexError(model, '[abc]{3}') self.assertEqual( error.stderr, 'ModelIdRegexError in BaseModel(id="?!\\""): id does not ' 'match the expected regex="[abc]{3}"')
def test_message(self) -> None: model = base_models.BaseModel(id='123', created_on=self.NOW, last_updated=self.YEAR_LATER) error = base_validation_errors.ModelMutatedDuringJobError(model) self.assertEqual( error.stderr, 'ModelMutatedDuringJobError in BaseModel(id="123"): ' 'last_updated=%r is later than the audit job\'s start time' % (model.last_updated))
def test_message(self) -> None: model = base_models.BaseModel(id='123', created_on=self.NOW, last_updated=self.YEAR_AGO) error = base_validation_errors.InconsistentTimestampsError(model) self.assertEqual( error.stderr, 'InconsistentTimestampsError in BaseModel(id="123"): ' 'created_on=%r is later than last_updated=%r' % (self.NOW, self.YEAR_AGO))
def test_message(self) -> None: model = base_models.BaseModel(id='123', deleted=True, created_on=self.YEAR_AGO, last_updated=self.YEAR_AGO) error = base_validation_errors.ModelExpiredError(model) self.assertEqual( error.stderr, 'ModelExpiredError in BaseModel(id="123"): deleted=True when ' 'older than %d days' % (feconf.PERIOD_TO_HARD_DELETE_MODELS_MARKED_AS_DELETED.days))
def test_delete_multi(self) -> None: model1 = base_models.BaseModel() model2 = base_models.BaseModel() model3 = base_models.BaseModel() model2.deleted = True model1.update_timestamps() model1.put() model2.update_timestamps() model2.put() model3.update_timestamps() model3.put() model1_id = model1.id model2_id = model2.id model3_id = model3.id base_models.BaseModel.delete_multi([model1, model2, model3]) result = base_models.BaseModel.get_multi([ model1_id, model2_id, model3_id]) self.assertEqual(result, [None, None, None])
def test_model_domain_object_validate_error(self) -> None: model = base_models.BaseModel(id='123', deleted=True, created_on=self.YEAR_AGO, last_updated=self.YEAR_AGO) error_message = 'Invalid validation type for domain object: Invalid' error = base_validation_errors.ModelDomainObjectValidateError( model, error_message) msg = ('ModelDomainObjectValidateError in BaseModel(id="123"): Entity' ' fails domain validation with the error: %s' % error_message) self.assertEqual(error.stderr, msg)
def test_get_multi(self) -> None: model1 = base_models.BaseModel() model2 = base_models.BaseModel() model3 = base_models.BaseModel() model2.deleted = True model1.update_timestamps() model1.put() model2.update_timestamps() model2.put() model3.update_timestamps() model3.put() model1_id = model1.id model2_id = model2.id model3_id = model3.id # For all the None ids, get_multi should return None at the appropriate # position. result = base_models.BaseModel.get_multi( [model1_id, model2_id, None, model3_id, 'none', None]) self.assertEqual(result, [model1, None, None, model3, None, None])
def test_validation_type_for_domain_object(self) -> None: model = base_models.BaseModel( id='mock-123', deleted=False, created_on=self.YEAR_AGO, last_updated=self.NOW) output = ( self.pipeline | beam.Create([model]) | beam.ParDo( base_validation.ValidateModelDomainObjectInstances()) ) self.assert_pcoll_equal(output, [])
def test_process_reports_model_mutated_during_job_error(self) -> None: invalid_timestamp = base_models.BaseModel( id='124', created_on=self.NOW, last_updated=self.YEAR_LATER) output = ( self.pipeline | beam.Create([invalid_timestamp]) | beam.ParDo(base_validation.ValidateModelTimestamps()) ) self.assert_pcoll_equal(output, [ base_validation_errors.ModelMutatedDuringJobError( invalid_timestamp), ])
def test_process_reports_model_timestamp_relationship_error(self) -> None: invalid_timestamp = base_models.BaseModel( id='123', created_on=self.NOW, last_updated=self.YEAR_AGO) output = ( self.pipeline | beam.Create([invalid_timestamp]) | beam.ParDo(base_validation.ValidateModelTimestamps()) ) self.assert_pcoll_equal(output, [ base_validation_errors.InconsistentTimestampsError( invalid_timestamp), ])
def test_process_reports_error_for_old_deleted_model(self) -> None: expired_model = base_models.BaseModel( id='123', deleted=True, created_on=self.YEAR_AGO, last_updated=self.YEAR_AGO) output = ( self.pipeline | beam.Create([expired_model]) | beam.ParDo(base_validation.ValidateDeletedModel()) ) self.assert_pcoll_equal(output, [ base_validation_errors.ModelExpiredError(expired_model), ])
def test_validate_model_id(self) -> None: invalid_id_model = base_models.BaseModel( id='123@?!*', created_on=self.YEAR_AGO, last_updated=self.NOW) output = ( self.pipeline | beam.Create([invalid_id_model]) | beam.ParDo(base_validation.ValidateBaseModelId()) ) self.assert_pcoll_equal(output, [ base_validation_errors.ModelIdRegexError( invalid_id_model, base_validation.BASE_MODEL_ID_PATTERN), ])
def test_put_multi(self): # type: () -> None models_1 = [base_models.BaseModel() for _ in python_utils.RANGE(3)] for model in models_1: self.assertIsNone(model.created_on) self.assertIsNone(model.last_updated) # Field last_updated will get updated anyway because it is None. base_models.BaseModel.update_timestamps_multi( models_1, update_last_updated_time=False) base_models.BaseModel.put_multi(models_1) model_ids = [model.id for model in models_1] last_updated_values = [] for model_id in model_ids: model = base_models.BaseModel.get_by_id(model_id) self.assertIsNotNone(model.created_on) self.assertIsNotNone(model.last_updated) last_updated_values.append(model.last_updated) # Field last_updated won't get updated because update_last_updated_time # is set to False and last_updated already has some value. models_2_without_none = cast( List[base_models.BaseModel], base_models.BaseModel.get_multi(model_ids) ) base_models.BaseModel.update_timestamps_multi( models_2_without_none, update_last_updated_time=False) base_models.BaseModel.put_multi(models_2_without_none) for model_id, last_updated in python_utils.ZIP( model_ids, last_updated_values): model = base_models.BaseModel.get_by_id(model_id) self.assertEqual(model.last_updated, last_updated) # Field last_updated will get updated because update_last_updated_time # is set to True (by default). models_3_without_none = cast( List[base_models.BaseModel], base_models.BaseModel.get_multi(model_ids) ) base_models.BaseModel.update_timestamps_multi(models_3_without_none) base_models.BaseModel.put_multi(models_3_without_none) for model_id, last_updated in python_utils.ZIP( model_ids, last_updated_values): model = base_models.BaseModel.get_by_id(model_id) self.assertNotEqual(model.last_updated, last_updated)
def test_error_is_raised_with_invalid_validation_type_for_domain_object( self ) -> None: model = base_models.BaseModel( id='mock-123', deleted=False, created_on=self.YEAR_AGO, last_updated=self.NOW) output = ( self.pipeline | beam.Create([model]) | beam.ParDo(MockValidateModelDomainObjectInstancesWithInvalid()) ) self.assert_pcoll_equal(output, [ base_validation_errors.ModelDomainObjectValidateError( model, 'Invalid validation type for domain object: invalid') ])
def setUp(self) -> None: super(BaseAuditErrorTests, self).setUp() self.model = base_models.BaseModel(id='123')
def test_get_from_datastore_model(self) -> None: model = base_models.BaseModel() self.assertEqual(job_utils.get_model_kind(model), 'BaseModel')