def test_decimaltimestamp_corresponds_with_decimaltimestamp_from_uuid( self): if os.getenv("TRAVIS_PYTHON_VERSION") in [ "3.6", "3.7", "3.7-dev", "pypy3.5" ]: self.skipTest("Somehow this fails on Travis dist:xenial.") # This is the weird error that happens in Python 3.7.1 on Travis Xenial dist. # Why does the second timestamp "happen" more than one second before the first? # Perhaps UUID1 doesn't actually use time.time() sometimes? Maybe it was a bug in v3.7.1? """ FAIL: test_decimaltimestamp_corresponds_with_decimaltimestamp_from_uuid ( eventsourcing.tests.core_tests.test_utils.TestUtils) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/travis/build/johnbywater/eventsourcing/eventsourcing/tests/core_tests/test_utils.py", line 61, in test_decimaltimestamp_corresponds_with_decimaltimestamp_from_uuid self.assertLess(timestamp1, timestamp2) AssertionError: Decimal('1561464862.322443') not less than Decimal('1561464861.100940') """ timestamp1 = decimaltimestamp() sleep(0.000001) uuid_1 = uuid1() sleep(0.000001) timestamp3 = decimaltimestamp() sleep(0.000001) timestamp2 = decimaltimestamp_from_uuid(uuid_1) self.assertLess(timestamp1, timestamp2) self.assertLess(timestamp2, timestamp3)
def test_with_timestamped_entity_event(self): # Setup the mapper, and create an event. mapper = SequencedItemMapper( sequenced_item_class=SequencedItem, sequence_id_attr_name="originator_id", position_attr_name="timestamp", ) before = decimaltimestamp() sleep(0.000001) # Avoid test failing due to timestamp having limited precision. event2 = Event2(originator_id="entity2") sleep(0.000001) # Avoid test failing due to timestamp having limited precision. after = decimaltimestamp() # Check to_sequenced_item() method results in a sequenced item. sequenced_item = mapper.item_from_event(event2) self.assertIsInstance(sequenced_item, SequencedItem) self.assertGreater(sequenced_item.position, before) self.assertLess(sequenced_item.position, after) self.assertEqual(sequenced_item.sequence_id, "entity2") self.assertEqual(sequenced_item.topic, get_topic(Event2)) self.assertTrue(sequenced_item.state) # Use the returned values to create a new sequenced item. sequenced_item_copy = SequencedItem( sequence_id=sequenced_item.sequence_id, position=sequenced_item.position, topic=sequenced_item.topic, state=sequenced_item.state, ) # Check from_sequenced_item() returns an event. domain_event = mapper.event_from_item(sequenced_item_copy) self.assertIsInstance(domain_event, Event2) self.assertEqual(domain_event.originator_id, event2.originator_id) self.assertEqual(domain_event.timestamp, event2.timestamp)
def test_one_subclass(self): # Check base class can be sub-classed. class Event(EventWithTimestamp, EventWithOriginatorID): pass # Check construction requires an ID. with self.assertRaises(TypeError): Event() # Get timestamp before events. time1 = decimaltimestamp() # Construct events. event1 = Event(originator_id="1") event2 = Event(originator_id="1") # Check the entity IDs. self.assertEqual(event1.originator_id, "1") self.assertEqual(event2.originator_id, "1") # Check the event timestamps. self.assertLessEqual(time1, event1.timestamp) self.assertLessEqual(event1.timestamp, event2.timestamp) self.assertLessEqual(event2.timestamp, decimaltimestamp()) # Check the events are not equal to each other, whilst being equal to themselves. self.assertEqual(event1, event1) self.assertEqual(event2, event2) self.assertNotEqual(event1, event2)
def test_timestamp_from_uuid(self): until = decimaltimestamp() uuid = uuid1() after = decimaltimestamp() uuid_timestamp = decimaltimestamp_from_uuid(uuid) self.assertLess(until, uuid_timestamp) self.assertGreater(after, uuid_timestamp) # Check timestamp_from_uuid() works with hex strings, as well as UUID objects. self.assertEqual(decimaltimestamp_from_uuid(uuid.hex), decimaltimestamp_from_uuid(uuid))
def test_decimaltimestamp(self): # Check a given float is correctly converted to a Decimal. self.assertEqual(decimaltimestamp(123456789.1234560), Decimal("123456789.123456")) self.assertEqual(decimaltimestamp(1234567890.1234560), Decimal("1234567890.123456")) # Generate a series of timestamps. timestamps = [decimaltimestamp() for _ in range(100)] # Check series doesn't decrease at any point. last = timestamps[0] for timestamp in timestamps[1:]: self.assertLessEqual(last, timestamp) last = timestamp
def test(self): # Check base class can be sub-classed. class Event(EventWithTimeuuid): pass # Check event has a UUID event_id. event = Event() self.assertIsInstance(event.event_id, UUID) # Check the event_id can't be reassigned. with self.assertRaises(AttributeError): # noinspection PyPropertyAccess event.event_id = decimaltimestamp() # Check event can be instantiated with a given UUID. event_id = uuid1() event = Event(event_id=event_id) self.assertEqual(event.event_id, event_id) # Generate a series of timestamps. events = [Event() for _ in range(100)] timestamps = [decimaltimestamp_from_uuid(e.event_id) for e in events] # Check series doesn't decrease at any point. last = timestamps[0] for timestamp in timestamps[1:]: self.assertLessEqual(last, timestamp) last = timestamp # Check last timestamp is greater than the first. self.assertGreater(timestamps[-1], timestamps[0])
def log_message(self, message: str) -> "MessageLogged": assert isinstance(message, str) bucket_id = make_timebucket_id(self.name, decimaltimestamp(), self.bucket_size) event = MessageLogged(originator_id=bucket_id, message=message) publish([event]) return event
def get_events(self, gt=None, gte=None, lt=None, lte=None, limit=None, is_ascending=False, page_size=None): assert limit is None or limit > 0 # Identify the first time bucket. now = decimaltimestamp() started_on = self.log.started_on absolute_latest = min(now, lt or now, lte or now) absolute_earlyist = max(started_on, gt or 0, gte or 0) if is_ascending: position = absolute_earlyist else: position = absolute_latest # Start counting events. count_events = 0 while True: bucket_id = make_timebucket_id(self.log.name, position, self.log.bucket_size) for message_logged_event in self.event_store.get_domain_events( originator_id=bucket_id, gt=gt, gte=gte, lt=lt, lte=lte, limit=limit, is_ascending=is_ascending, page_size=page_size, ): yield message_logged_event if limit is not None: count_events += 1 if count_events >= limit: return # See if there's another bucket. if is_ascending: next_timestamp = next_bucket_starts(position, self.log.bucket_size) if next_timestamp > absolute_latest: return else: position = next_timestamp else: if position < absolute_earlyist: return else: position = previous_bucket_starts(position, self.log.bucket_size)
def append_message(self, message): assert isinstance(message, six.string_types) bucket_id = make_timebucket_id(self.name, decimaltimestamp(), self.bucket_size) event = MessageLogged( originator_id=bucket_id, message=message, ) publish(event) return event
def test(self): # Check base class can be sub-classed. class Event(EventWithTimestamp): pass # Check event can be instantiated with a timestamp. time1 = decimaltimestamp() event = Event(timestamp=time1) self.assertEqual(event.timestamp, time1) # Check event can be instantiated without a timestamp. event = Event() self.assertGreaterEqual(event.timestamp, time1) self.assertLessEqual(event.timestamp, decimaltimestamp()) # Check the timestamp value can't be reassigned. with self.assertRaises(AttributeError): # noinspection PyPropertyAccess event.timestamp = decimaltimestamp()
def test(self): # Check base class can be sub-classed. class Event(EventWithTimeuuid): pass # Check event can be instantiated with an event_id. event_id = uuid1() event = Event(event_id=event_id) self.assertEqual(event.event_id, event_id) # Check event can be instantiated without an event_id. time1 = decimaltimestamp() event = Event() self.assertGreater(decimaltimestamp_from_uuid(event.event_id), time1) self.assertLess(decimaltimestamp_from_uuid(event.event_id), decimaltimestamp()) # Check the event_id can't be reassigned. with self.assertRaises(AttributeError): # noinspection PyPropertyAccess event.event_id = decimaltimestamp()
def setup_sequenced_items(self): self.sequenced_items = [] self.number_of_sequenced_items = 12 for i in range(self.number_of_sequenced_items): sequenced_item = SequencedItem( sequence_id=self.entity_id, position=i, topic='eventsourcing.example.domain_model#Example.Created', state='{"i":%s,"entity_id":"%s","timestamp":%s}' % (i, self.entity_id, decimaltimestamp()), ) self.sequenced_items.append(sequenced_item) self.entity_record_manager.record_sequenced_items(sequenced_item)
def construct_positions(self, num=3): while num: yield decimaltimestamp() num -= 1 sleep(0.00001)
def __init__(self, timestamp=None, **kwargs): kwargs['timestamp'] = timestamp or decimaltimestamp() super(EventWithTimestamp, self).__init__(**kwargs)
def __init__(self, timestamp: Optional[Decimal] = None, **kwargs: Any): kwargs["timestamp"] = timestamp or decimaltimestamp() super(EventWithTimestamp, self).__init__(**kwargs)
def test_entity_lifecycle(self): log_name = uuid4() log = self.log_repo.get_or_create(log_name=log_name, bucket_size='year') log = self.log_repo[log_name] self.assertIsInstance(log, Timebucketedlog) self.assertEqual(log.name, log_name) self.assertEqual(log.bucket_size, 'year') # Append some messages to the log. message1 = 'This is message 1' message2 = 'This is message 2' message3 = 'This is message 3' message4 = 'This is message 4' message5 = 'This is message 5' message6 = 'This is message 6' event1 = log.append_message(message1) event2 = log.append_message(message2) event3 = log.append_message(message3) halfway = decimaltimestamp() event4 = log.append_message(message4) event5 = log.append_message(message5) event6 = log.append_message(message6) # Read messages from the log. reader = TimebucketedlogReader(log, event_store=self.log_event_store) messages = list(reader.get_messages(is_ascending=False)) self.assertEqual(len(messages), 6) self.assertEqual(messages[0], message6) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message4) self.assertEqual(messages[3], message3) self.assertEqual(messages[4], message2) self.assertEqual(messages[5], message1) # Check we can get all the messages (query running in ascending order). messages = list(reader.get_messages(is_ascending=True)) self.assertEqual(len(messages), 6) self.assertEqual(messages[0], message1) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message3) self.assertEqual(messages[3], message4) self.assertEqual(messages[4], message5) self.assertEqual(messages[5], message6) # Check we can get messages after halfway (query running in descending order). messages = list(reader.get_messages(gt=halfway, is_ascending=False)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message6) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message4) # Check we can get messages until halfway (query running in descending order). messages = list(reader.get_messages(lte=halfway, is_ascending=False)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message3) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message1) # Check we can get messages until halfway (query running in ascending order). messages = list(reader.get_messages(lte=halfway, is_ascending=True)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message1) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message3) # Check we can get messages after halfway (query running in ascending order). messages = list(reader.get_messages(gt=halfway, is_ascending=True)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message4) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message6) # Check we can get last three messages (query running in descending order). messages = list(reader.get_messages(limit=3, is_ascending=False)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message6) self.assertEqual(messages[1], message5) self.assertEqual(messages[2], message4) # Check we can get first three messages (query running in ascending order). messages = list(reader.get_messages(limit=3, is_ascending=True)) self.assertEqual(len(messages), 3) self.assertEqual(messages[0], message1) self.assertEqual(messages[1], message2) self.assertEqual(messages[2], message3) # Check we can get last line (query running in descending order). messages = list( reader.get_messages(limit=1, gt=halfway, is_ascending=False)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message6) # Check we can get the first line after halfway (query running in ascending order). messages = list( reader.get_messages(limit=1, gt=halfway, is_ascending=True)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message4) # Check we can get the first line before halfway (query running in descending order). messages = list( reader.get_messages(limit=1, lte=halfway, is_ascending=False)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message3) # Check we can get the first line (query running in ascending order). messages = list( reader.get_messages(limit=1, lte=halfway, is_ascending=True)) self.assertEqual(len(messages), 1) self.assertEqual(messages[0], message1) # Check there isn't a line after the last line (query running in ascending order). messages = list( reader.get_messages(limit=1, gt=event6.timestamp, is_ascending=True)) self.assertEqual(len(messages), 0) # Check there is nothing somehow both after and until halfway. messages = list(reader.get_messages(gt=halfway, lte=halfway)) self.assertEqual(len(messages), 0)
def test_buckets_of_all_sizes(self): # Start new second sized log. log_id2 = uuid4() log = start_new_timebucketedlog(name=log_id2, bucket_size='second') log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new minute sized log. log_id3 = uuid4() log = start_new_timebucketedlog(name=log_id3, bucket_size='minute') log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new hour sized log. log_id4 = uuid4() log = start_new_timebucketedlog(name=log_id4, bucket_size='hour') log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new day sized log. log_id5 = uuid4() log = start_new_timebucketedlog(name=log_id5, bucket_size='day') log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new month sized log. log_id6 = uuid4() log = start_new_timebucketedlog(name=log_id6, bucket_size='month') log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new year sized log. log_id7 = uuid4() log = start_new_timebucketedlog(name=log_id7, bucket_size='year') log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new default sized log. log_id8 = uuid4() log = start_new_timebucketedlog(name=log_id8) log.append_message('message') # Get the messages. reader = TimebucketedlogReader(log, self.log_event_store) self.assertTrue(len(list(reader.get_messages()))) # Start new invalid sized log. with self.assertRaises(ValueError): log_id9 = uuid4() log = start_new_timebucketedlog(name=log_id9, bucket_size='invalid') # Check the helper methods are protected against invalid bucket sizes. with self.assertRaises(ValueError): log_id10 = uuid4() make_timebucket_id(log_id10, decimaltimestamp(), bucket_size='invalid') with self.assertRaises(ValueError): bucket_starts(decimaltimestamp(), bucket_size='invalid') with self.assertRaises(ValueError): bucket_duration(bucket_size='invalid')