You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
10 KiB
314 lines
10 KiB
package main |
|
|
|
import ( |
|
"log" |
|
"testing" |
|
"time" |
|
) |
|
|
|
// TestMonitorIsValid tests the Monitor.IsValid() |
|
func TestMonitorIsValid(t *testing.T) { |
|
cases := []struct { |
|
monitor Monitor |
|
expected bool |
|
name string |
|
}{ |
|
{Monitor{Command: CommandOrShell{Command: []string{"echo", "test"}}, AlertDown: []string{"log"}}, true, "Command only"}, |
|
{Monitor{Command: CommandOrShell{ShellCommand: "echo test"}, AlertDown: []string{"log"}}, true, "CommandShell only"}, |
|
{Monitor{Command: CommandOrShell{Command: []string{"echo", "test"}}}, false, "No AlertDown"}, |
|
{Monitor{AlertDown: []string{"log"}}, false, "No commands"}, |
|
{Monitor{Command: CommandOrShell{Command: []string{"echo", "test"}}, AlertDown: []string{"log"}, AlertAfter: -1}, false, "Invalid alert threshold, -1"}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
actual := c.monitor.IsValid() |
|
if actual != c.expected { |
|
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expected, actual) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorShouldCheck tests the Monitor.ShouldCheck() |
|
func TestMonitorShouldCheck(t *testing.T) { |
|
timeNow := time.Now() |
|
timeTenSecAgo := time.Now().Add(time.Second * -10) |
|
timeTwentySecAgo := time.Now().Add(time.Second * -20) |
|
|
|
cases := []struct { |
|
monitor Monitor |
|
expected bool |
|
name string |
|
}{ |
|
{Monitor{}, true, "Empty"}, |
|
{Monitor{lastCheck: timeNow, CheckInterval: SecondsOrDuration{time.Second * 15}}, false, "Just checked"}, |
|
{Monitor{lastCheck: timeTenSecAgo, CheckInterval: SecondsOrDuration{time.Second * 15}}, false, "-10s"}, |
|
{Monitor{lastCheck: timeTwentySecAgo, CheckInterval: SecondsOrDuration{time.Second * 15}}, true, "-20s"}, |
|
} |
|
|
|
for _, c := range cases { |
|
actual := c.monitor.ShouldCheck() |
|
if actual != c.expected { |
|
t.Errorf("ShouldCheck(%v), expected=%t actual=%t", c.name, c.expected, actual) |
|
} |
|
} |
|
} |
|
|
|
// TestMonitorIsUp tests the Monitor.IsUp() |
|
func TestMonitorIsUp(t *testing.T) { |
|
cases := []struct { |
|
monitor Monitor |
|
expected bool |
|
name string |
|
}{ |
|
{Monitor{}, true, "Empty"}, |
|
{Monitor{alertCount: 1}, false, "Has alert"}, |
|
{Monitor{alertCount: -1}, false, "Negative alerts"}, |
|
{Monitor{alertCount: 0}, true, "No alerts"}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
actual := c.monitor.IsUp() |
|
if actual != c.expected { |
|
t.Errorf("IsUp(%v), expected=%t actual=%t", c.name, c.expected, actual) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorGetAlertNames tests that proper alert names are returned |
|
func TestMonitorGetAlertNames(t *testing.T) { |
|
cases := []struct { |
|
monitor Monitor |
|
up bool |
|
expected []string |
|
name string |
|
}{ |
|
{Monitor{}, true, nil, "Empty up"}, |
|
{Monitor{}, false, nil, "Empty down"}, |
|
{Monitor{AlertUp: []string{"alert"}}, true, []string{"alert"}, "Return up"}, |
|
{Monitor{AlertDown: []string{"alert"}}, false, []string{"alert"}, "Return down"}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
actual := c.monitor.GetAlertNames(c.up) |
|
if !EqualSliceString(actual, c.expected) { |
|
t.Errorf("GetAlertNames(%v), expected=%v actual=%v", c.name, c.expected, actual) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorSuccess tests the Monitor.success() |
|
func TestMonitorSuccess(t *testing.T) { |
|
cases := []struct { |
|
monitor Monitor |
|
expectNotice bool |
|
name string |
|
}{ |
|
{Monitor{}, false, "Empty"}, |
|
{Monitor{alertCount: 0}, false, "No alerts"}, |
|
{Monitor{alertCount: 1}, true, "Has alert"}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
notice := c.monitor.success() |
|
hasNotice := (notice != nil) |
|
|
|
if hasNotice != c.expectNotice { |
|
t.Errorf("success(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorFailureAlertAfter tests that alerts will not trigger until |
|
// hitting the threshold provided by AlertAfter |
|
func TestMonitorFailureAlertAfter(t *testing.T) { |
|
cases := []struct { |
|
monitor Monitor |
|
expectNotice bool |
|
name string |
|
}{ |
|
{Monitor{AlertAfter: 1}, true, "Empty"}, // Defaults to true because and AlertEvery default to 0 |
|
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 1}, true, "Alert after 1: first failure"}, |
|
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 1}, true, "Alert after 1: second failure"}, |
|
{Monitor{failureCount: 0, AlertAfter: 20, AlertEvery: 1}, false, "Alert after 20: first failure"}, |
|
{Monitor{failureCount: 19, AlertAfter: 20, AlertEvery: 1}, true, "Alert after 20: 20th failure"}, |
|
{Monitor{failureCount: 20, AlertAfter: 20, AlertEvery: 1}, true, "Alert after 20: 21st failure"}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
notice := c.monitor.failure() |
|
hasNotice := (notice != nil) |
|
|
|
if hasNotice != c.expectNotice { |
|
t.Errorf("failure(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorFailureAlertEvery tests that alerts will trigger |
|
// on the expected intervals |
|
func TestMonitorFailureAlertEvery(t *testing.T) { |
|
cases := []struct { |
|
monitor Monitor |
|
expectNotice bool |
|
name string |
|
}{ |
|
/* |
|
TODO: Actually found a bug in original implementation. There is an inconsistency in the way AlertAfter is treated. |
|
For "First alert only" (ie. AlertEvery=0), it is the number of failures to ignore before alerting, so AlertAfter=1 |
|
will ignore the first failure and alert on the second failure |
|
For other intervals (ie. AlertEvery=1), it is essentially indexed on one. Essentially making AlertAfter=1 trigger |
|
on the first failure. |
|
|
|
For usabilty, this should be consistent. Consistent with what though? minitor-py? Or itself? Dun dun duuuunnnnn! |
|
*/ |
|
{Monitor{AlertAfter: 1}, true, "Empty"}, // Defaults to true because AlertAfter and AlertEvery default to 0 |
|
// Alert first time only, after 1 |
|
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 0}, true, "Alert first time only after 1: first failure"}, |
|
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 0}, false, "Alert first time only after 1: second failure"}, |
|
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: 0}, false, "Alert first time only after 1: third failure"}, |
|
// Alert every time, after 1 |
|
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 1}, true, "Alert every time after 1: first failure"}, |
|
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 1}, true, "Alert every time after 1: second failure"}, |
|
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 1}, true, "Alert every time after 1: third failure"}, |
|
// Alert every other time, after 1 |
|
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 2}, true, "Alert every other time after 1: first failure"}, |
|
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 2}, false, "Alert every other time after 1: second failure"}, |
|
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: 2}, true, "Alert every other time after 1: third failure"}, |
|
{Monitor{failureCount: 3, AlertAfter: 1, AlertEvery: 2}, false, "Alert every other time after 1: fourth failure"}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
notice := c.monitor.failure() |
|
hasNotice := (notice != nil) |
|
|
|
if hasNotice != c.expectNotice { |
|
t.Errorf("failure(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorFailureExponential tests that alerts will trigger |
|
// with an exponential backoff after repeated failures |
|
func TestMonitorFailureExponential(t *testing.T) { |
|
cases := []struct { |
|
expectNotice bool |
|
name string |
|
}{ |
|
{true, "Alert exponential after 1: first failure"}, |
|
{true, "Alert exponential after 1: second failure"}, |
|
{false, "Alert exponential after 1: third failure"}, |
|
{true, "Alert exponential after 1: fourth failure"}, |
|
{false, "Alert exponential after 1: fifth failure"}, |
|
{false, "Alert exponential after 1: sixth failure"}, |
|
{false, "Alert exponential after 1: seventh failure"}, |
|
{true, "Alert exponential after 1: eighth failure"}, |
|
} |
|
|
|
// Unlike previous tests, this one requires a static Monitor with repeated |
|
// calls to the failure method |
|
monitor := Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: -1} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
notice := monitor.failure() |
|
hasNotice := (notice != nil) |
|
|
|
if hasNotice != c.expectNotice { |
|
t.Errorf("failure(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
} |
|
|
|
// TestMonitorCheck tests successful and failed commands and shell commands |
|
func TestMonitorCheck(t *testing.T) { |
|
type expected struct { |
|
isSuccess bool |
|
hasNotice bool |
|
lastOutput string |
|
} |
|
|
|
cases := []struct { |
|
monitor Monitor |
|
expect expected |
|
name string |
|
}{ |
|
{ |
|
Monitor{Command: CommandOrShell{Command: []string{"echo", "success"}}}, |
|
expected{isSuccess: true, hasNotice: false, lastOutput: "success\n"}, |
|
"Test successful command", |
|
}, |
|
{ |
|
Monitor{Command: CommandOrShell{ShellCommand: "echo success"}}, |
|
expected{isSuccess: true, hasNotice: false, lastOutput: "success\n"}, |
|
"Test successful command shell", |
|
}, |
|
{ |
|
Monitor{Command: CommandOrShell{Command: []string{"total", "failure"}}}, |
|
expected{isSuccess: false, hasNotice: true, lastOutput: ""}, |
|
"Test failed command", |
|
}, |
|
{ |
|
Monitor{Command: CommandOrShell{ShellCommand: "false"}}, |
|
expected{isSuccess: false, hasNotice: true, lastOutput: ""}, |
|
"Test failed command shell", |
|
}, |
|
} |
|
|
|
for _, c := range cases { |
|
log.Printf("Testing case %s", c.name) |
|
|
|
isSuccess, notice := c.monitor.Check() |
|
if isSuccess != c.expect.isSuccess { |
|
t.Errorf("Check(%v) (success), expected=%t actual=%t", c.name, c.expect.isSuccess, isSuccess) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
hasNotice := (notice != nil) |
|
if hasNotice != c.expect.hasNotice { |
|
t.Errorf("Check(%v) (notice), expected=%t actual=%t", c.name, c.expect.hasNotice, hasNotice) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
lastOutput := c.monitor.lastOutput |
|
if lastOutput != c.expect.lastOutput { |
|
t.Errorf("Check(%v) (output), expected=%v actual=%v", c.name, c.expect.lastOutput, lastOutput) |
|
log.Printf("Case failed: %s", c.name) |
|
} |
|
|
|
log.Println("-----") |
|
} |
|
}
|
|
|