How do I test while using httprouter without using controllers? - go

I'm trying to write tests for a microservice I wrote using httprouter. I've previously tried to use the answers given here: How to write test with httprouter but that doesn't seem too relevant since I don't use controllers in my microservice. Any help would be greatly appreciated.
Here's what I have so far:
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/julienschmidt/httprouter"
)
func TestCreateURL(t *testing.T) {
expected := `{"original_url":"https://www.google.com", "short_url":"https://morning-retreat-24523.herokuapp.com/get/3578"}`
handler := createURL
router := httprouter.New()
router.GET("/new/*url", handler)
req, err := http.NewRequest("GET", "/new/https://www.google.com", nil)
if err != nil {
fmt.Println(err)
}
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if rr.Body.String() != expected {
t.Errorf("Handler returned unexpected body: Got %v but want %v",
rr.Body.String(), expected)
}
}
createURL is a function like this (but with a lot more in it obviously):
func createURL(res http.ResponseWriter, req *http.Request, ps httprouter.Params) {
}

Related

How can I check specific golang net/http error code?

package main
import "net/http"
func main() {
req, err := http.NewRequest("GET", "http://domain_does_not_exist", nil)
if err != nil { panic("NewRequest") }
client := http.Client{ }
_, err = client.Do(req)
if err == ???
}
I would like to check my GET request for specific error(DNS resolve error). How to accomplish this?
Package "errors" has functions As, Is to unwrap specific error types, and package "net" has a *DNSError type. So:
var dnsErr *net.DNSError
if errors.As(err, &dnsErr) {
...
}

Web address routing works fine with http.ListenAndServe, but fails with cgi.Serve()

I'm working on a website using Go. The server constraints require that I use CGI. When I test the following code locally using http.ListenAndServe() (commented out below), the various handlers are called correctly depending on the address requested. However, if I use cgi.Serve() instead, the default router is executed for all addresses (i.e., the handler for "/" is always executed). I'd appreciate any clues as to how to fix the issue.
EDIT: Here is the simplest test case I can think of to show the problem:
//=============SIMPLIFIED CODE================//
package main
import (
"fmt"
"net/http"
"net/http/cgi"
)
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Default")
}
func otherHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Other")
}
func main() {
http.HandleFunc("/other", otherHandler)
http.HandleFunc("/", defaultHandler)
/*
//Works fine
err := http.ListenAndServe(":8090", nil)
if err != nil {
panic(err)
}
*/
//Always fires defaultHandler no matter the address requested
err := cgi.Serve(nil)
if err != nil {
panic(err)
}
}
//=============CODE FROM ORIGINAL POST===================//
package main
import (
"fmt"
"net/http"
"net/http/cgi"
"net/url"
"os"
"github.com/go-cas/cas"
)
func logoutHandler(w http.ResponseWriter, r *http.Request) {
cas.RedirectToLogout(w, r)
}
func calendarHandler(w http.ResponseWriter, r *http.Request) {
if !cas.IsAuthenticated(r) {
cas.RedirectToLogin(w, r)
}
fmt.Fprintf(w, "Calendar for %s", cas.Username(r))
}
func defaultHandler(w http.ResponseWriter, r *http.Request) {
if !cas.IsAuthenticated(r) {
cas.RedirectToLogin(w, r)
}
fmt.Fprintf(w, "Hi there %s!", cas.Username(r))
}
func main() {
u, _ := url.Parse("https://www.examplecasserver.com")
client := cas.NewClient(&cas.Options{
URL: u,
})
http.Handle("/logout", client.HandleFunc(logoutHandler))
http.Handle("/calendar", client.HandleFunc(calendarHandler))
http.Handle("/", client.HandleFunc(defaultHandler))
/*
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
*/
err := cgi.Serve(nil)
if err != nil {
panic(err)
}
}
The CGI program expects some variables to be set in order to build the request.
Probably there is some issue with the configuration of your web server in which the variables are either not set correctly or not named correctly.
To verify this:
1) Add this before calling cgi.Serve and you'll see how the right handler is called (otherHandler)
os.Setenv("REQUEST_METHOD", "get")
os.Setenv("SERVER_PROTOCOL", "HTTP/1.1")
os.Setenv("SCRIPT_NAME", "/other")
2) Add this at the beginning of the main to check how the variables are being set by the web server:
fmt.Println(os.Environ())
In that output, look for the CGI meta variables defined in the spec:
http://www.ietf.org/rfc/rfc3875
Look for the section "Request Meta-Variables" in that page, you are probably looking for the SCRIPT_NAME or PATH_INFO variables.
EDIT
From the variable values you pasted below, it seems the issue is that the REQUEST_URI contains an additional path component:
REQUEST_URI=/main.cgi/other
So the easiest fix would be for you to map the routes accordingly:
http.HandleFunc("/main.cgi/other", otherHandler)
http.HandleFunc("/", defaultHandler) // or maybe /main.cgi

How to write test with httprouter

