def test_direct_entries_access(ctx): from melkman.db.bucket import NewsBucket, NewsItemRef # if you add something to bucket.entries directly rather than going # through the interface, make sure it's there after saving (i.e. NewsBucket # is properly observing changes to the underlying nldict) bucket = NewsBucket.create(ctx) assert len(bucket.entries) == 0 item1 = NewsItemRef.create_from_info(ctx, bucket.id, item_id=random_id()) item2 = NewsItemRef.create_from_info(ctx, bucket.id, item_id=random_id()) bucket.entries[item1.item_id] = item1 bucket.entries[item2.item_id] = item2 assert len(bucket.entries) == 2 assert bucket.has_news_item(item1) assert bucket.has_news_item(item2) bucket.save() bucket.reload() assert len(bucket.entries) == 2 assert bucket.has_news_item(item1) assert bucket.has_news_item(item2) # directly changing the maxlen of the underlying nldict instead of using # NewsBucket.set_maxlen is *not* supported: bucket.entries.maxlen = 1 # causes items to be deleted in memory: assert len(bucket.entries) == 1 # but the document's maxlen field is not updated: assert bucket.maxlen is None # so after re-retrieving the document the nldict's maxlen is the old value bucket.reload() assert bucket.entries.maxlen is None assert len(bucket.entries) == 2 # and the bucket still has two items # if we save after changing the nldict's maxlen... bucket.entries.maxlen = 1 # items deleted in memory assert len(bucket.entries) == 1 bucket.save() # ...items are now deleted from persistent storage too bucket.reload() assert len(bucket.entries) == 1 # but the maxlen field on the document was still never set assert bucket.maxlen == None # add back both items since one of them was discarded bucket.entries.update({item1.item_id: item1, item2.item_id: item2}) bucket.save() bucket.reload() assert len(bucket.entries) == 2 # if we set the maxlen field on the bucket rather than using set_maxlen... bucket.maxlen = 1 # maxlen changes in the document but the underlying nldict is not updated: assert len(bucket.entries) == 2 # ...until *after* saving: bucket.save() bucket.reload() assert len(bucket.entries) == 1
def test_bucket_maxlen(ctx): """ Test that bucket with maxlen behaves as expected """ from melkman.db.bucket import NewsBucket, NewsItemRef, SORTKEY from datetime import datetime, timedelta from operator import attrgetter # add 10 items spaced an hour apart to a bucket of max-length 3: maxlen = 3 sortkey = SORTKEY # for now this is hardcoded in melkman.db.bucket bucket = NewsBucket.create(ctx, maxlen=maxlen) assert bucket.maxlen == maxlen items = [] timestamp = datetime.utcnow() for i in xrange(10): item = NewsItemRef.create_from_info(ctx, bucket.id, item_id=random_id(), timestamp=timestamp - timedelta(hours=i), ) items.append(item) bucket.add_news_item(item) # make sure the bucket has only the maxlen latest items # before and after saving: idgetter = attrgetter('item_id') sorteditemsids = map(idgetter, sorted(items, key=sortkey)) def ids_by_timestamp(entries): return map(idgetter, sorted(entries.values(), key=sortkey)) def check_before_and_after_save(bucket): bucketlen = len(bucket.entries) if bucket.maxlen is not None: assert bucketlen <= bucket.maxlen assert ids_by_timestamp(bucket.entries) == sorteditemsids[-bucketlen:] bucket.save() bucket.reload() assert ids_by_timestamp(bucket.entries) == sorteditemsids[-bucketlen:] return bucket bucket = check_before_and_after_save(bucket) # decrease maxlen and make sure bucket.entries remains consistent: maxlen -= 1 bucket.set_maxlen(maxlen) bucket = check_before_and_after_save(bucket) # now increase maxlen so that the bucket is under capacity and check consistency: maxlen += 2 bucket.set_maxlen(maxlen) bucket = check_before_and_after_save(bucket) # fill to capacity and check that the new maxlen is maintained: for i in items: bucket.add_news_item(i) bucket = check_before_and_after_save(bucket) # now set the maxlen to None and make sure it becomes an unbounded bucket: maxlen = None bucket.set_maxlen(maxlen) for i in items: bucket.add_news_item(i) bucket = check_before_and_after_save(bucket)