简体   繁体   中英

How to test a rate limiting algorithm?

This is the sliding window rate limiting algorithm I got from this page :

from time import time, sleep

class SlidingWindow:

    def __init__(self, capacity, time_unit, forward_callback, drop_callback):
        self.capacity = capacity
        self.time_unit = time_unit
        self.forward_callback = forward_callback
        self.drop_callback = drop_callback

        self.cur_time = time()
        self.pre_count = capacity
        self.cur_count = 0

    def handle(self, packet):

        if (time() - self.cur_time) > self.time_unit:
            self.cur_time = time()
            self.pre_count = self.cur_count
            self.cur_count = 0

        ec = (self.pre_count * (self.time_unit - (time() - self.cur_time)) / self.time_unit) + self.cur_count

        if (ec > self.capacity):
            return self.drop_callback(packet)

        self.cur_count += 1
        return self.forward_callback(packet)


def forward(packet):
    print("Packet Forwarded: " + str(packet))


def drop(packet):
    print("Packet Dropped: " + str(packet))


throttle = SlidingWindow(5, 1, forward, drop)

packet = 0

while True:
    sleep(0.1)
    throttle.handle(packet)
    packet += 1

How can I test this time sensitive algorithm in pytest? I want to test forward_callback is getting called when the request (by calling handle() ) passes, and drop_callback is getting called when the request fails. How could I simulate a loop in pytest?

You should test the SlidingWindow class directly, without testing the loop in the end. The reason is that the SlidingWindow is independent and it should work regardless of how it's used.

Since the class is heavily dependent on the current time, you can use pytest-freezegun to simulate the time of the requests.

A test example would be something similar to:

from datetime import datetime, timedelta
from mock import MagicMock, call

from sliding_window import SlidingWindow


def test_moving_date(freezer, mg):
    start = datetime(year=2012, month=1, day=14, hour=12, minute=0, second=1)
    freezer.move_to(start)
    forward, drop = MagicMock(), MagicMock()
    throttle = SlidingWindow(5, 1, forward, drop)  # allow rate ~5 per second

    # first two should be accepted, while the 3rd should be dropped
    throttle.handle(0)
    freezer.move_to(start + timedelta(milliseconds=200))
    throttle.handle(1)
    freezer.move_to(start + timedelta(milliseconds=250))
    throttle.handle(2)

    forward.assert_has_calls(calls=[call(0), call(1)])
    drop.assert_called_once_with(2)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM