diff options
Diffstat (limited to 'test/alert_and_notification_logic.rb')
-rw-r--r-- | test/alert_and_notification_logic.rb | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/test/alert_and_notification_logic.rb b/test/alert_and_notification_logic.rb new file mode 100644 index 0000000..19b2478 --- /dev/null +++ b/test/alert_and_notification_logic.rb @@ -0,0 +1,391 @@ +# Mauve server tests - alerts and notification logic. Define the basic workings +# so that we know what should happen when we send sequences of alerts at +# different times. +# +# These aren't really unit tests, just narrative specifications as to what +# should happen under what stimuli. I suspect I will break these down into +# smaller units if things break under otherwise difficult conditions. +# + +$: << __FILE__.split("/")[0..-2].join("/") +require 'test/unit' +require 'mauve_test_helper' +require 'mauve_time' + +class AlertAndNotificationLogic < Test::Unit::TestCase + include MauveTestHelper + + def configuration_template + <<-TEMPLATE + # This is the head of all the configuration files. Filenames are relative + # to the cwd, which is assumed to be a fleeting test directory. + + server { + ip "127.0.0.1" + port #{@port_alerts ||= 44444} + log_file ENV['TEST_LOG'] ? STDOUT : "#{dir}/log" + log_level 0 + database "sqlite3:///#{dir}/mauve_test.db" + transmission_id_expire_time 600 + + # doesn't restart nicely at the moment + #web_interface { + # port #{@port_web ||= 44444} + #} + } + + # + # All notifications are sent to files which we can open up and check during + # our tests. Network delivery is not tested in this script. + # + + notification_method("xmpp") { + deliver_to_queue AlertAndNotificationLogic::Notifications + deliver_to_file "#{dir}/xmpp.txt" + disable_normal_delivery! + + jid "mauveserv@chat.bytemark.co.uk" + password "foo" + } + + notification_method("email") { + deliver_to_queue AlertAndNotificationLogic::Notifications + deliver_to_file "#{dir}/email.txt" + disable_normal_delivery! + + # add in SMTP server, username, password etc. + # default to sending through localhost + from "matthew@bytemark.co.uk" + server "bytemail.bytemark.co.uk" + subject_prefix "[Bytemark alerts] " + + } + + notification_method("sms") { + provider "AQL" + deliver_to_queue AlertAndNotificationLogic::Notifications + deliver_to_file "#{dir}/sms.txt" + disable_normal_delivery! + + username "x" + password "x" + from "01904890890" + max_messages_per_alert 3 + } + + # a person common to all our tests + + person("joe_bloggs") { + urgent { sms("12345") } + normal { email("12345@joe_bloggs.email") } + low { xmpp("12345@joe_bloggs.xmpp") } + } + + person("jimmy_junior") { + urgent { sms("66666") } + normal { email("jimmy@junior.email") } + low { email("jimmy@junior.email") } + } + + alert_group { + includes { source == "rare-and-important" } + acknowledgement_time 60.minutes + level URGENT + + notify("joe_bloggs") { every 10.minutes } + } + + alert_group { + includes { source == "noisy-and-annoying" || alert_id == "whine" } + acknowledgement_time 24.hours + level LOW + + notify("jimmy_junior") { every 2.hours } + notify("joe_bloggs") { + every 30.minutes + during { + unacknowledged 6.hours + } + } + } + + alert_group { + includes { source == "can-wait-until-monday" } + level NORMAL + + notify("jimmy_junior") { + every 30.minutes + during { days_in_week(1..5) && hours_in_day(9..5) } + } + notify("joe_bloggs") { + every 2.hours + during { days_in_week(1..5) && hours_in_day(9..5) } + } + } + + # catch-all + alert_group { + acknowledgement_time 1.minute + level NORMAL + + notify("joe_bloggs") { every 1.hour } + } + TEMPLATE + end + + def setup + start_server(configuration_template) + end + + def teardown + stop_server + # no tests should leave notifications on the stack + assert_no_notification + end + + # Raise one alert, check representation in database, and that alert is + # received as expected. + # + def test_basic_fields_are_recognised + mauvesend("-o my_source -i alert1 -s \"alert1 summary\" -d \"alert1 detail\" -u \"alert1 subject\"") + + assert_not_nil(alert = Alert.first) + assert_equal("my_source", alert.source) + assert_equal("alert1", alert.alert_id) + assert_equal("alert1 summary", alert.summary) + assert_equal("alert1 detail", alert.detail) + assert_equal("alert1 subject", alert.subject) + assert(alert.raised?) + assert(!alert.cleared?) + assert(!alert.acknowledged?) + + with_next_notification do |destination, this_alert, other_alerts| + assert_equal("12345@joe_bloggs.email", destination) + assert_equal(Alert.first, this_alert) + assert_equal([Alert.first], other_alerts) + end + + end + + # Check that a simple automatic raise, acknowledge & auto-clear request + # work properly. + # + def test_auto_raise_and_clear + # Raise the alert, wait for it to be processed + mauvesend("-o my_source -i alert1 -s \"alert1 summary\" -d \"alert1 detail\" -u \"alert1 subject\" -r +5m -c +10m") + + # Check internal state + # + assert(!Alert.first.raised?, "Auto-raising alert raised early") + assert(!Alert.first.cleared?, "Auto-clearing alert cleared early") + assert(!Alert.first.acknowledged?, "Alert acknowledged when I didn't expect it") + + # We asked for it to be raised in 5 minutes, so no alert yet... + # + assert_no_notification + + # Push forward to when the alert should be raised, check it has been + # + Time.advance(5.minutes) + assert(Alert.first.raised?, "#{Alert.first.inspect} should be raised by now") + assert(!Alert.first.cleared?, "#{Alert.first.inspect} should not be cleared") + + # Check that we have a notification + # + with_next_notification do |destination, this_alert, other_alerts| + assert_equal("12345@joe_bloggs.email", destination) + assert_equal(Alert.first, this_alert) + assert_equal('raised', this_alert.update_type) + end + + # Simulate manual acknowledgement + # + Alert.first.acknowledge!(Configuration.current.people["joe_bloggs"]) + Timers.restart_and_then_wait_until_idle + assert(Alert.first.acknowledged?, "Acknowledgement didn't work") + + # Check that the acknowledgement has caused a notification + # + with_next_notification do |destination, this_alert, other_alerts| + assert_equal("12345@joe_bloggs.email", destination) + assert_equal(Alert.first, this_alert) + assert_equal('acknowledged', this_alert.update_type, this_alert.inspect) + end + assert(Alert.first.acknowledged?) + assert(Alert.first.raised?) + assert(!Alert.first.cleared?) + + # Now with the config set to un-acknowledge alerts after only 1 minute, + # try winding time on and check that this happens. + # + Time.advance(2.minutes) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal("12345@joe_bloggs.email", destination) + assert_equal(Alert.first, this_alert) + assert_equal('raised', this_alert.update_type, this_alert.inspect) + end + + # Check that auto-clearing works four minutes later + # + Time.advance(5.minutes) + assert(Alert.first.cleared?) + assert(!Alert.first.raised?) + + # Finally check for a notification that auto-clearing has happened + # + with_next_notification do |destination, this_alert, other_alerts| + assert_equal("12345@joe_bloggs.email", destination) + assert_equal(Alert.first, this_alert) + assert_equal('cleared', this_alert.update_type, this_alert.inspect) + end + + # And see that no further reminders are sent a while later + Time.advance(1.day) + assert_no_notification + end + + def test_one_alert_changes_from_outside + # Raise our test alert, wait for it to be processed + mauvesend("-o my_source -i alert1 -s \"alert1 summary\" -d \"alert1 detail\" -u \"alert1 subject\"") + + # Check internal representation, external notification + # + assert(Alert.first.raised?) + assert(!Alert.first.cleared?) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('raised', this_alert.update_type, this_alert.inspect) + end + + # Check we get reminders every hour, and no more + # + 12.times do + Time.advance(1.hour) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('raised', this_alert.update_type, this_alert.inspect) + end + assert_no_notification + end + + # Clear the alert, wait for it to be processed + mauvesend("-o my_source -i alert1 -c now") + assert(!Alert.first.raised?) + assert(Alert.first.cleared?) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('cleared', this_alert.update_type, this_alert.inspect) + end + + # Check we can raise the same alert again + Time.advance(1.minute) + mauvesend("-o my_source -i alert1 -s \"alert1 summary\" -d \"alert1 detail\" -u \"alert1 subject\" -r now") + assert(Alert.first.raised?, Alert.first.inspect) + assert(!Alert.first.cleared?, Alert.first.inspect) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('raised', this_alert.update_type, this_alert.inspect) + end + end + + def test_alert_groups + # check that this alert is reminded more often than normal + mauvesend("-o rare-and-important -i alert1 -s \"rare and important alert\"") + assert(Alert.first.raised?) + assert(!Alert.first.cleared?) + + 10.times do + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('raised', this_alert.update_type, this_alert.inspect) + assert_equal('12345', destination) + Time.advance(10.minutes) + end + end + discard_next_notification + end + + def test_future_raising + mauvesend("-i heartbeat -c now -r +10m -s \"raise in the future\"") + assert(!Alert.first.raised?) + assert(Alert.first.cleared?) + assert_no_notification + + # Check the future alert goes off + # + Time.advance(10.minutes) + assert(Alert.first.raised?) + assert(!Alert.first.cleared?) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('raised', this_alert.update_type, this_alert.inspect) + end + + # Check that a repeat of the "heartbeat" update clears it, and we get + # a notification. + # + mauvesend("-i heartbeat -c now -r +10m -s \"raise in the future\"") + assert(!Alert.first.raised?) + assert(Alert.first.cleared?) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('cleared', this_alert.update_type, this_alert.inspect) + end + + # Check that a re-send of the same clear alert doesn't send another + # notification + # + Time.advance(1.minute) + mauvesend("-i heartbeat -c now -r +10m -s \"raise in the future\"") + assert(!Alert.first.raised?) + assert(Alert.first.cleared?) + assert_no_notification + + # Check that a skewed resend doesn't confuse it + # + mauvesend("-i heartbeat -c +1m -r +11m -s \"raise in the future\"") + assert(!Alert.first.raised?) + assert(Alert.first.cleared?) + Time.advance(1.minute) + assert(!Alert.first.raised?) + assert(Alert.first.cleared?) + assert_no_notification + end + + # Make sure that using the "replace all flag" works as expected. + # + def test_replace_flag + mauvesend("-p") + #mauvesend("-p") + assert_no_notification + + mauvesend("-i test1 -s\"\test1\"") + assert(Alert.first.raised?) + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('raised', this_alert.update_type, this_alert.inspect) + end + assert_no_notification + + mauvesend("-p") + #mauvesend("-p") + with_next_notification do |destination, this_alert, other_alerts| + assert_equal('cleared', this_alert.update_type, this_alert.inspect) + end + assert_no_notification + end + + def test_earliest_date + alert = Alert.create!( + :alert_id => "test_id", + :source => "test1", + :subject => "test subject", + :summary => "test summary", + :raised_at => nil, + :will_raise_at => Time.now + 60, + :will_clear_at => Time.now + 120, + :update_type => "cleared", + :updated_at => Time.now + ) + assert(alert) + + assert(AlertEarliestDate.first.alert == alert) + end + +end + + + + |