package redfish

import (
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/config"
	"github.com/influxdata/telegraf/metric"
	"github.com/influxdata/telegraf/testutil"
)

func TestDellApis(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		switch r.URL.Path {
		case "/redfish/v1/":
			http.ServeFile(w, r, "testdata/base.json")
		case "/redfish/v1/Systems/":
			http.ServeFile(w, r, "testdata/dell/dell_available_systems.json")
		case "/redfish/v1/Chassis/System.Embedded.1/Thermal":
			http.ServeFile(w, r, "testdata/dell/dell_thermal.json")
		case "/redfish/v1/Chassis/System.Embedded.1/Power":
			http.ServeFile(w, r, "testdata/dell/dell_power.json")
		case "/redfish/v1/Chassis/System.Embedded.1":
			http.ServeFile(w, r, "testdata/dell/dell_chassis.json")
		case "/redfish/v1/Systems/System.Embedded.1":
			http.ServeFile(w, r, "testdata/dell/dell_systems.json")
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}))

	defer ts.Close()

	u, err := url.Parse(ts.URL)
	require.NoError(t, err)
	address, _, err := net.SplitHostPort(u.Host)
	require.NoError(t, err)

	expectedMetrics := []telegraf.Metric{
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":      "CPU1 Temp",
				"member_id": "iDRAC.Embedded.1#CPU1Temp",
				"source":    "tpa-hostname",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 3.0,
				"lower_threshold_fatal":    3.0,
				"reading_celsius":          40.0,
				"upper_threshold_critical": 93.0,
				"upper_threshold_fatal":    93.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan1A",
				"member_id": "0x17||Fan.Embedded.1A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_rpm":              17760,
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan1B",
				"member_id": "0x17||Fan.Embedded.1B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15360,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan2A",
				"member_id": "0x17||Fan.Embedded.2A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              17880,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan2B",
				"member_id": "0x17||Fan.Embedded.2B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15120,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan3A",
				"member_id": "0x17||Fan.Embedded.3A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              18000,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan3B",
				"member_id": "0x17||Fan.Embedded.3B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15600,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan4A",
				"member_id": "0x17||Fan.Embedded.4A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              17280,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan4B",
				"member_id": "0x17||Fan.Embedded.4B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15360,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan5A",
				"member_id": "0x17||Fan.Embedded.5A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              17640,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan5B",
				"member_id": "0x17||Fan.Embedded.5B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15600,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan6A",
				"member_id": "0x17||Fan.Embedded.6A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              17760,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan6B",
				"member_id": "0x17||Fan.Embedded.6B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15600,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan7A",
				"member_id": "0x17||Fan.Embedded.7A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              17400,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan7B",
				"member_id": "0x17||Fan.Embedded.7B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15720,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan8A",
				"member_id": "0x17||Fan.Embedded.8A",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              18000,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board Fan8B",
				"member_id": "0x17||Fan.Embedded.8B",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"lower_threshold_critical": 600,
				"lower_threshold_fatal":    600,
				"reading_rpm":              15840,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powercontrol",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Power Control",
				"member_id": "PowerControl",
				"address":   address,
			},
			map[string]interface{}{
				"average_consumed_watts": 426.0,
				"interval_in_min":        int64(1),
				"max_consumed_watts":     436.0,
				"min_consumed_watts":     425.0,
				"power_allocated_watts":  1628.0,
				"power_available_watts":  0.0,
				"power_capacity_watts":   1628.0,
				"power_consumed_watts":   429.0,
				"power_requested_watts":  704.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powersupplies",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "PS1 Status",
				"member_id": "PSU.Slot.1",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"power_capacity_watts": 750.00,
				"power_input_watts":    900.0,
				"power_output_watts":   203.0,
				"line_input_voltage":   206.00,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_voltages",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board DIMM PG",
				"member_id": "iDRAC.Embedded.1#SystemBoardDIMMPG",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_volts": 1.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_voltages",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board NDC PG",
				"member_id": "iDRAC.Embedded.1#SystemBoardNDCPG",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_volts": 1.0,
			},
			time.Unix(0, 0),
		),

		metric.New(
			"redfish_power_voltages",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "System Board PS1 PG FAIL",
				"member_id": "iDRAC.Embedded.1#SystemBoardPS1PGFAIL",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_volts": 1.0,
			},
			time.Unix(0, 0),
		),
	}
	plugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "System.Embedded.1",
		IncludeMetrics:   []string{"thermal", "power"},
	}
	require.NoError(t, plugin.Init())
	var acc testutil.Accumulator

	err = plugin.Gather(&acc)
	require.NoError(t, err)
	require.True(t, acc.HasMeasurement("redfish_thermal_temperatures"))
	testutil.RequireMetricsEqual(t, expectedMetrics, acc.GetTelegrafMetrics(),
		testutil.IgnoreTime())
}

