Add alert tests

This commit is contained in:
IamTheFij 2019-10-03 16:30:49 -07:00
parent 68ee048b76
commit 71574dd8a9
5 changed files with 156 additions and 25 deletions

View File

@ -2,6 +2,7 @@ package main
import (
"bytes"
"fmt"
"log"
"os/exec"
"text/template"
@ -36,7 +37,7 @@ func (alert Alert) IsValid() bool {
}
// BuildTemplates compiles command templates for the Alert
func (alert *Alert) BuildTemplates() {
func (alert *Alert) BuildTemplates() error {
if alert.commandTemplate == nil && alert.Command != nil {
// build template
log.Println("Building template for command...")
@ -52,46 +53,50 @@ func (alert *Alert) BuildTemplates() {
alert.commandShellTemplate = template.Must(
template.New(alert.Name).Parse(alert.CommandShell),
)
log.Printf("Template built: %v", alert.commandShellTemplate)
} else {
log.Fatalf("No template provided for alert %s", alert.Name)
return fmt.Errorf("No template provided for alert %s", alert.Name)
}
return nil
}
// Send will send an alert notice by executing the command template
func (alert Alert) Send(notice AlertNotice) {
func (alert Alert) Send(notice AlertNotice) (output_str string, err error) {
var cmd *exec.Cmd
if alert.commandTemplate != nil {
// build template
log.Println("Send command thing...")
command := []string{}
for _, cmdTmp := range alert.commandTemplate {
var commandBuffer bytes.Buffer
err := cmdTmp.Execute(&commandBuffer, notice)
err = cmdTmp.Execute(&commandBuffer, notice)
if err != nil {
panic(err)
return
}
command = append(command, commandBuffer.String())
}
cmd = exec.Command(command[0], command[1:]...)
} else if alert.commandShellTemplate != nil {
var commandBuffer bytes.Buffer
err := alert.commandShellTemplate.Execute(&commandBuffer, notice)
err = alert.commandShellTemplate.Execute(&commandBuffer, notice)
if err != nil {
panic(err)
return
}
shellCommand := commandBuffer.String()
log.Printf("About to run alert command: %s", shellCommand)
cmd = ShellCommand(shellCommand)
} else {
panic("No template compiled?")
err = fmt.Errorf("No templates compiled for alert %v", alert.Name)
return
}
output, err := cmd.CombinedOutput()
log.Printf("Check %s\n---\n%s\n---", alert.Name, string(output))
if err != nil {
panic(err)
// Exit if we're not ready to run the command
if cmd == nil || err != nil {
return
}
var output []byte
output, err = cmd.CombinedOutput()
output_str = string(output)
log.Printf("Check %s\n---\n%s\n---", alert.Name, output_str)
return output_str, err
}

121
alert_test.go Normal file
View File

@ -0,0 +1,121 @@
package main
import (
"log"
"testing"
)
func TestAlertIsValid(t *testing.T) {
cases := []struct {
alert Alert
expected bool
name string
}{
{Alert{Command: []string{"echo", "test"}}, true, "Command only"},
{Alert{CommandShell: "echo test"}, true, "CommandShell only"},
{Alert{}, false, "No commands"},
{
Alert{Command: []string{"echo", "test"}, CommandShell: "echo test"},
false,
"Both commands",
},
}
for _, c := range cases {
log.Printf("Testing case %s", c.name)
actual := c.alert.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("-----")
}
}
func TestAlertSend(t *testing.T) {
cases := []struct {
alert Alert
notice AlertNotice
expectedOutput string
expectErr bool
name string
}{
{
Alert{Command: []string{"echo", "{{.MonitorName}}"}},
AlertNotice{MonitorName: "test"},
"test\n",
false,
"Command with template",
},
{
Alert{CommandShell: "echo {{.MonitorName}}"},
AlertNotice{MonitorName: "test"},
"test\n",
false,
"Command shell with template",
},
{
Alert{Command: []string{"echo", "{{.Bad}}"}},
AlertNotice{MonitorName: "test"},
"",
true,
"Command with bad template",
},
{
Alert{CommandShell: "echo {{.Bad}}"},
AlertNotice{MonitorName: "test"},
"",
true,
"Command shell with bad template",
},
}
for _, c := range cases {
log.Printf("Testing case %s", c.name)
c.alert.BuildTemplates()
output, err := c.alert.Send(c.notice)
hasErr := (err != nil)
if output != c.expectedOutput {
t.Errorf("Send(%v output), expected=%v actual=%v", c.name, c.expectedOutput, output)
log.Printf("Case failed: %s", c.name)
}
if hasErr != c.expectErr {
t.Errorf("Send(%v err), expected=%v actual=%v", c.name, "Err", err)
log.Printf("Case failed: %s", c.name)
}
log.Println("-----")
}
}
func TestAlertSendNoTemplates(t *testing.T) {
alert := Alert{}
notice := AlertNotice{}
output, err := alert.Send(notice)
if err == nil {
t.Errorf("Send(no template), expected=%v actual=%v", "Err", output)
}
log.Println("-----")
}
func TestAlertBuildTemplate(t *testing.T) {
cases := []struct {
alert Alert
expectErr bool
name string
}{
{Alert{Command: []string{"echo", "test"}}, false, "Command only"},
{Alert{CommandShell: "echo test"}, false, "CommandShell only"},
{Alert{}, true, "No commands"},
}
for _, c := range cases {
log.Printf("Testing case %s", c.name)
err := c.alert.BuildTemplates()
hasErr := (err != nil)
if hasErr != c.expectErr {
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expectErr, err)
log.Printf("Case failed: %s", c.name)
}
log.Println("-----")
}
}

View File

@ -38,7 +38,9 @@ func (config Config) IsValid() (isValid bool) {
func (config *Config) Init() {
for name, alert := range config.Alerts {
alert.Name = name
alert.BuildTemplates()
if err := alert.BuildTemplates(); err != nil {
panic(err)
}
}
}

View File

@ -29,7 +29,10 @@ func main() {
}
for _, alertName := range alerts {
if alert, ok := config.Alerts[alertName]; ok {
alert.Send(*alertNotice)
_, err := alert.Send(*alertNotice)
if err != nil {
panic(err)
}
} else {
log.Printf("WARNING: Could not find alert for %s", alertName)
}

View File

@ -146,13 +146,13 @@ func TestMonitorFailureAlertEvery(t *testing.T) {
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.
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!
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