Skip to content

Commit

Permalink
Feat/unittests (#60)
Browse files Browse the repository at this point in the history
- add unit tests
- small code improvements
  • Loading branch information
gi8lino authored Jun 13, 2024
1 parent 3120b9d commit 17125e3
Show file tree
Hide file tree
Showing 31 changed files with 1,589 additions and 37 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ require (
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/prometheus/client_golang v1.19.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.11.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -26,8 +28,8 @@ require (
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -55,6 +57,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"os"
)

const version = "0.6.8"
const version = "0.6.7"

//go:embed web
var templates embed.FS
Expand Down
118 changes: 118 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package config

import (
"heartbeats/pkg/heartbeat"
"heartbeats/pkg/history"
"heartbeats/pkg/notify"
"heartbeats/pkg/notify/notifier"
"os"
"testing"

"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)

// Sample configuration YAML for testing.
const sampleConfig = `
version: "1.0.0"
verbose: true
path: "./config.yaml"
server:
siteRoot: "http://localhost:8080"
listenAddress: "localhost:8080"
cache:
maxSize: 100
reduce: 10
notifications:
slack:
type: "slack"
slack_config:
channel: "general"
heartbeats:
heartbeat1:
name: "heartbeat1"
interval: "1m"
grace: "1m"
notifications:
- slack
`

func writeSampleConfig(t *testing.T, content string) string {
file, err := os.CreateTemp("", "config*.yaml")
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
defer file.Close()

if _, err := file.WriteString(content); err != nil {
t.Fatalf("Failed to write to temp file: %v", err)
}

return file.Name()
}

func TestConfig_Read(t *testing.T) {
App.NotificationStore = notify.NewStore()
HistoryStore = history.NewStore()

tempFile := writeSampleConfig(t, sampleConfig)
defer os.Remove(tempFile)

App.Path = tempFile

err := App.Read()
assert.NoError(t, err, "Expected no error when reading the config file")

notification := App.NotificationStore.Get("slack")
assert.NotNil(t, notification, "Expected slack notification to be present")
assert.Equal(t, `{{ if eq .Status "ok" }}good{{ else }}danger{{ end }}`, notification.SlackConfig.ColorTemplate)

heartbeat := App.HeartbeatStore.Get("heartbeat1")
assert.NotNil(t, heartbeat, "Expected heartbeat1 to be present")
assert.Equal(t, "heartbeat1", heartbeat.Name)
}

func TestProcessNotifications(t *testing.T) {
App.NotificationStore = notify.NewStore()
HistoryStore = history.NewStore()

var rawConfig map[string]interface{}
err := yaml.Unmarshal([]byte(sampleConfig), &rawConfig)
assert.NoError(t, err)
err = App.processNotifications(rawConfig["notifications"])
assert.NoError(t, err, "Expected no error when processing notifications")

notification := App.NotificationStore.Get("slack")
assert.NotNil(t, notification, "Expected slack notification to be present")
assert.Equal(t, "slack", notification.Type)
assert.Equal(t, `{{ if eq .Status "ok" }}good{{ else }}danger{{ end }}`, notification.SlackConfig.ColorTemplate)
}

func TestProcessHeartbeats(t *testing.T) {
App.HeartbeatStore = heartbeat.NewStore()
HistoryStore = history.NewStore()

var rawConfig map[string]interface{}
err := yaml.Unmarshal([]byte(sampleConfig), &rawConfig)
assert.NoError(t, err)

err = App.processHeartbeats(rawConfig["heartbeats"])
assert.NoError(t, err, "Expected no error when processing heartbeats")

heartbeat := App.HeartbeatStore.Get("heartbeat1")
assert.NotNil(t, heartbeat, "Expected heartbeat1 to be present")
assert.Equal(t, "heartbeat1", heartbeat.Name)
}

func TestUpdateSlackNotification(t *testing.T) {
notification := &notify.Notification{
Type: "slack",
SlackConfig: &notifier.SlackConfig{
Channel: "general",
},
}

err := App.updateSlackNotification("slack", notification)
assert.NoError(t, err, "Expected no error when updating slack notification")
assert.Equal(t, `{{ if eq .Status "ok" }}good{{ else }}danger{{ end }}`, notification.SlackConfig.ColorTemplate)
}
86 changes: 86 additions & 0 deletions pkg/flags/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package flags

import (
"heartbeats/pkg/config"
"os"
"strings"
"testing"

"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
)

func resetFlags() {
pflag.CommandLine = pflag.NewFlagSet("heartbeats", pflag.ExitOnError)
}

func TestParseFlags(t *testing.T) {
resetFlags()

output := &strings.Builder{}
args := []string{"cmd", "-c", "config.yaml", "-l", "127.0.0.1:9090", "-s", "http://example.com", "-m", "200", "-r", "20", "-v"}

result := ParseFlags(args, output)
assert.NoError(t, result.Err)
assert.Equal(t, result.ShowVersion, false)
assert.Equal(t, result.ShowHelp, false)

assert.Equal(t, "config.yaml", config.App.Path)
assert.Equal(t, "127.0.0.1:9090", config.App.Server.ListenAddress)
assert.Equal(t, "http://example.com", config.App.Server.SiteRoot)
assert.Equal(t, 200, config.App.Cache.MaxSize)
assert.Equal(t, 20, config.App.Cache.Reduce)
assert.True(t, config.App.Verbose)
}

func TestShowVersionFlag(t *testing.T) {
resetFlags()

output := &strings.Builder{}
args := []string{"cmd", "--version"}
result := ParseFlags(args, output)
assert.NoError(t, result.Err)
assert.Equal(t, result.ShowVersion, true)
assert.Equal(t, result.ShowHelp, false)
}

func TestShowHelpFlag(t *testing.T) {
resetFlags()

output := &strings.Builder{}
args := []string{"cmd", "--help"}

result := ParseFlags(args, output)
assert.NoError(t, result.Err)
assert.Equal(t, result.ShowVersion, false)
assert.Equal(t, result.ShowHelp, true)
}

func TestProcessEnvVariables(t *testing.T) {
resetFlags()

os.Setenv("HEARTBEATS_CONFIG", "env_config.yaml")
os.Setenv("HEARTBEATS_LISTEN_ADDRESS", "0.0.0.0:8080")
os.Setenv("HEARTBEATS_SITE_ROOT", "http://env.com")
os.Setenv("HEARTBEATS_MAX_SIZE", "300")
os.Setenv("HEARTBEATS_REDUCE", "30")
os.Setenv("HEARTBEATS_VERBOSE", "true")

pflag.StringVarP(&config.App.Path, "config", "c", "./deploy/config.yaml", "Path to the configuration file")
pflag.StringVarP(&config.App.Server.ListenAddress, "listen-address", "l", "localhost:8080", "Address to listen on")
pflag.StringVarP(&config.App.Server.SiteRoot, "site-root", "s", "", "Site root for the heartbeat service (default \"http://<listenAddress>\")")
pflag.IntVarP(&config.App.Cache.MaxSize, "max-size", "m", 100, "Maximum size of the cache")
pflag.IntVarP(&config.App.Cache.Reduce, "reduce", "r", 10, "Amount to reduce when max size is exceeded")
pflag.BoolVarP(&config.App.Verbose, "verbose", "v", false, "Enable verbose logging")

pflag.Parse()

processEnvVariables()

assert.Equal(t, "env_config.yaml", config.App.Path)
assert.Equal(t, "0.0.0.0:8080", config.App.Server.ListenAddress)
assert.Equal(t, "http://env.com", config.App.Server.SiteRoot)
assert.Equal(t, 300, config.App.Cache.MaxSize)
assert.Equal(t, 30, config.App.Cache.Reduce)
assert.True(t, config.App.Verbose)
}
23 changes: 23 additions & 0 deletions pkg/handlers/healthz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package handlers

import (
"heartbeats/pkg/logger"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
)

func TestHealthz(t *testing.T) {
log := logger.NewLogger(true)
handler := Healthz(log)

req := httptest.NewRequest("GET", "/healthz", nil)
rec := httptest.NewRecorder()

handler.ServeHTTP(rec, req)

assert.Equal(t, http.StatusOK, rec.Code, "Expected status code 200")
assert.Equal(t, "ok", rec.Body.String(), "Expected response body 'ok'")
}
6 changes: 3 additions & 3 deletions pkg/handlers/heartbeats.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package handlers

import (
"embed"
"heartbeats/pkg/config"
"heartbeats/pkg/logger"
"heartbeats/pkg/timer"
"html/template"
"io/fs"
"net/http"
"time"

Expand Down Expand Up @@ -37,7 +37,7 @@ type NotificationState struct {
}

// Heartbeats handles the / endpoint
func Heartbeats(logger logger.Logger, staticFS embed.FS) http.HandlerFunc {
func Heartbeats(logger logger.Logger, staticFS fs.FS) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmap := sprig.TxtFuncMap()
fmap["isTrue"] = isTrue
Expand All @@ -48,7 +48,7 @@ func Heartbeats(logger logger.Logger, staticFS embed.FS) http.HandlerFunc {
Funcs(fmap).
ParseFS(
staticFS,
"web/templates/heartbeat.html",
"web/templates/heartbeats.html",
"web/templates/footer.html",
)
if err != nil {
Expand Down
Loading

0 comments on commit 17125e3

Please sign in to comment.