func TestHPApis(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		switch r.URL.Path {
		case "/redfish/v1/":
			http.ServeFile(w, r, "testdata/base.json")
		case "/redfish/v1/Systems/":
			http.ServeFile(w, r, "testdata/hp/hp_available_systems.json")
		case "/redfish/v1/Chassis/1/Thermal":
			http.ServeFile(w, r, "testdata/hp/hp_thermal.json")
		case "/redfish/v1/Chassis/1/Power":
			http.ServeFile(w, r, "testdata/hp/hp_power.json")
		case "/redfish/v1/Systems/1":
			http.ServeFile(w, r, "testdata/hp/hp_systems.json")
		case "/redfish/v1/Chassis/1/":
			http.ServeFile(w, r, "testdata/hp/hp_chassis.json")
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}))

	defer ts.Close()

	u, err := url.Parse(ts.URL)
	require.NoError(t, err)
	address, _, err := net.SplitHostPort(u.Host)
	require.NoError(t, err)

	expectedMetricsHp := []telegraf.Metric{
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":      "01-Inlet Ambient",
				"member_id": "0",
				"source":    "tpa-hostname",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_celsius":          19.0,
				"upper_threshold_critical": 42.0,
				"upper_threshold_fatal":    47.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":      "44-P/S 2 Zone",
				"source":    "tpa-hostname",
				"member_id": "42",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_celsius":          34.0,
				"upper_threshold_critical": 75.0,
				"upper_threshold_fatal":    80.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "Fan 1",
				"member_id": "0",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_percent": 23,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "Fan 2",
				"member_id": "1",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_percent": 23,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "Fan 3",
				"member_id": "2",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_percent": 23,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powercontrol",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "",
				"member_id": "0",
				"address":   address,
			},
			map[string]interface{}{
				"average_consumed_watts": 221.0,
				"interval_in_min":        int64(20),
				"max_consumed_watts":     252.0,
				"min_consumed_watts":     220.0,
				"power_capacity_watts":   1600.0,
				"power_consumed_watts":   221.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powersupplies",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "HpeServerPowerSupply",
				"member_id": "0",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"power_capacity_watts":    800.0,
				"line_input_voltage":      205.0,
				"last_power_output_watts": 0.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powersupplies",
			map[string]string{
				"source":    "tpa-hostname",
				"name":      "HpeServerPowerSupply",
				"member_id": "1",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"power_capacity_watts":    800.0,
				"line_input_voltage":      205.0,
				"last_power_output_watts": 90.0,
			},
			time.Unix(0, 0),
		),
	}

	hpPlugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "1",
		IncludeMetrics:   []string{"thermal", "power"},
	}
	require.NoError(t, hpPlugin.Init())
	var hpAcc testutil.Accumulator

	err = hpPlugin.Gather(&hpAcc)
	require.NoError(t, err)
	require.True(t, hpAcc.HasMeasurement("redfish_thermal_temperatures"))
	testutil.RequireMetricsEqual(t, expectedMetricsHp, hpAcc.GetTelegrafMetrics(),
		testutil.IgnoreTime())
}

