Get the weather for Honolulu #
A logical next step would be to get the weather for a single specific location,
let’s go with Honolulu for now. To keep things simple, let’s get the smallest
string we can by calling curl "https://wttr.in/honolulu?0" This should return
something like
Weather report: honolulu
\ / Partly cloudy
_ /"".-. +23(25) °C
\_( ). ↑ 11 km/h
/(___(__) 16 km
0.1 mmmain.go #
The minimum code we can get away with for getting the latest weather in Honolulu would be:
package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
resp, err := http.Get("https://wttr.in/honolulu?0A")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Panic(err)
}
fmt.Println(string(body))
}Read the response #
Get the response using http.Get(), if that
fails return an error with log.Fatal(). This
prints the error and calls os.Exit(1), this lets
the command return status 1 when something goes wrong.
Defer with resp.Body.Close() is used to
free up the response when the end of the main() function is reached.
resp, err := http.Get("https://wttr.in/honolulu?0A")
if err != nil {
log.Fatal(err)
}
// close up the response to save resources
defer resp.Body.Close()Translate the response #
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Panic(err)
}
fmt.Println(string(body))Response.Body is an io.ReadCloser. This
type is used as the response body could be multiple things (i.e.: string, JSON
etc.). Therefore, io.ReadAll must first be
called to transform the response body into a []bypte type. If nothing goes
wrong this is converted to a string and outputted to the user. Note that
log.Panic() is called instead of io.LogFatal()
here as io.LogPanic() calls the defer function before exiting.
We can de better #
This solution isn’t great for multiple reasons:
- There is no control of the http timeout
- We can only see the weather in Honolulu
- We’re overly dependent on wttr.in. If the host is down for whatever reason, the tests will fail.
- All the logic is in
main(), making testing challenging - The wttr domain is hard coded, this prevents us from accessing our own instance
All of these issues and more will be addressed later, for now this is good enough.
main_test.go #
For our test, we’re just going to check that Weather report: honolulu is
included in the command output.
package main
import (
"bytes"
"github.com/stretchr/testify/assert"
"io"
"os"
"testing"
)
func TestCmdOutput(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
main()
_ = w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
got := buf.String()
want := "Weather report: honolulu"
assert.Contains(t, got, want)
}The only real change in the test is that we check that the response contains the
string Weather report: honolulu.
Source code #
This version of the code can be found on Github.