mirror of
https://github.com/EricNeid/go-openweather.git
synced 2025-01-10 15:05:07 +00:00
Initial commit
This commit is contained in:
commit
084da1a39f
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.key
|
||||
debug.test
|
||||
.vscode
|
||||
*.exe
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Eric Neidhardt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
Makefile
Normal file
5
Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
build:
|
||||
cd cmd/openweatherclient && go build
|
||||
|
||||
test:
|
||||
go test ./...
|
46
README.md
Normal file
46
README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# go - openweatherapi
|
||||
|
||||
This Repo contains golang library to query OpenWetherMaps (<http://openweathermap.org/>) for weather information.
|
||||
|
||||
* current weather: http://openweathermap.org/current
|
||||
* 5 days forecast: http://openweathermap.org/forecast5
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/EricNeid/openweather
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Is available on ``godoc``:
|
||||
|
||||
<https://godoc.org/github.com/EricNeid/openweather>
|
||||
|
||||
## Examples
|
||||
|
||||
Consuming the library:
|
||||
|
||||
```go
|
||||
import "github.com/EricNeid/openweather"
|
||||
|
||||
// create a query
|
||||
q := openweather.NewQueryForCity(readAPIKey(), "Berlin,de")
|
||||
|
||||
// obtain data
|
||||
resp, err := q.Weather()
|
||||
|
||||
// enjoy
|
||||
fmt.Println(resp.Name) // Berlin
|
||||
fmt.Println(resp.Weather[0].Description) // misc
|
||||
fmt.Println(resp.Main.Temp) // 1
|
||||
```
|
||||
|
||||
See the test files for more example.
|
||||
|
||||
A simple client for testing is also included:
|
||||
|
||||
```bash
|
||||
go build cmd/openweatherclient
|
||||
openweatherclient -key <OpenWeather API Key> -city Berlin,de
|
||||
```
|
131
api.go
Normal file
131
api.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Package openweather contains helper functions to query
|
||||
// OpenWeatherMaps (http://openweathermap.org/) for weather information.
|
||||
// Currently the current weather API (http://openweathermap.org/current) and the
|
||||
// 5 days forecast API (http://openweathermap.org/forecast5) are supported.
|
||||
package openweather
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WeatherRaw downloads current weather data from openweathermap and return them as string.
|
||||
func (query Query) WeatherRaw() (json string, err error) {
|
||||
bytes, err := download(WeatherURL(query))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Weather downloads current weather data from openweathermap and return them as WeatherData.
|
||||
func (query Query) Weather() (*CurrentWeather, error) {
|
||||
bytes, err := download(WeatherURL(query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dataPtr := &CurrentWeather{}
|
||||
err = json.Unmarshal(bytes, dataPtr)
|
||||
return dataPtr, err
|
||||
}
|
||||
|
||||
// DailyForecast5Raw downloads 5 days forecast data from openweathermap and return them as string.
|
||||
func (query Query) DailyForecast5Raw() (json string, err error) {
|
||||
bytes, err := download(DailyForecast5URL(query))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// DailyForecast5 downloads 5 days forecast data from openweathermap and return them as DailyForecast5.
|
||||
func (query Query) DailyForecast5() (*DailyForecast5, error) {
|
||||
bytes, err := download(DailyForecast5URL(query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataPtr := &DailyForecast5{}
|
||||
err = json.Unmarshal(bytes, dataPtr)
|
||||
return dataPtr, err
|
||||
}
|
||||
|
||||
// DailyForecast16Raw downloads 16 days forecast data from openweathermap and return them as string.
|
||||
// Warning: the 16 days forecast requires a paid account.
|
||||
func (query Query) DailyForecast16Raw() (json string, err error) {
|
||||
bytes, err := download(DailyForecast16URL(query))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// DailyForecast16 downloads 16 days forecast data from openweathermap and return them as DailyForecast16.
|
||||
// Warning: the 16 days forecast requires a paid account.
|
||||
func (query Query) DailyForecast16() (*DailyForecast16, error) {
|
||||
bytes, err := download(DailyForecast16URL(query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataPtr := &DailyForecast16{}
|
||||
err = json.Unmarshal(bytes, dataPtr)
|
||||
return dataPtr, err
|
||||
}
|
||||
|
||||
func download(url string) (res []byte, err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// WeatherIconURL returns an url to download matching icon for
|
||||
// given weather id.
|
||||
func WeatherIconURL(iconID string) (url string) {
|
||||
return "http://openweathermap.org/img/w/" + iconID + ".png"
|
||||
}
|
||||
|
||||
// DailyForecast5URL returns a matching url for the given query which can be used to obtain the 5 days forecast
|
||||
// from openweathermap.org.
|
||||
func DailyForecast5URL(q Query) string {
|
||||
return "http://api.openweathermap.org/data/2.5/forecast/daily" + formatURLQuery(q)
|
||||
}
|
||||
|
||||
// DailyForecast16URL returns a matching url for the given query which can be used to obtain the 16 days forecast
|
||||
// from openweathermap.org.
|
||||
func DailyForecast16URL(q Query) string {
|
||||
return "http://api.openweathermap.org/data/2.5/forecast/daily" + formatURLQuery(q) + "&cnt=16"
|
||||
}
|
||||
|
||||
// WeatherURL returns a matching url for the given query which can be used to obtain the current weather information
|
||||
// from openweathermap.org.
|
||||
func WeatherURL(q Query) string {
|
||||
return "http://api.openweathermap.org/data/2.5/weather" + formatURLQuery(q)
|
||||
}
|
||||
|
||||
func formatURLQuery(q Query) string {
|
||||
queryType := q.queryType
|
||||
queryValue := q.Query
|
||||
var query string
|
||||
|
||||
if queryType == queryTypeGeo {
|
||||
params := strings.Split(queryValue, "|") // expected format is lat|long
|
||||
lat := params[0]
|
||||
lon := params[1]
|
||||
query = fmt.Sprintf("?lat=%s&lon=%s", lat, lon)
|
||||
} else {
|
||||
query = fmt.Sprintf("?%s=%s", queryType, queryValue)
|
||||
}
|
||||
|
||||
query = query + fmt.Sprintf("&appid=%s&units=%s", q.APIKey, q.Unit)
|
||||
return query
|
||||
}
|
64
api_test.go
Normal file
64
api_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package openweather
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/EricNeid/openweather/internal/test"
|
||||
)
|
||||
|
||||
const apiKeyFile = "testdata/api.key"
|
||||
const cityBerlin = "Berlin,de"
|
||||
|
||||
func readAPIKey() string {
|
||||
key, err := ioutil.ReadFile(apiKeyFile)
|
||||
if err != nil {
|
||||
panic(`
|
||||
Cannot run test, you must provide openweathermap api key.
|
||||
Expected: testdata/api.key
|
||||
|
||||
See https://home.openweathermap.org/users/sign_up
|
||||
for information how to obtain a key`)
|
||||
}
|
||||
return string(key)
|
||||
}
|
||||
|
||||
func TestForecastRaw(t *testing.T) {
|
||||
// arrange
|
||||
q := NewQueryForCity(readAPIKey(), cityBerlin)
|
||||
// action
|
||||
resp, err := q.DailyForecast5Raw()
|
||||
// verify
|
||||
test.Ok(t, err)
|
||||
test.Assert(t, len(resp) > 0, "Received empty response")
|
||||
}
|
||||
|
||||
func TestWeatherRaw(t *testing.T) {
|
||||
// arrange
|
||||
q := NewQueryForCity(readAPIKey(), cityBerlin)
|
||||
// action
|
||||
resp, err := q.WeatherRaw()
|
||||
// verify
|
||||
test.Ok(t, err)
|
||||
test.Assert(t, len(resp) > 0, "Received empty response")
|
||||
}
|
||||
|
||||
func TestWeather(t *testing.T) {
|
||||
// arrange
|
||||
q := NewQueryForCity(readAPIKey(), cityBerlin)
|
||||
// action
|
||||
data, err := q.Weather()
|
||||
// verify
|
||||
test.Ok(t, err)
|
||||
test.Equals(t, "Berlin", data.Name)
|
||||
}
|
||||
|
||||
func TestDailyForecast(t *testing.T) {
|
||||
// arrange
|
||||
q := NewQueryForCity(readAPIKey(), cityBerlin)
|
||||
// action
|
||||
data, err := q.DailyForecast5()
|
||||
// verify
|
||||
test.Ok(t, err)
|
||||
test.Equals(t, "Berlin", data.City.Name)
|
||||
}
|
29
cmd/openweatherclient/main.go
Normal file
29
cmd/openweatherclient/main.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/EricNeid/openweather"
|
||||
)
|
||||
|
||||
func main() {
|
||||
keyPtr := flag.String("key", "", "Supply valid api key, see https://home.openweathermap.org/users/sign_up for details.")
|
||||
cityPtr := flag.String("city", "", "City to look up")
|
||||
flag.Parse()
|
||||
|
||||
if len(*keyPtr) == 0 || len(*cityPtr) == 0 {
|
||||
fmt.Println("Usage: simpleclient.exe -key <api-key> -city <city-to-query>")
|
||||
return
|
||||
}
|
||||
|
||||
query := openweather.NewQueryForCity(*keyPtr, *cityPtr, "metric")
|
||||
weather, err := query.WeatherRaw()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error while retrieving data: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(weather)
|
||||
}
|
36
internal/test/testing_utils.go
Normal file
36
internal/test/testing_utils.go
Normal file
@ -0,0 +1,36 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Assert fails the test if the condition is false.
|
||||
func Assert(t *testing.T, condition bool, msg string, v ...interface{}) {
|
||||
if !condition {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// Ok fails the test if an err is not nil.
|
||||
func Ok(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// Equals fails the test if exp is not equal to act.
|
||||
func Equals(t *testing.T, exp, act interface{}) {
|
||||
if !reflect.DeepEqual(exp, act) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
123
model.go
Normal file
123
model.go
Normal file
@ -0,0 +1,123 @@
|
||||
package openweather
|
||||
|
||||
// CurrentWeather represents unmarshalled data from openweathermap
|
||||
// for the current weather API (http://openweathermap.org/current).
|
||||
type CurrentWeather struct {
|
||||
Coord struct {
|
||||
Lon float64 `json:"lon"`
|
||||
Lat float64 `json:"lat"`
|
||||
} `json:"coord"`
|
||||
Weather []struct {
|
||||
ID int `json:"id"`
|
||||
Main string `json:"main"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
} `json:"weather"`
|
||||
Base string `json:"base"`
|
||||
Main struct {
|
||||
Temp float64 `json:"temp"`
|
||||
Pressure int `json:"pressure"`
|
||||
Humidity int `json:"humidity"`
|
||||
TempMin float64 `json:"temp_min"`
|
||||
TempMax float64 `json:"temp_max"`
|
||||
} `json:"main"`
|
||||
Wind struct {
|
||||
Speed float64 `json:"speed"`
|
||||
Deg int `json:"deg"`
|
||||
} `json:"wind"`
|
||||
Clouds struct {
|
||||
All int `json:"all"`
|
||||
} `json:"clouds"`
|
||||
Rain struct {
|
||||
ThreeH int `json:"3h"`
|
||||
} `json:"rain"`
|
||||
Dt int `json:"dt"`
|
||||
Sys struct {
|
||||
Type int `json:"type"`
|
||||
ID int `json:"id"`
|
||||
Message float64 `json:"message"`
|
||||
Country string `json:"country"`
|
||||
Sunrise int `json:"sunrise"`
|
||||
Sunset int `json:"sunset"`
|
||||
} `json:"sys"`
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Cod int `json:"cod"`
|
||||
}
|
||||
|
||||
// DailyForecast5 represents unmarshalled data from openweathermap
|
||||
// for the 5 days forecast weather API (http://openweathermap.org/forecast5).
|
||||
type DailyForecast5 struct {
|
||||
Cod string `json:"cod"`
|
||||
Message float64 `json:"message"`
|
||||
City struct {
|
||||
GeonameID int `json:"geoname_id"`
|
||||
Name string `json:"name"`
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
Country string `json:"country"`
|
||||
Iso2 string `json:"iso2"`
|
||||
Type string `json:"type"`
|
||||
Population int `json:"population"`
|
||||
} `json:"city"`
|
||||
Cnt int `json:"cnt"`
|
||||
List []struct {
|
||||
Dt int `json:"dt"`
|
||||
Temp struct {
|
||||
Day float64 `json:"day"`
|
||||
Min float64 `json:"min"`
|
||||
Max float64 `json:"max"`
|
||||
Night float64 `json:"night"`
|
||||
Eve float64 `json:"eve"`
|
||||
Morn float64 `json:"morn"`
|
||||
} `json:"temp"`
|
||||
Pressure float64 `json:"pressure"`
|
||||
Humidity int `json:"humidity"`
|
||||
Weather []struct {
|
||||
ID int `json:"id"`
|
||||
Main string `json:"main"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
} `json:"weather"`
|
||||
Speed float64 `json:"speed"`
|
||||
Deg int `json:"deg"`
|
||||
Clouds int `json:"clouds"`
|
||||
Snow float64 `json:"snow,omitempty"`
|
||||
} `json:"list"`
|
||||
}
|
||||
|
||||
// DailyForecast16 represents unmarshalled data from openweathermap
|
||||
// for the 16 days forecast weather API (http://openweathermap.org/forecast16).
|
||||
type DailyForecast16 struct {
|
||||
Cod string `json:"cod"`
|
||||
Message float64 `json:"message"`
|
||||
City struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Coord struct {
|
||||
Lon float64 `json:"lon"`
|
||||
Lat float64 `json:"lat"`
|
||||
} `json:"coord"`
|
||||
Country string `json:"country"`
|
||||
} `json:"city"`
|
||||
Cnt int `json:"cnt"`
|
||||
List []struct {
|
||||
Dt int `json:"dt"`
|
||||
Temp struct {
|
||||
Day float64 `json:"day"`
|
||||
Min float64 `json:"min"`
|
||||
Max float64 `json:"max"`
|
||||
Night float64 `json:"night"`
|
||||
Eve float64 `json:"eve"`
|
||||
Morn float64 `json:"morn"`
|
||||
} `json:"temp"`
|
||||
Pressure float64 `json:"pressure"`
|
||||
Humidity int `json:"humidity"`
|
||||
Weather []struct {
|
||||
ID int `json:"id"`
|
||||
Main string `json:"main"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
} `json:"weather"`
|
||||
} `json:"list"`
|
||||
}
|
74
query.go
Normal file
74
query.go
Normal file
@ -0,0 +1,74 @@
|
||||
package openweather
|
||||
|
||||
// Query represents a pending request to openweathermap.
|
||||
type Query struct {
|
||||
APIKey string
|
||||
Unit string
|
||||
Query string
|
||||
queryType string
|
||||
}
|
||||
|
||||
const queryTypeCity = "q"
|
||||
const queryTypeZip = "zip"
|
||||
const queryTypeID = "id"
|
||||
const queryTypeGeo = "lat|lon"
|
||||
|
||||
// NewQueryForCity creates a query for openweathermap from city name.
|
||||
// The unit is optional and defaults to metric.
|
||||
func NewQueryForCity(apiKey string, city string, unit ...string) Query {
|
||||
u := "metric"
|
||||
if len(unit) > 0 {
|
||||
u = unit[0]
|
||||
}
|
||||
return Query{
|
||||
APIKey: apiKey,
|
||||
Query: city,
|
||||
queryType: queryTypeCity,
|
||||
Unit: u,
|
||||
}
|
||||
}
|
||||
|
||||
// NewQueryForZip creates a query for openweathermap from zip code.
|
||||
// The unit is optional and defaults to metric.
|
||||
func NewQueryForZip(apiKey string, zip string, unit ...string) Query {
|
||||
u := "metric"
|
||||
if len(unit) > 0 {
|
||||
u = unit[0]
|
||||
}
|
||||
return Query{
|
||||
APIKey: apiKey,
|
||||
Query: zip,
|
||||
queryType: queryTypeZip,
|
||||
Unit: u,
|
||||
}
|
||||
}
|
||||
|
||||
// NewQueryForID creates a query for openweathermap from city id.
|
||||
// The unit is optional and defaults to metric.
|
||||
func NewQueryForID(apiKey string, id string, unit ...string) Query {
|
||||
u := "metric"
|
||||
if len(unit) > 0 {
|
||||
u = unit[0]
|
||||
}
|
||||
return Query{
|
||||
APIKey: apiKey,
|
||||
Query: id,
|
||||
queryType: queryTypeID,
|
||||
Unit: u,
|
||||
}
|
||||
}
|
||||
|
||||
// NewQueryForLocation creates a query for openweathermap from latitude and longitude.
|
||||
// The unit is optional and defaults to metric.
|
||||
func NewQueryForLocation(apiKey string, lat string, lon string, unit ...string) Query {
|
||||
u := "metric"
|
||||
if len(unit) > 0 {
|
||||
u = unit[0]
|
||||
}
|
||||
return Query{
|
||||
APIKey: apiKey,
|
||||
Query: lat + "|" + lon,
|
||||
queryType: queryTypeGeo,
|
||||
Unit: u,
|
||||
}
|
||||
}
|
64
query_test.go
Normal file
64
query_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package openweather
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/EricNeid/openweather/internal/test"
|
||||
)
|
||||
|
||||
func TestNewQueryForCity(t *testing.T) {
|
||||
// arrange
|
||||
apiKey := "testKey"
|
||||
location := cityBerlin
|
||||
// action
|
||||
q := NewQueryForCity(apiKey, location)
|
||||
// verify
|
||||
test.Equals(t, apiKey, q.APIKey)
|
||||
test.Equals(t, location, q.Query)
|
||||
test.Equals(t, "metric", q.Unit)
|
||||
test.Equals(t, queryTypeCity, q.queryType)
|
||||
|
||||
// arrange
|
||||
unit := "imperial"
|
||||
// action
|
||||
q = NewQueryForCity(apiKey, location, unit)
|
||||
// verify
|
||||
test.Equals(t, unit, q.Unit)
|
||||
}
|
||||
|
||||
func TestNewQueryForZip(t *testing.T) {
|
||||
// arrange
|
||||
apiKey := "testKey"
|
||||
zip := "12345"
|
||||
// action
|
||||
q := NewQueryForZip(apiKey, zip)
|
||||
// verify
|
||||
test.Equals(t, apiKey, q.APIKey)
|
||||
test.Equals(t, zip, q.Query)
|
||||
test.Equals(t, queryTypeZip, q.queryType)
|
||||
}
|
||||
|
||||
func TestNewQueryForID(t *testing.T) {
|
||||
// arrange
|
||||
apiKey := "testKey"
|
||||
id := "42"
|
||||
// action
|
||||
q := NewQueryForID(apiKey, id)
|
||||
// verify
|
||||
test.Equals(t, apiKey, q.APIKey)
|
||||
test.Equals(t, id, q.Query)
|
||||
test.Equals(t, queryTypeID, q.queryType)
|
||||
}
|
||||
|
||||
func TestNewQueryForLocation(t *testing.T) {
|
||||
// arrange
|
||||
apiKey := "testKey"
|
||||
lat := "51"
|
||||
lon := "13"
|
||||
// action
|
||||
q := NewQueryForLocation(apiKey, lat, lon)
|
||||
// verify
|
||||
test.Equals(t, apiKey, q.APIKey)
|
||||
test.Equals(t, lat+"|"+lon, q.Query)
|
||||
test.Equals(t, queryTypeGeo, q.queryType)
|
||||
}
|
Loading…
Reference in New Issue
Block a user