func TestHPilo4Apis(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		switch r.URL.Path {
		case "/redfish/v1/":
			http.ServeFile(w, r, "testdata/base.json")
		case "/redfish/v1/Systems/":
			http.ServeFile(w, r, "testdata/hp/hp_available_systems.json")
		case "/redfish/v1/Chassis/1/Thermal":
			http.ServeFile(w, r, "testdata/hp/hp_thermal_ilo4.json")
		case "/redfish/v1/Chassis/1/Power":
			http.ServeFile(w, r, "testdata/hp/hp_power.json")
		case "/redfish/v1/Systems/1":
			http.ServeFile(w, r, "testdata/hp/hp_systems.json")
		case "/redfish/v1/Chassis/1/":
			http.ServeFile(w, r, "testdata/hp/hp_chassis.json")
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}))

	defer ts.Close()

	u, err := url.Parse(ts.URL)
	require.NoError(t, err)
	address, _, err := net.SplitHostPort(u.Host)
	require.NoError(t, err)

	expectedMetricsHp := []telegraf.Metric{
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":      "01-Inlet Ambient",
				"member_id": "0",
				"source":    "tpa-hostname",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_celsius":          19.0,
				"upper_threshold_critical": 42.0,
				"upper_threshold_fatal":    47.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":      "44-P/S 2 Zone",
				"member_id": "42",
				"source":    "tpa-hostname",
				"address":   address,
				"health":    "OK",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_celsius":          34.0,
				"upper_threshold_critical": 75.0,
				"upper_threshold_fatal":    80.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"address":   address,
				"health":    "OK",
				"member_id": "",
				"name":      "Fan 1",
				"source":    "tpa-hostname",
				"state":     "Enabled",
			},
			map[string]interface{}{
				"reading_percent": 17,
			},
			time.Unix(0, 0),
		),
	}

	hpPlugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "1",
		IncludeMetrics:   []string{"thermal"},
	}
	require.NoError(t, hpPlugin.Init())
	var hpAcc testutil.Accumulator

	err = hpPlugin.Gather(&hpAcc)
	require.NoError(t, err)
	require.True(t, hpAcc.HasMeasurement("redfish_thermal_temperatures"))
	testutil.RequireMetricsEqual(t, expectedMetricsHp, hpAcc.GetTelegrafMetrics(),
		testutil.IgnoreTime())
}

func checkAuth(r *http.Request, username, password string) bool {
	user, pass, ok := r.BasicAuth()
	if !ok {
		return false
	}
	return user == username && pass == password
}

func TestInvalidUsernameorPassword(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "testing", "testing") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		switch r.URL.Path {
		case "/redfish/v1/":
			http.ServeFile(w, r, "testdata/base.json")
		case "/redfish/v1/Systems/":
			http.ServeFile(w, r, "testdata/dell/dell_available_systems.json")
		case "/redfish/v1/Systems/System.Embedded.1":
			http.ServeFile(w, r, "testdata/dell/dell_systems.json")
		case "/redfish/v1/Chassis/System.Embedded.1/Thermal":
			http.ServeFile(w, r, "testdata/dell/dell_thermal.json")
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}))
	defer ts.Close()

	r := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "System.Embedded.1",
		IncludeMetrics:   []string{"thermal", "power"},
	}

	var acc testutil.Accumulator
	require.NoError(t, r.Init())
	u, err := url.Parse(ts.URL)
	require.NoError(t, err)
	err = r.Gather(&acc)
	require.EqualError(t, err, "received status code 401 (Unauthorized) for address http://"+u.Host+"/redfish/v1/Systems/System.Embedded.1, expected 200")
}
func TestNoUsernameorPasswordConfiguration(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "testing", "testing") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		switch r.URL.Path {
		case "/redfish/v1/":
			http.ServeFile(w, r, "testdata/base.json")
		case "/redfish/v1/Systems/":
			http.ServeFile(w, r, "testdata/dell/dell_available_systems.json")
		case "/redfish/v1/Systems/System.Embedded.1":
			http.ServeFile(w, r, "testdata/dell/dell_systems.json")
		case "/redfish/v1/Chassis/System.Embedded.1/Thermal":
			http.ServeFile(w, r, "testdata/dell/dell_thermal.json")
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}))
	defer ts.Close()

	r := &Redfish{
		Address:          ts.URL,
		ComputerSystemID: "System.Embedded.1",
		IncludeMetrics:   []string{"thermal", "power"},
	}

	err := r.Init()
	require.Error(t, err)
	require.EqualError(t, err, "did not provide username and password")
}

