import unittest
import time
from unittest.mock import patch

# Assuming autoscaler.py contains the HysteresisScaler class
# and related logic.  Adjust import path as needed.
from autoscaler import HysteresisScaler  # Replace with actual path

class TestHysteresisScaler(unittest.TestCase):

    def test_downscale_below_threshold(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 10  # Below downscale threshold
        time.sleep(2.1) # Wait longer than hysteresis time
        self.assertTrue(scaler.should_downscale())

    def test_no_downscale_above_threshold(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 30  # Above downscale threshold
        time.sleep(2.1)
        self.assertFalse(scaler.should_downscale())

    def test_downscale_after_time_below_threshold(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 10  # Below downscale threshold
        start_time = time.time()
        while time.time() - start_time < 1.5:
            time.sleep(0.1)
        self.assertFalse(scaler.should_downscale())

        while time.time() - start_time < 2.1:
            time.sleep(0.1)
        self.assertTrue(scaler.should_downscale())

    def test_timer_reset_above_threshold(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 10
        time.sleep(1) # Let some time pass below threshold
        scaler.resource_utilization = 30 # Above threshold, timer should reset
        time.sleep(1.5) # Wait, but not enough to trigger downscale
        scaler.resource_utilization = 10  # Back below threshold
        time.sleep(1.5) # Wait, but not enough to trigger downscale
        self.assertFalse(scaler.should_downscale())

        time.sleep(0.6) # Additional time to reach hysteresis period
        self.assertTrue(scaler.should_downscale())

    def test_fluctuating_resource_utilization(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 10
        time.sleep(1)
        scaler.resource_utilization = 30
        time.sleep(0.5)
        scaler.resource_utilization = 10
        time.sleep(0.5)
        scaler.resource_utilization = 30
        time.sleep(0.5)
        scaler.resource_utilization = 10
        time.sleep(1.1) # Total time below threshold: 1 + 0.5 + 0.5 + 1.1 = 3.1 > 2
        self.assertTrue(scaler.should_downscale())

    def test_upscale_above_threshold(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 90
        self.assertTrue(scaler.should_upscale())

    def test_no_upscale_below_threshold(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 70
        self.assertFalse(scaler.should_upscale())

    def test_upscale_after_immediate_drop(self):
        scaler = HysteresisScaler(upscale_threshold=80, downscale_threshold=20, hysteresis_time=2)
        scaler.resource_utilization = 90
        self.assertTrue(scaler.should_upscale())
        scaler.resource_utilization = 70
        self.assertFalse(scaler.should_upscale())

    # Add more tests as needed to cover edge cases and different scenarios

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