123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- #!/usr/bin/env python3
- """
- Test suite for the Price Alarm System
- Tests alarm creation, management, triggering, and integration.
- """
- import unittest
- import tempfile
- import os
- from datetime import datetime
- from alarm_manager import AlarmManager
- class TestAlarmManager(unittest.TestCase):
- """Test the AlarmManager class."""
-
- def setUp(self):
- """Set up test fixtures."""
- # Use temporary file for testing
- self.temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.json')
- self.temp_file.close()
- self.alarm_manager = AlarmManager(self.temp_file.name)
-
- def tearDown(self):
- """Clean up after tests."""
- # Remove temporary file
- if os.path.exists(self.temp_file.name):
- os.unlink(self.temp_file.name)
-
- def test_create_alarm_above(self):
- """Test creating an alarm for price going above current price."""
- # Current price $50, target $55 (above)
- alarm = self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
-
- self.assertEqual(alarm['id'], 1)
- self.assertEqual(alarm['token'], 'BTC')
- self.assertEqual(alarm['target_price'], 55.0)
- self.assertEqual(alarm['current_price_when_set'], 50.0)
- self.assertEqual(alarm['direction'], 'above')
- self.assertEqual(alarm['status'], 'active')
-
- def test_create_alarm_below(self):
- """Test creating an alarm for price going below current price."""
- # Current price $50, target $45 (below)
- alarm = self.alarm_manager.create_alarm("ETH", 45.0, 50.0)
-
- self.assertEqual(alarm['id'], 1)
- self.assertEqual(alarm['token'], 'ETH')
- self.assertEqual(alarm['target_price'], 45.0)
- self.assertEqual(alarm['current_price_when_set'], 50.0)
- self.assertEqual(alarm['direction'], 'below')
- self.assertEqual(alarm['status'], 'active')
-
- def test_multiple_alarms_increment_id(self):
- """Test that multiple alarms get incrementing IDs."""
- alarm1 = self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- alarm2 = self.alarm_manager.create_alarm("ETH", 3500.0, 3000.0)
- alarm3 = self.alarm_manager.create_alarm("BTC", 45.0, 50.0)
-
- self.assertEqual(alarm1['id'], 1)
- self.assertEqual(alarm2['id'], 2)
- self.assertEqual(alarm3['id'], 3)
-
- def test_get_alarms_by_token(self):
- """Test filtering alarms by token."""
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- self.alarm_manager.create_alarm("ETH", 3500.0, 3000.0)
- self.alarm_manager.create_alarm("BTC", 45.0, 50.0)
-
- btc_alarms = self.alarm_manager.get_alarms_by_token("BTC")
- eth_alarms = self.alarm_manager.get_alarms_by_token("ETH")
-
- self.assertEqual(len(btc_alarms), 2)
- self.assertEqual(len(eth_alarms), 1)
- self.assertEqual(btc_alarms[0]['token'], 'BTC')
- self.assertEqual(eth_alarms[0]['token'], 'ETH')
-
- def test_remove_alarm(self):
- """Test removing an alarm by ID."""
- alarm = self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- alarm_id = alarm['id']
-
- # Verify alarm exists
- self.assertIsNotNone(self.alarm_manager.get_alarm_by_id(alarm_id))
-
- # Remove alarm
- result = self.alarm_manager.remove_alarm(alarm_id)
- self.assertTrue(result)
-
- # Verify alarm is gone
- self.assertIsNone(self.alarm_manager.get_alarm_by_id(alarm_id))
-
- def test_remove_nonexistent_alarm(self):
- """Test removing an alarm that doesn't exist."""
- result = self.alarm_manager.remove_alarm(999)
- self.assertFalse(result)
-
- def test_check_alarms_trigger_above(self):
- """Test triggering an alarm when price goes above target."""
- # Create alarm: trigger when BTC goes above $55
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
-
- # Price data: BTC is now $56 (above $55)
- price_data = {"BTC": 56.0}
- triggered = self.alarm_manager.check_alarms(price_data)
-
- self.assertEqual(len(triggered), 1)
- self.assertEqual(triggered[0]['token'], 'BTC')
- self.assertEqual(triggered[0]['triggered_price'], 56.0)
- self.assertEqual(triggered[0]['status'], 'triggered')
-
- def test_check_alarms_trigger_below(self):
- """Test triggering an alarm when price goes below target."""
- # Create alarm: trigger when ETH goes below $45
- self.alarm_manager.create_alarm("ETH", 45.0, 50.0)
-
- # Price data: ETH is now $44 (below $45)
- price_data = {"ETH": 44.0}
- triggered = self.alarm_manager.check_alarms(price_data)
-
- self.assertEqual(len(triggered), 1)
- self.assertEqual(triggered[0]['token'], 'ETH')
- self.assertEqual(triggered[0]['triggered_price'], 44.0)
- self.assertEqual(triggered[0]['status'], 'triggered')
-
- def test_check_alarms_no_trigger(self):
- """Test that alarms don't trigger when conditions aren't met."""
- # Create alarm: trigger when BTC goes above $55
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
-
- # Price data: BTC is now $54 (still below $55)
- price_data = {"BTC": 54.0}
- triggered = self.alarm_manager.check_alarms(price_data)
-
- self.assertEqual(len(triggered), 0)
-
- # Verify alarm is still active
- active_alarms = self.alarm_manager.get_all_active_alarms()
- self.assertEqual(len(active_alarms), 1)
- self.assertEqual(active_alarms[0]['status'], 'active')
-
- def test_check_alarms_exact_price(self):
- """Test that alarms trigger at exact target price."""
- # Create alarm: trigger when BTC goes above $55
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
-
- # Price data: BTC is exactly $55
- price_data = {"BTC": 55.0}
- triggered = self.alarm_manager.check_alarms(price_data)
-
- self.assertEqual(len(triggered), 1)
- self.assertEqual(triggered[0]['triggered_price'], 55.0)
-
- def test_alarm_only_triggers_once(self):
- """Test that triggered alarms don't trigger again."""
- # Create alarm
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
-
- # Trigger alarm
- price_data = {"BTC": 56.0}
- triggered1 = self.alarm_manager.check_alarms(price_data)
- self.assertEqual(len(triggered1), 1)
-
- # Try to trigger again with even higher price
- price_data = {"BTC": 60.0}
- triggered2 = self.alarm_manager.check_alarms(price_data)
- self.assertEqual(len(triggered2), 0) # Should not trigger again
-
- def test_multiple_alarms_selective_trigger(self):
- """Test that only applicable alarms trigger."""
- # Create multiple alarms
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0) # Above $55
- self.alarm_manager.create_alarm("ETH", 45.0, 50.0) # Below $45
- self.alarm_manager.create_alarm("BTC", 40.0, 50.0) # Below $40
-
- # Price data: BTC $56 (triggers first), ETH $46 (doesn't trigger)
- price_data = {"BTC": 56.0, "ETH": 46.0}
- triggered = self.alarm_manager.check_alarms(price_data)
-
- self.assertEqual(len(triggered), 1)
- self.assertEqual(triggered[0]['token'], 'BTC')
- self.assertEqual(triggered[0]['target_price'], 55.0)
-
- def test_persistence(self):
- """Test that alarms persist across manager instances."""
- # Create alarm
- alarm = self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- alarm_id = alarm['id']
-
- # Create new manager instance with same file
- new_manager = AlarmManager(self.temp_file.name)
-
- # Check that alarm persists
- restored_alarm = new_manager.get_alarm_by_id(alarm_id)
- self.assertIsNotNone(restored_alarm)
- self.assertEqual(restored_alarm['token'], 'BTC')
- self.assertEqual(restored_alarm['target_price'], 55.0)
- self.assertEqual(restored_alarm['status'], 'active')
-
- def test_format_alarm_list_empty(self):
- """Test formatting empty alarm list."""
- message = self.alarm_manager.format_alarm_list([])
- self.assertIn("No alarms found", message)
-
- def test_format_alarm_list_with_alarms(self):
- """Test formatting alarm list with alarms."""
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- self.alarm_manager.create_alarm("ETH", 45.0, 50.0)
-
- alarms = self.alarm_manager.get_all_active_alarms()
- message = self.alarm_manager.format_alarm_list(alarms)
-
- self.assertIn("BTC", message)
- self.assertIn("ETH", message)
- self.assertIn("ID 1", message)
- self.assertIn("ID 2", message)
- self.assertIn("55.00", message)
- self.assertIn("45.00", message)
-
- def test_format_triggered_alarm(self):
- """Test formatting triggered alarm notification."""
- # Create and trigger alarm
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- price_data = {"BTC": 56.0}
- triggered = self.alarm_manager.check_alarms(price_data)
-
- message = self.alarm_manager.format_triggered_alarm(triggered[0])
-
- self.assertIn("Price Alert Triggered", message)
- self.assertIn("BTC", message)
- self.assertIn("56.00", message)
- self.assertIn("55.00", message)
- self.assertIn("50.00", message)
-
- def test_statistics(self):
- """Test alarm statistics."""
- # Create some alarms
- self.alarm_manager.create_alarm("BTC", 55.0, 50.0)
- self.alarm_manager.create_alarm("BTC", 45.0, 50.0)
- self.alarm_manager.create_alarm("ETH", 3500.0, 3000.0)
-
- # Trigger one alarm
- price_data = {"BTC": 56.0}
- self.alarm_manager.check_alarms(price_data)
-
- stats = self.alarm_manager.get_statistics()
-
- self.assertEqual(stats['total_active'], 2) # 2 still active
- self.assertEqual(stats['total_triggered'], 1) # 1 triggered
- self.assertEqual(stats['tokens_tracked'], 2) # BTC and ETH
- self.assertEqual(stats['token_breakdown']['BTC'], 1) # 1 BTC alarm left
- self.assertEqual(stats['token_breakdown']['ETH'], 1) # 1 ETH alarm
- if __name__ == '__main__':
- unittest.main()
|