func TestInvalidDellJSON(t *testing.T) {
	tests := []struct {
		name             string
		thermalfilename  string
		powerfilename    string
		chassisfilename  string
		hostnamefilename string
	}{
		{
			name:             "check Thermal",
			thermalfilename:  "testdata/dell/dell_thermalinvalid.json",
			powerfilename:    "testdata/dell/dell_power.json",
			chassisfilename:  "testdata/dell/dell_chassis.json",
			hostnamefilename: "testdata/dell/dell_systems.json",
		},
		{
			name:             "check Power",
			thermalfilename:  "testdata/dell/dell_thermal.json",
			powerfilename:    "testdata/dell/dell_powerinvalid.json",
			chassisfilename:  "testdata/dell/dell_chassis.json",
			hostnamefilename: "testdata/dell/dell_systems.json",
		},
		{
			name:             "check Location",
			thermalfilename:  "testdata/dell/dell_thermal.json",
			powerfilename:    "testdata/dell/dell_power.json",
			chassisfilename:  "testdata/dell/dell_chassisinvalid.json",
			hostnamefilename: "testdata/dell/dell_systems.json",
		},
		{
			name:             "check Hostname",
			thermalfilename:  "testdata/dell/dell_thermal.json",
			powerfilename:    "testdata/dell/dell_power.json",
			chassisfilename:  "testdata/dell/dell_chassis.json",
			hostnamefilename: "testdata/dell/dell_systemsinvalid.json",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				if !checkAuth(r, "test", "test") {
					http.Error(w, "Unauthorized.", http.StatusUnauthorized)
					return
				}

				switch r.URL.Path {
				case "/redfish/v1/":
					http.ServeFile(w, r, "testdata/base.json")
				case "/redfish/v1/Systems/":
					http.ServeFile(w, r, "testdata/dell/dell_available_systems.json")
				case "/redfish/v1/Chassis/System.Embedded.1/Thermal":
					http.ServeFile(w, r, tt.thermalfilename)
				case "/redfish/v1/Chassis/System.Embedded.1/Power":
					http.ServeFile(w, r, tt.powerfilename)
				case "/redfish/v1/Chassis/System.Embedded.1":
					http.ServeFile(w, r, tt.chassisfilename)
				case "/redfish/v1/Systems/System.Embedded.1":
					http.ServeFile(w, r, tt.hostnamefilename)
				default:
					w.WriteHeader(http.StatusNotFound)
				}
			}))
			defer ts.Close()

			plugin := &Redfish{
				Address:          ts.URL,
				Username:         config.NewSecret([]byte("test")),
				Password:         config.NewSecret([]byte("test")),
				ComputerSystemID: "System.Embedded.1",
				IncludeMetrics:   []string{"thermal", "power"},
			}

			require.NoError(t, plugin.Init())

			var acc testutil.Accumulator
			err := plugin.Gather(&acc)
			require.Error(t, err)
			require.ErrorContains(t, err, "error parsing input from")
		})
	}
}

func TestInvalidHPJSON(t *testing.T) {
	tests := []struct {
		name             string
		thermalfilename  string
		powerfilename    string
		hostnamefilename string
		chassisfilename  string
	}{
		{
			name:             "check Thermal",
			thermalfilename:  "testdata/hp/hp_thermalinvalid.json",
			powerfilename:    "testdata/hp/hp_power.json",
			hostnamefilename: "testdata/hp/hp_systems.json",
			chassisfilename:  "testdata/hp/hp_chassis.json",
		},
		{
			name:             "check Power",
			thermalfilename:  "testdata/hp/hp_thermal.json",
			powerfilename:    "testdata/hp/hp_powerinvalid.json",
			hostnamefilename: "testdata/hp/hp_systems.json",
			chassisfilename:  "testdata/hp/hp_chassis.json",
		},
		{
			name:             "check Hostname",
			thermalfilename:  "testdata/hp/hp_thermal.json",
			powerfilename:    "testdata/hp/hp_power.json",
			hostnamefilename: "testdata/hp/hp_systemsinvalid.json",
			chassisfilename:  "testdata/hp/hp_chassis.json",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				if !checkAuth(r, "test", "test") {
					http.Error(w, "Unauthorized.", http.StatusUnauthorized)
					return
				}

				switch r.URL.Path {
				case "/redfish/v1/Chassis/1/Thermal":
					http.ServeFile(w, r, tt.thermalfilename)
				case "/redfish/v1/Chassis/1/Power":
					http.ServeFile(w, r, tt.powerfilename)
				case "/redfish/v1/Chassis/1/":
					http.ServeFile(w, r, tt.chassisfilename)
				case "/redfish/v1/Systems/System.Embedded.2":
					http.ServeFile(w, r, tt.hostnamefilename)
				default:
					w.WriteHeader(http.StatusNotFound)
				}
			}))
			defer ts.Close()

			plugin := &Redfish{
				Address:          ts.URL,
				Username:         config.NewSecret([]byte("test")),
				Password:         config.NewSecret([]byte("test")),
				ComputerSystemID: "System.Embedded.2",
				IncludeMetrics:   []string{"thermal", "power"},
			}

			require.NoError(t, plugin.Init())

			var acc testutil.Accumulator
			err := plugin.Gather(&acc)
			require.Error(t, err)
			require.ErrorContains(t, err, "error parsing input from")
		})
	}
}

