cyberark/secretless-broker

View on GitHub
internal/util/health_test.go

Summary

Maintainability
A
3 hrs
Test Coverage
package util

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strconv"
    "testing"
    "time"

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

var listenerPort = strconv.Itoa(defaultHealthCheckPort)

var ReadyEndpoint = "http://localhost:" + listenerPort + "/ready"
var LiveEndpoint = "http://localhost:" + listenerPort + "/live"

var VerboseReadyEndpoint = "http://localhost:" + listenerPort + "/ready?full=1"
var VerboseLiveEndpoint = "http://localhost:" + listenerPort + "/live?full=1"

type HealthJSON struct {
    Ready     string `json:"ready,omitempty"`
    Listening string `json:"listening,omitempty"`
}

func getHealth(endpoint string) (*HealthJSON, error) {
    webClient := http.Client{
        Timeout: time.Second * 2,
    }

    request, requestErr := http.NewRequest(http.MethodGet, endpoint, nil)
    if requestErr != nil {
        return nil, requestErr
    }

    response, responseErr := webClient.Do(request)
    if responseErr != nil {
        return nil, responseErr
    }

    body, readErr := ioutil.ReadAll(response.Body)
    if readErr != nil {
        return nil, readErr
    }

    healthJSON := &HealthJSON{}
    unmarshalErr := json.Unmarshal(body, healthJSON)
    if unmarshalErr != nil {
        return nil, unmarshalErr
    }

    if response.StatusCode < 200 || response.StatusCode > 299 {
        err := fmt.Errorf("response Status: %d: %s",
            response.StatusCode,
            http.StatusText(response.StatusCode))
        return healthJSON, err
    }

    fmt.Println(healthJSON)

    return healthJSON, nil
}

func assertHealthStatusCodeIsBad(endpoint string, t *testing.T) {
    _, err := getHealth(endpoint)

    assert.Error(t, err)
    errorMsg := "response Status: 503: Service Unavailable"
    assert.EqualError(t, err, errorMsg)
}

func assertHealthStatusCodeIsGood(endpoint string, t *testing.T) {
    _, err := getHealth(endpoint)

    assert.NoError(t, err)
}

func assertReadyJSONIsBad(err error, healthJSON *HealthJSON, t *testing.T) {
    assert.Error(t, err)
    errorMsg := "response Status: 503: Service Unavailable"
    assert.EqualError(t, err, errorMsg)

    assert.NotNil(t, healthJSON)

    assert.Equal(t, "secretless is not ready", (*healthJSON).Ready)
}

func assertReadyJSONIsGood(err error, healthJSON *HealthJSON, t *testing.T) {
    assert.NoError(t, err)
    assert.NotNil(t, healthJSON)
    assert.Equal(t, "OK", (*healthJSON).Ready)
}

func assertReadyJSONIsNotPresent(healthJSON *HealthJSON, t *testing.T) {
    assert.NotNil(t, healthJSON)
    assert.Equal(t, "", (*healthJSON).Ready)
}

func assertListeningJSONIsBad(err error, healthJSON *HealthJSON, t *testing.T) {
    assert.Error(t, err)
    errorMsg := "response Status: 503: Service Unavailable"
    assert.EqualError(t, err, errorMsg)

    assert.NotNil(t, healthJSON)

    assert.Equal(t, "secretless is not listening", (*healthJSON).Listening)
}

func assertListeningJSONIsGood(err error, healthJSON *HealthJSON, t *testing.T) {
    assert.NoError(t, err)
    assert.NotNil(t, healthJSON)
    assert.Equal(t, "OK", (*healthJSON).Listening)
}

func callEnableHealthCheck() {
    enableHealthCheck()

    // Server can be slow to come up :(
    time.Sleep(250 * time.Millisecond)
}

func enableAndReadyHealthCheck() {
    callEnableHealthCheck()
    SetAppInitializedFlag()
}

func enableReadyAndLivenHealthCheck() {
    enableAndReadyHealthCheck()
    SetAppIsLive(true)
}

