package openweathermap import ( "fmt" "net/http" "net/http/httptest" "testing" "time" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/require" ) const sampleNoContent = ` { } ` const sampleStatusResponse = ` { "city": { "coord": { "lat": 48.8534, "lon": 2.3488 }, "country": "FR", "id": 2988507, "name": "Paris" }, "cnt": 40, "cod": "200", "list": [ { "clouds": { "all": 88 }, "dt": 1543622400, "dt_txt": "2018-12-01 00:00:00", "main": { "grnd_level": 1018.65, "humidity": 91, "pressure": 1018.65, "sea_level": 1030.99, "temp": 6.71, "temp_kf": -2.14 }, "rain": { "3h": 0.035 }, "sys": { "pod": "n" }, "weather": [ { "description": "light rain", "icon": "10n", "id": 500, "main": "Rain" } ], "wind": { "deg": 228.501, "speed": 3.76 } }, { "clouds": { "all": 92 }, "dt": 1544043600, "dt_txt": "2018-12-05 21:00:00", "main": { "grnd_level": 1032.18, "humidity": 98, "pressure": 1032.18, "sea_level": 1044.78, "temp": 6.38, "temp_kf": 0 }, "rain": { "3h": 0.049999999999997 }, "sys": { "pod": "n" }, "weather": [ { "description": "light rain", "icon": "10n", "id": 500, "main": "Rain" } ], "wind": { "deg": 335.005, "speed": 2.66 } } ], "message": 0.0025 } ` const groupWeatherResponse = ` { "cnt": 1, "list": [{ "clouds": { "all": 0 }, "coord": { "lat": 48.85, "lon": 2.35 }, "dt": 1544194800, "id": 2988507, "main": { "humidity": 87, "pressure": 1007, "temp": 9.25 }, "name": "Paris", "sys": { "country": "FR", "id": 6550, "message": 0.002, "sunrise": 1544167818, "sunset": 1544198047, "type": 1 }, "visibility": 10000, "weather": [ { "description": "light intensity drizzle", "icon": "09d", "id": 300, "main": "Drizzle" } ], "wind": { "deg": 290, "speed": 8.7 } }] } ` const rainWeatherResponse = ` { "cnt": 2, "list": [{ "dt": 1544194800, "id": 111, "main": { "humidity": 87, "pressure": 1007, "temp": 9.25 }, "name": "Paris", "sys": { "country": "FR", "id": 6550, "message": 0.002, "sunrise": 1544167818, "sunset": 1544198047, "type": 1 }, "visibility": 10000, "weather": [ { "description": "light intensity drizzle", "icon": "09d", "id": 300, "main": "Drizzle" } ], "rain": { "1h": 1.000 }, "wind": { "deg": 290, "speed": 8.7 } }, { "dt": 1544194800, "id": 222, "main": { "humidity": 87, "pressure": 1007, "temp": 9.25 }, "name": "Paris", "sys": { "country": "FR", "id": 6550, "message": 0.002, "sunrise": 1544167818, "sunset": 1544198047, "type": 1 }, "visibility": 10000, "weather": [ { "description": "light intensity drizzle", "icon": "09d", "id": 300, "main": "Drizzle" } ], "rain": { "3h": 3.000 }, "wind": { "deg": 290, "speed": 8.7 } }, { "dt": 1544194800, "id": 333, "main": { "humidity": 87, "pressure": 1007, "temp": 9.25 }, "name": "Paris", "sys": { "country": "FR", "id": 6550, "message": 0.002, "sunrise": 1544167818, "sunset": 1544198047, "type": 1 }, "visibility": 10000, "weather": [ { "description": "light intensity drizzle", "icon": "09d", "id": 300, "main": "Drizzle" } ], "rain": { "1h": 1.300, "3h": 999 }, "wind": { "deg": 290, "speed": 8.7 } }, { "dt": 1544194800, "id": 444, "main": { "humidity": 87, "pressure": 1007, "temp": 9.25 }, "name": "Paris", "sys": { "country": "FR", "id": 6550, "message": 0.002, "sunrise": 1544167818, "sunset": 1544198047, "type": 1 }, "visibility": 10000, "weather": [ { "description": "light intensity drizzle", "icon": "09d", "id": 300, "main": "Drizzle" } ], "wind": { "deg": 290, "speed": 8.7 } }] } ` const batchWeatherResponse = ` { "cnt": 3, "list": [{ "coord": { "lon": 37.62, "lat": 55.75 }, "sys": { "type": 1, "id": 9029, "message": 0.0061, "country": "RU", "sunrise": 1556416455, "sunset": 1556470779 }, "weather": [{ "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03d" }], "main": { "temp": 9.57, "pressure": 1014, "humidity": 46 }, "visibility": 10000, "wind": { "speed": 5, "deg": 60 }, "clouds": { "all": 40 }, "dt": 1556444155, "id": 524901, "name": "Moscow" }, { "coord": { "lon": 30.52, "lat": 50.43 }, "sys": { "type": 1, "id": 8903, "message": 0.0076, "country": "UA", "sunrise": 1556419155, "sunset": 1556471486 }, "weather": [{ "id": 520, "main": "Rain", "description": "light intensity shower rain", "icon": "09d" }], "main": { "temp": 19.29, "pressure": 1009, "humidity": 63 }, "visibility": 10000, "wind": { "speed": 1 }, "clouds": { "all": 0 }, "dt": 1556444155, "id": 703448, "name": "Kiev" }, { "coord": { "lon": -0.13, "lat": 51.51 }, "sys": { "type": 1, "id": 1414, "message": 0.0088, "country": "GB", "sunrise": 1556426319, "sunset": 1556479032 }, "weather": [{ "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04d" }], "main": { "temp": 10.62, "pressure": 1019, "humidity": 66 }, "visibility": 10000, "wind": { "speed": 6.2, "deg": 290 }, "rain": { "3h": 0.072 }, "clouds": { "all": 75 }, "dt": 1556444155, "id": 2643743, "name": "London" }] } ` func TestForecastGeneratesMetrics(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var rsp string if r.URL.Path == "/data/2.5/forecast" { rsp = sampleStatusResponse w.Header()["Content-Type"] = []string{"application/json"} } else if r.URL.Path == "/data/2.5/group" { rsp = sampleNoContent } else { panic("Cannot handle request") } fmt.Fprintln(w, rsp) })) defer ts.Close() n := &OpenWeatherMap{ BaseUrl: ts.URL, AppId: "noappid", CityId: []string{"2988507"}, Fetch: []string{"weather", "forecast"}, Units: "metric", } n.Init() var acc testutil.Accumulator err := n.Gather(&acc) require.NoError(t, err) expected := []telegraf.Metric{ testutil.MustMetric( "weather", map[string]string{ "city_id": "2988507", "forecast": "3h", "city": "Paris", "country": "FR", "condition_id": "500", "condition_main": "Rain", }, map[string]interface{}{ "cloudiness": int64(88), "humidity": int64(91), "pressure": 1018.65, "temperature": 6.71, "rain": 0.035, "wind_degrees": 228.501, "wind_speed": 3.76, "condition_description": "light rain", "condition_icon": "10n", }, time.Unix(1543622400, 0), ), testutil.MustMetric( "weather", map[string]string{ "city_id": "2988507", "forecast": "6h", "city": "Paris", "country": "FR", "condition_id": "500", "condition_main": "Rain", }, map[string]interface{}{ "cloudiness": int64(92), "humidity": int64(98), "pressure": 1032.18, "temperature": 6.38, "rain": 0.049999999999997, "wind_degrees": 335.005, "wind_speed": 2.66, "condition_description": "light rain", "condition_icon": "10n", }, time.Unix(1544043600, 0), ), } testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.SortMetrics()) } func TestWeatherGeneratesMetrics(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var rsp string if r.URL.Path == "/data/2.5/group" { rsp = groupWeatherResponse w.Header()["Content-Type"] = []string{"application/json"} } else if r.URL.Path == "/data/2.5/forecast" { rsp = sampleNoContent } else { panic("Cannot handle request") } fmt.Fprintln(w, rsp) })) defer ts.Close() n := &OpenWeatherMap{ BaseUrl: ts.URL, AppId: "noappid", CityId: []string{"2988507"}, Fetch: []string{"weather"}, Units: "metric", } n.Init() var acc testutil.Accumulator err := n.Gather(&acc) require.NoError(t, err) expected := []telegraf.Metric{ testutil.MustMetric( "weather", map[string]string{ "city_id": "2988507", "forecast": "*", "city": "Paris", "country": "FR", "condition_id": "300", "condition_main": "Drizzle", }, map[string]interface{}{ "cloudiness": int64(0), "humidity": int64(87), "pressure": 1007.0, "temperature": 9.25, "rain": 0.0, "sunrise": int64(1544167818000000000), "sunset": int64(1544198047000000000), "wind_degrees": 290.0, "wind_speed": 8.7, "visibility": 10000, "condition_description": "light intensity drizzle", "condition_icon": "09d", }, time.Unix(1544194800, 0), ), } testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics()) } // Ensure that results containing "1h", "3h", both, or no rain values are parsed correctly func TestRainMetrics(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var rsp string if r.URL.Path == "/data/2.5/group" { rsp = rainWeatherResponse w.Header()["Content-Type"] = []string{"application/json"} } else { panic("Cannot handle request") } fmt.Fprintln(w, rsp) })) defer ts.Close() n := &OpenWeatherMap{ BaseUrl: ts.URL, AppId: "noappid", CityId: []string{"111", "222", "333", "444"}, Fetch: []string{"weather"}, Units: "metric", } n.Init() var acc testutil.Accumulator err := n.Gather(&acc) require.NoError(t, err) expected := []telegraf.Metric{ // City with 1h rain value testutil.MustMetric( "weather", map[string]string{ "city_id": "111", "forecast": "*", "city": "Paris", "country": "FR", "condition_id": "300", "condition_main": "Drizzle", }, map[string]interface{}{ "cloudiness": int64(0), "humidity": int64(87), "pressure": 1007.0, "temperature": 9.25, "rain": 1.0, "sunrise": int64(1544167818000000000), "sunset": int64(1544198047000000000), "wind_degrees": 290.0, "wind_speed": 8.7, "visibility": 10000, "condition_description": "light intensity drizzle", "condition_icon": "09d", }, time.Unix(1544194800, 0), ), // City with 3h rain value testutil.MustMetric( "weather", map[string]string{ "city_id": "222", "forecast": "*", "city": "Paris", "country": "FR", "condition_id": "300", "condition_main": "Drizzle", }, map[string]interface{}{ "cloudiness": int64(0), "humidity": int64(87), "pressure": 1007.0, "temperature": 9.25, "rain": 3.0, "sunrise": int64(1544167818000000000), "sunset": int64(1544198047000000000), "wind_degrees": 290.0, "wind_speed": 8.7, "visibility": 10000, "condition_description": "light intensity drizzle", "condition_icon": "09d", }, time.Unix(1544194800, 0), ), // City with both 1h and 3h rain values, prefer the 1h value testutil.MustMetric( "weather", map[string]string{ "city_id": "333", "forecast": "*", "city": "Paris", "country": "FR", "condition_id": "300", "condition_main": "Drizzle", }, map[string]interface{}{ "cloudiness": int64(0), "humidity": int64(87), "pressure": 1007.0, "temperature": 9.25, "rain": 1.3, "sunrise": int64(1544167818000000000), "sunset": int64(1544198047000000000), "wind_degrees": 290.0, "wind_speed": 8.7, "visibility": 10000, "condition_description": "light intensity drizzle", "condition_icon": "09d", }, time.Unix(1544194800, 0), ), // City with no rain values testutil.MustMetric( "weather", map[string]string{ "city_id": "444", "forecast": "*", "city": "Paris", "country": "FR", "condition_id": "300", "condition_main": "Drizzle", }, map[string]interface{}{ "cloudiness": int64(0), "humidity": int64(87), "pressure": 1007.0, "temperature": 9.25, "rain": 0.0, "sunrise": int64(1544167818000000000), "sunset": int64(1544198047000000000), "wind_degrees": 290.0, "wind_speed": 8.7, "visibility": 10000, "condition_description": "light intensity drizzle", "condition_icon": "09d", }, time.Unix(1544194800, 0), ), } testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics()) } func TestBatchWeatherGeneratesMetrics(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var rsp string if r.URL.Path == "/data/2.5/group" { rsp = batchWeatherResponse w.Header()["Content-Type"] = []string{"application/json"} } else if r.URL.Path == "/data/2.5/forecast" { rsp = sampleNoContent } else { panic("Cannot handle request") } fmt.Fprintln(w, rsp) })) defer ts.Close() n := &OpenWeatherMap{ BaseUrl: ts.URL, AppId: "noappid", CityId: []string{"524901", "703448", "2643743"}, Fetch: []string{"weather"}, Units: "metric", } n.Init() var acc testutil.Accumulator err := n.Gather(&acc) require.NoError(t, err) expected := []telegraf.Metric{ testutil.MustMetric( "weather", map[string]string{ "city_id": "524901", "forecast": "*", "city": "Moscow", "country": "RU", "condition_id": "802", "condition_main": "Clouds", }, map[string]interface{}{ "cloudiness": 40, "humidity": int64(46), "pressure": 1014.0, "temperature": 9.57, "wind_degrees": 60.0, "wind_speed": 5.0, "rain": 0.0, "sunrise": int64(1556416455000000000), "sunset": int64(1556470779000000000), "visibility": 10000, "condition_description": "scattered clouds", "condition_icon": "03d", }, time.Unix(1556444155, 0), ), testutil.MustMetric( "weather", map[string]string{ "city_id": "703448", "forecast": "*", "city": "Kiev", "country": "UA", "condition_id": "520", "condition_main": "Rain", }, map[string]interface{}{ "cloudiness": 0, "humidity": int64(63), "pressure": 1009.0, "temperature": 19.29, "wind_degrees": 0.0, "wind_speed": 1.0, "rain": 0.0, "sunrise": int64(1556419155000000000), "sunset": int64(1556471486000000000), "visibility": 10000, "condition_description": "light intensity shower rain", "condition_icon": "09d", }, time.Unix(1556444155, 0), ), testutil.MustMetric( "weather", map[string]string{ "city_id": "2643743", "forecast": "*", "city": "London", "country": "GB", "condition_id": "803", "condition_main": "Clouds", }, map[string]interface{}{ "cloudiness": 75, "humidity": int64(66), "pressure": 1019.0, "temperature": 10.62, "wind_degrees": 290.0, "wind_speed": 6.2, "rain": 0.072, "sunrise": int64(1556426319000000000), "sunset": int64(1556479032000000000), "visibility": 10000, "condition_description": "broken clouds", "condition_icon": "04d", }, time.Unix(1556444155, 0), ), } testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.SortMetrics()) } func TestFormatURL(t *testing.T) { n := &OpenWeatherMap{ AppId: "appid", Units: "units", Lang: "lang", BaseUrl: "http://foo.com", } n.Init() require.Equal(t, "http://foo.com/data/2.5/forecast?APPID=appid&id=12345&lang=lang&units=units", n.formatURL("/data/2.5/forecast", "12345")) } func TestDefaultUnits(t *testing.T) { n := &OpenWeatherMap{} n.Init() require.Equal(t, "metric", n.Units) } func TestDefaultLang(t *testing.T) { n := &OpenWeatherMap{} n.Init() require.Equal(t, "en", n.Lang) }