func TestParseErrorIncludesContext(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}
		// Return an HTML page with a 200 status, as some BMCs do when they
		// serve a web-UI/login page instead of the Redfish JSON resource.
		w.Header().Set("Content-Type", "text/html")
		if _, err := w.Write([]byte("<html><body>login</body></html>")); err != nil {
			t.Error(err)
		}
	}))
	defer ts.Close()

	plugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "System.Embedded.1",
		IncludeMetrics:   []string{"thermal", "power"},
	}
	require.NoError(t, plugin.Init())

	var acc testutil.Accumulator
	err := plugin.Gather(&acc)
	require.ErrorContains(t, err, ts.URL+"/redfish/v1/Systems/System.Embedded.1")
	require.ErrorContains(t, err, "text/html")
}

func TestSkipChassisWithoutReference(t *testing.T) {
	var mu sync.Mutex
	var requested []string

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		mu.Lock()
		requested = append(requested, r.URL.Path)
		mu.Unlock()

		w.Header().Set("Content-Type", "application/json")
		if _, err := w.Write([]byte(`{"Links": {"Chassis": [{"@odata.id": ""}]}}`)); err != nil {
			t.Error(err)
		}
	}))
	defer ts.Close()

	plugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "1",
		IncludeMetrics:   []string{"thermal", "power"},
		Log:              testutil.Logger{},
	}
	require.NoError(t, plugin.Init())

	var acc testutil.Accumulator
	require.NoError(t, plugin.Gather(&acc))
	require.Empty(t, acc.GetTelegrafMetrics())

	// The empty reference must not be requested as it resolves to the web root
	mu.Lock()
	defer mu.Unlock()
	require.Equal(t, []string{"/redfish/v1/Systems/1"}, requested)
}

func TestSkipChassisWithoutThermalAndPowerReference(t *testing.T) {
	var mu sync.Mutex
	var requested []string

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		mu.Lock()
		requested = append(requested, r.URL.Path)
		mu.Unlock()

		w.Header().Set("Content-Type", "application/json")

		var body string
		switch r.URL.Path {
		case "/redfish/v1/Systems/1":
			body = `{"Links": {"Chassis": [{"@odata.id": "/redfish/v1/Chassis/1"}]}}`
		case "/redfish/v1/Chassis/1":
			// Firmware exposing the newer ThermalSubsystem and PowerSubsystem
			// resources does not provide the Thermal and Power ones
			body = `{"ChassisType": "RackMount", "Model": "AS-1116CS-TN"}`
		default:
			t.Errorf("unexpected request for %q", r.URL.Path)
			return
		}

		if _, err := w.Write([]byte(body)); err != nil {
			t.Error(err)
		}
	}))
	defer ts.Close()

	plugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "1",
		IncludeMetrics:   []string{"thermal", "power"},
		Log:              testutil.Logger{},
	}
	require.NoError(t, plugin.Init())

	var acc testutil.Accumulator
	require.NoError(t, plugin.Gather(&acc))
	require.Empty(t, acc.GetTelegrafMetrics())

	// The missing references must not be requested as they resolve to the web root
	mu.Lock()
	defer mu.Unlock()
	require.Equal(t, []string{"/redfish/v1/Systems/1", "/redfish/v1/Chassis/1"}, requested)
}