func Test_Health(t *testing.T) {
    t.Run("When nothing is proactively done", func(t *testing.T) {
        t.Run("Shows not ready", func(t *testing.T) {
            callEnableHealthCheck()
            assertHealthStatusCodeIsBad(ReadyEndpoint, t)
        })

        t.Run("Shows not live", func(t *testing.T) {
            callEnableHealthCheck()
            assertHealthStatusCodeIsBad(LiveEndpoint, t)
        })

        t.Run("Shows expected not ready JSON", func(t *testing.T) {
            callEnableHealthCheck()

            health, err := getHealth(VerboseReadyEndpoint)
            assertReadyJSONIsBad(err, health, t)
            assertListeningJSONIsBad(err, health, t)
        })

        t.Run("Shows expected not live JSON", func(t *testing.T) {
            callEnableHealthCheck()

            health, err := getHealth(VerboseLiveEndpoint)
            assertReadyJSONIsNotPresent(health, t)
            assertListeningJSONIsBad(err, health, t)
        })

        t.Cleanup(func() {
            disableHealthCheck()
        })
    })

    t.Run("When readniness flag is turned on but liveliness is not", func(t *testing.T) {
        t.Run("Shows not ready", func(t *testing.T) {
            enableAndReadyHealthCheck()
            assertHealthStatusCodeIsBad(ReadyEndpoint, t)
        })

        t.Run("Shows not live", func(t *testing.T) {
            enableAndReadyHealthCheck()
            assertHealthStatusCodeIsBad(LiveEndpoint, t)
        })

        t.Run("Shows expected ready JSON but bad listening status", func(t *testing.T) {
            enableAndReadyHealthCheck()

            health, err := getHealth(VerboseReadyEndpoint)

            // We ignore error manually since we know it will be checked below
            assertReadyJSONIsGood(nil, health, t)

            assertListeningJSONIsBad(err, health, t)
        })

        t.Run("Shows expected not live JSON", func(t *testing.T) {
            enableAndReadyHealthCheck()

            health, err := getHealth(VerboseLiveEndpoint)
            assertReadyJSONIsNotPresent(health, t)
            assertListeningJSONIsBad(err, health, t)
        })

        t.Cleanup(func() {
            disableHealthCheck()
        })
    })

    t.Run("When app is ready and listening", func(t *testing.T) {
        t.Run("Shows ready", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()
            assertHealthStatusCodeIsGood(ReadyEndpoint, t)
        })

        t.Run("Shows live", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()
            assertHealthStatusCodeIsGood(LiveEndpoint, t)
        })

        t.Run("Shows expected ready JSON", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()

            health, err := getHealth(VerboseReadyEndpoint)

            assertReadyJSONIsGood(err, health, t)
            assertListeningJSONIsGood(err, health, t)
        })

        t.Run("Shows expected live JSON", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()

            health, err := getHealth(VerboseLiveEndpoint)
            assertReadyJSONIsNotPresent(health, t)
            assertListeningJSONIsGood(err, health, t)
        })

        t.Cleanup(func() {
            disableHealthCheck()
        })
    })

    t.Run("When app is ready and listening is dynamically changed", func(t *testing.T) {
        t.Run("Updates ready status", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()
            assertHealthStatusCodeIsGood(ReadyEndpoint, t)

            SetAppIsLive(false)
            assertHealthStatusCodeIsBad(ReadyEndpoint, t)

            SetAppIsLive(true)
            assertHealthStatusCodeIsGood(ReadyEndpoint, t)
        })

        t.Run("Updates live status", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()
            assertHealthStatusCodeIsGood(LiveEndpoint, t)

            SetAppIsLive(false)
            assertHealthStatusCodeIsBad(LiveEndpoint, t)

            SetAppIsLive(true)
            assertHealthStatusCodeIsGood(LiveEndpoint, t)
        })

        t.Run("Updates expected ready JSON", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()

            health, err := getHealth(VerboseReadyEndpoint)
            assertReadyJSONIsGood(err, health, t)
            assertListeningJSONIsGood(err, health, t)

            SetAppIsLive(false)
            health, err = getHealth(VerboseReadyEndpoint)
            assertReadyJSONIsGood(nil, health, t)
            assertListeningJSONIsBad(err, health, t)

            SetAppIsLive(true)
            health, err = getHealth(VerboseReadyEndpoint)
            assertReadyJSONIsGood(err, health, t)
            assertListeningJSONIsGood(err, health, t)
        })

        t.Run("Updates expected live JSON", func(t *testing.T) {
            enableReadyAndLivenHealthCheck()

            health, err := getHealth(VerboseLiveEndpoint)
            assertReadyJSONIsNotPresent(health, t)
            assertListeningJSONIsGood(err, health, t)

            SetAppIsLive(false)
            health, err = getHealth(VerboseLiveEndpoint)
            assertReadyJSONIsNotPresent(health, t)
            assertListeningJSONIsBad(err, health, t)

            SetAppIsLive(true)
            health, err = getHealth(VerboseLiveEndpoint)
            assertReadyJSONIsNotPresent(health, t)
            assertListeningJSONIsGood(err, health, t)
        })

        t.Cleanup(func() {
            disableHealthCheck()
        })
    })
}