package healthtiming

import (
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/jarcoal/httpmock"
	. "github.com/smartystreets/goconvey/convey"

	"code.justin.tv/d8a/pg-healthcheck/stats"
)

func TestGraphiteQuery(t *testing.T) {
	Convey("With New Graphite Query", t, func(c C) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()

		var expectedTarget string
		var graphiteResponseCode int
		var graphiteResponse string
		var graphiteURL = "graphite.internal.justin.tv"
		var cluster = "some-test"
		var environment = "prod"
		var role = "replica"
		var host = "some-host"
		var port = 5432

		var expectedData float64
		var expectedTimestamp = time.Unix(0, 0)
		var expectedError error

		//Graphite only returns the unix timestamp & not the nanoseconds of the time, so strip nanos from the current time
		currentTime := time.Unix(time.Now().Unix(), 0)

		Convey("With default query values", func(c C) {
			expectedTarget = "http://graphite.internal.justin.tv/render?target=divideSeries(movingAverage(stats.timers.pg-healthcheck.some-test-prod.replica.some-host.health.5432.*.total.mean,10),movingAverage(averageSeries(stats.timers.pg-healthcheck.some-test-prod.replica.*.health.*.*.total.mean),10))&format=json&from=-10s"

			Convey("Receive a successful graphite response", func(c C) {
				graphiteResponseCode = 200
				graphiteResponse = fmt.Sprintf(`[{"target": "divideSeries(movingAverage(stats.timers.pg-healthcheck.some-test-prod.replica.some-host.health.5432.*.total.mean,10),movingAverage(averageSeries(stats.timers.pg-healthcheck.some-test-prod.replica.*.health.*.*.total.mean),10))",
            "datapoints": [[1.5,%d]]}]`, currentTime.Unix())
				expectedData = 1.5
				expectedTimestamp = currentTime
			})

			Convey("Receive a 500 response from graphite raises error", func(c C) {
				graphiteResponseCode = 500
				graphiteResponse = "A serious error"
				expectedError = fmt.Errorf("Received 500 response when accessing %s", expectedTarget)
			})

			Convey("Graphite response has no results raises error", func(c C) {
				graphiteResponseCode = 200
				graphiteResponse = "[]"
				expectedError = errors.New("No graphite metrics returned from the json API.")
			})
		})

		Convey("With some other query values", func(c C) {
			graphiteURL = "google.com"
			cluster = "garbage-land"
			environment = "test"
			role = "master"
			host = "garbage-host"
			port = 12006

			expectedTarget = "http://google.com/render?target=divideSeries(movingAverage(stats.timers.pg-healthcheck.garbage-land-test.master.garbage-host.health.12006.*.total.mean,10),movingAverage(averageSeries(stats.timers.pg-healthcheck.garbage-land-test.master.*.health.*.*.total.mean),10))&format=json&from=-10s"

			Convey("Receive a successful graphite response", func(c C) {
				graphiteResponseCode = 200
				graphiteResponse = fmt.Sprintf(`[{"target": "divideSeries(movingAverage(stats.timers.pg-healthcheck.some-test-prod.replica.some-host.health.5432.*.total.mean,10),movingAverage(averageSeries(stats.timers.pg-healthcheck.some-test-prod.replica.*.health.*.*.total.mean),10))",
            "datapoints": [[1.5,%d]]}]`, currentTime.Unix())
				expectedData = 1.5
				expectedTimestamp = currentTime
			})
		})

		Convey("With an empty role, receive an error", func(c C) {
			role = ""
			expectedTarget = ""
			expectedError = errors.New("No role data available.")
		})

		if expectedTarget != "" {
			httpmock.RegisterResponder("GET", expectedTarget, httpmock.NewStringResponder(graphiteResponseCode, graphiteResponse))
		}
		query := NewGraphiteQuery(graphiteURL, cluster, environment, stats.NewRoleContainer(role), host, port)

		actualData, actualTimestamp, actualError := query.Execute()
		So(actualError, ShouldResemble, expectedError)
		So(actualData, ShouldResemble, expectedData)
		So(actualTimestamp, ShouldResemble, expectedTimestamp)
	})
}