func TestIncludeTagSetsConfiguration(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !checkAuth(r, "test", "test") {
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		switch r.URL.Path {
		case "/redfish/v1/":
			http.ServeFile(w, r, "testdata/base.json")
		case "/redfish/v1/Systems/":
			http.ServeFile(w, r, "testdata/hp/hp_available_systems.json")
		case "/redfish/v1/Chassis/1/Thermal":
			http.ServeFile(w, r, "testdata/hp/hp_thermal.json")
		case "/redfish/v1/Chassis/1/Power":
			http.ServeFile(w, r, "testdata/hp/hp_power.json")
		case "/redfish/v1/Systems/1":
			http.ServeFile(w, r, "testdata/hp/hp_systems.json")
		case "/redfish/v1/Chassis/1/":
			http.ServeFile(w, r, "testdata/hp/hp_chassis.json")
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}))

	defer ts.Close()

	u, err := url.Parse(ts.URL)
	require.NoError(t, err)
	address, _, err := net.SplitHostPort(u.Host)
	require.NoError(t, err)

	expectedMetricsHp := []telegraf.Metric{
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":                 "01-Inlet Ambient",
				"member_id":            "0",
				"source":               "tpa-hostname",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"reading_celsius":          19.0,
				"upper_threshold_critical": 42.0,
				"upper_threshold_fatal":    47.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_temperatures",
			map[string]string{
				"name":                 "44-P/S 2 Zone",
				"source":               "tpa-hostname",
				"member_id":            "42",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"reading_celsius":          34.0,
				"upper_threshold_critical": 75.0,
				"upper_threshold_fatal":    80.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":               "tpa-hostname",
				"name":                 "Fan 1",
				"member_id":            "0",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"reading_percent": 23,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":               "tpa-hostname",
				"name":                 "Fan 2",
				"member_id":            "1",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"reading_percent": 23,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_thermal_fans",
			map[string]string{
				"source":               "tpa-hostname",
				"name":                 "Fan 3",
				"member_id":            "2",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"reading_percent": 23,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powercontrol",
			map[string]string{
				"source":               "tpa-hostname",
				"name":                 "",
				"member_id":            "0",
				"address":              address,
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"average_consumed_watts": 221.0,
				"interval_in_min":        int64(20),
				"max_consumed_watts":     252.0,
				"min_consumed_watts":     220.0,
				"power_capacity_watts":   1600.0,
				"power_consumed_watts":   221.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powersupplies",
			map[string]string{
				"source":               "tpa-hostname",
				"name":                 "HpeServerPowerSupply",
				"member_id":            "0",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"power_capacity_watts":    800.0,
				"line_input_voltage":      205.0,
				"last_power_output_watts": 0.0,
			},
			time.Unix(0, 0),
		),
		metric.New(
			"redfish_power_powersupplies",
			map[string]string{
				"source":               "tpa-hostname",
				"name":                 "HpeServerPowerSupply",
				"member_id":            "1",
				"address":              address,
				"health":               "OK",
				"state":                "Enabled",
				"rack":                 "",
				"room":                 "",
				"row":                  "",
				"datacenter":           "",
				"chassis_chassistype":  "RackMount",
				"chassis_manufacturer": "HP",
				"chassis_model":        "Proliant Gen10",
				"chassis_partnumber":   "CT6NWPYZ",
				"chassis_powerstate":   "On",
				"chassis_sku":          "CLFYTTWP",
				"chassis_serialnumber": "QWEVC007C99803",
				"chassis_state":        "Enabled",
				"chassis_health":       "OK",
			},
			map[string]interface{}{
				"power_capacity_watts":    800.0,
				"line_input_voltage":      205.0,
				"last_power_output_watts": 90.0,
			},
			time.Unix(0, 0),
		),
	}

	hpPlugin := &Redfish{
		Address:          ts.URL,
		Username:         config.NewSecret([]byte("test")),
		Password:         config.NewSecret([]byte("test")),
		ComputerSystemID: "1",
		IncludeTagSets:   []string{"chassis", "chassis.location"},
		IncludeMetrics:   []string{"thermal", "power"},
	}
	require.NoError(t, hpPlugin.Init())
	var hpAcc testutil.Accumulator

	err = hpPlugin.Gather(&hpAcc)
	require.NoError(t, err)
	require.True(t, hpAcc.HasMeasurement("redfish_thermal_temperatures"))
	testutil.RequireMetricsEqual(t, expectedMetricsHp, hpAcc.GetTelegrafMetrics(),
		testutil.IgnoreTime())
}
