#!/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()