import unittest
import time
import random

# Mock the latency monitoring and scaling components for testing
class MockLatencyMonitor:
    def __init__(self, initial_latency):
        self.latency = initial_latency

    def get_current_latency(self):
        return self.latency

    def set_latency(self, latency):
        self.latency = latency

class MockScalingAlgorithm:
    def __init__(self, initial_threshold):
        self.threshold = initial_threshold
        self.scale_called = False
        self.scale_direction = None

    def adjust_threshold(self, latency):
        # Simple adjustment logic: increase threshold if latency is consistently high,
        # decrease if consistently low
        if latency > self.threshold * 1.1:
            self.threshold *= 1.05  # Increase by 5%
        elif latency < self.threshold * 0.9:
            self.threshold *= 0.95  # Decrease by 5%
        return self.threshold

    def scale(self, direction):
        self.scale_called = True
        self.scale_direction = direction


class TestDynamicThresholds(unittest.TestCase):

    def test_sudden_spike(self):
        initial_latency = 50
        monitor = MockLatencyMonitor(initial_latency)
        scaler = MockScalingAlgorithm(100)  # Initial threshold

        # Simulate a sudden spike in latency
        monitor.set_latency(200)
        new_threshold = scaler.adjust_threshold(monitor.get_current_latency())
        self.assertGreater(new_threshold, 100) # Threshold should increase

        if monitor.get_current_latency() > new_threshold:
            scaler.scale("up") # Scale up if latency exceeds threshold

        self.assertTrue(scaler.scale_called)
        self.assertEqual(scaler.scale_direction, "up")


    def test_gradual_increase(self):
        initial_latency = 50
        monitor = MockLatencyMonitor(initial_latency)
        scaler = MockScalingAlgorithm(100)  # Initial threshold

        # Simulate a gradual increase in latency over time
        for i in range(5):
            monitor.set_latency(monitor.get_current_latency() + 20)
            new_threshold = scaler.adjust_threshold(monitor.get_current_latency())

            #Threshold should increase over time
            self.assertGreaterEqual(new_threshold, 100)

            if monitor.get_current_latency() > new_threshold:
                scaler.scale("up")
                break # Scale only once

        self.assertTrue(scaler.scale_called)
        self.assertEqual(scaler.scale_direction, "up")


    def test_gradual_decrease(self):
        initial_latency = 150
        monitor = MockLatencyMonitor(initial_latency)
        scaler = MockScalingAlgorithm(100)  # Initial threshold

        # Simulate a gradual decrease in latency over time
        for i in range(5):
            monitor.set_latency(monitor.get_current_latency() - 10)
            new_threshold = scaler.adjust_threshold(monitor.get_current_latency())
            self.assertLessEqual(new_threshold, 100)

            if monitor.get_current_latency() < new_threshold:
                scaler.scale("down")
                break # Scale only once

        self.assertTrue(scaler.scale_called)
        self.assertEqual(scaler.scale_direction, "down")

    def test_no_scaling(self):
        initial_latency = 95
        monitor = MockLatencyMonitor(initial_latency)
        scaler = MockScalingAlgorithm(100)  # Initial threshold

        #Latency is close to threshold, no scaling should occur
        monitor.set_latency(98)
        new_threshold = scaler.adjust_threshold(monitor.get_current_latency())
        self.assertEqual(scaler.scale_called, False)


if __name__ == '__main__':
    unittest.main()