I am writing tests for a simple REST service in GoLang. But, because I am using julienschmidt/httprouter as the routing library. I am struggling on how to write test.
main.go
package main
func main() {
router := httprouter.New()
bookController := controllers.NewBookController()
router.GET("/book/:id", bookController.GetBook)
http.ListenAndServe(":8080", router)
}
controllers
package controllers
type BookController struct {}
func NewBookController *BookController {
return &BookController()
}
func (bc BookController) GetBook(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
fmt.Fprintf(w,"%s", p)
}
My question is: How can test this while GetBook is neither HttpHandler nor HttpHandle
If I use a traditional handler, the test will be easy like this
func TestGetBook(t *testing.T) {
req, _ := http.NewRequest("GET", "/book/sampleid", nil)
rr := httptest.NewRecorder()
handler := controllers.NewBookController().GetBook
handler.ServeHTTP(rr,req)
if status := rr.code; status != http.StatusOK {
t.Errorf("Wrong status")
}
}
The problem is, httprouter is not handler, or handlefunc. So I am stuck now
Just spin up a new router for each test and then register the handler under test, then pass the test request to the router, not the handler, so that the router can parse the path parameters and pass them to the handler.
func TestGetBook(t *testing.T) {
handler := controllers.NewBookController()
router := httprouter.New()
router.GET("/book/:id", handler.GetBook)
req, _ := http.NewRequest("GET", "/book/sampleid", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("Wrong status")
}
}
You need to wrap your handler so that it can be accessed as an http.HandlerFunc:
func TestGetBook(t *testing.T) {
req, _ := http.NewRequest("GET", "/book/sampleid", nil)
rr := httptest.NewRecorder()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
controllers.NewBookController().GetBook(w, r, httprouter.Params{})
})
handler.ServeHTTP(rr,req)
if status := rr.code; status != http.StatusOK {
t.Errorf("Wrong status")
}
}
If your handler requires parameters, you'll either have to parse them manually from the request, or just supply them as the third argument.
u can try this without ServeHTTP
func TestGetBook(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com/foo", nil)
w := httptest.NewRecorder()
controllers.NewBookController().GetBook(w, req, []httprouter.Param{{Key: "id", Value: "101"}})
resp := w.Result()
body, _ := ioutil.ReadAll(resp.Body)
t.Log(resp.StatusCode)
t.Log(resp.Header.Get("Content-Type"))
t.Log(string(body))
}
here is one more good blog and corresponding working code for this:
https://medium.com/#gauravsingharoy/build-your-first-api-server-with-httprouter-in-golang-732b7b01f6ab
https://github.com/gsingharoy/httprouter-tutorial/blob/master/part4/handlers_test.go

Can't read file in GoLang

I am new to GoLang dev and am trying to make a simple web-app. I have been following this tutorial https://www.youtube.com/watch?v=AiRhWG-2nGU.
However I can not even serve the index.html file.
This is my code
func check(e error) {
if e != nil {
fmt.Println(e)
panic(e)
}
}
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Println("Index functoin")
indexHTML, err := ioutil.ReadFile("index.html")
check(err)
fmt.Println(indexHTML)
w.Write(indexHTML)
}
and this is the error produced
Index functoin
open index.html: no such file or directory
My tree structure is like so
BasicWebServer/
BasicWebServer/main.go
BasicWebServer/index.html
BasicWebServer/static/
BasicWebServer/static/index.html
All I want is to be able to serve the index.html since it is a AngularJS app which is already running smoothly. I tried with static files like so
router := NewRouter()
s := http.StripPrefix("/static/", http.FileServer(http.Dir("./static/")))
but it did not work so I am now trying the most basic approach I could think of.
Please help.
Thank you
If you want BasicWebServer/main.go to show BasicWebServer/index.html, not the one inside the static folder, then it seems you didn't properly configure the HTTP server.
Here's your code, with package declaration, imports and a main function working as you expected.
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func check(e error) {
if e != nil {
fmt.Println(e)
panic(e)
}
}
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Println("Index functoin")
indexHTML, err := ioutil.ReadFile("index.html")
check(err)
fmt.Println(indexHTML)
w.Write(indexHTML)
}
func main() {
http.HandleFunc("/", Index)
err := http.ListenAndServe(":8080", nil)
check(err)
}

Auth0 in Go Martini

I'm trying to use Auth0 with Martini in Go. I'm using their examples but I can't seem to get it working no matter what I try.
Here is my code:
package main
import (
"flag"
"github.com/go-martini/martini"
"github.com/martini-contrib/render"
"github.com/auth0/go-jwt-middleware"
"encoding/base64"
"github.com/dgrijalva/jwt-go"
"net/http"
)
func main() {
m := martini.Classic()
port := flag.String("port", "8000", "HTTP Port")
flag.Parse()
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
decoded, err := base64.URLEncoding.DecodeString("<token>")
if err != nil {
return nil, err
}
return decoded, nil
},
})
m.Use(render.Renderer(render.Options{
IndentJSON: true, // Output human readable JSON
}))
m.Get("/", jwtMiddleware.Handler, func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini
res.WriteHeader(200) // HTTP 200
})
// Get the PORT from the environment.
m.RunOnAddr(":" + *port)
}
When I run that, I get a panic that says Value not found for type http.Handler
If I change the jwtMiddleware.Handler to jwtMiddleware.HandlerWithNext, I get a panic for Value not found for type http.HandlerFunc.
Does anyone have any ideas what I'm doing wrong?
To use the jwt-middleware with Martini, you just have to use the CheckJWT method instead of the Handler method.
Check this example: https://github.com/auth0/go-jwt-middleware/blob/master/examples/martini-example/main.go#L27
Let me know if this helps.
Cheers!

Resources