3

I have a Go app that use Gin gonic and a Nginx reverse proxy that send trafic to another app on domain.com and send all the *.domain.com subdomains traffic directly to my go app.

My Go app then has a middleware that will read the hostname that nginx passes to it from Context and allow my handlers to know what subdomain is being request and return the proper data and cookies for said subdomain.

It's a pretty simple setup and it seems to work fine from my test in postman as all my routes are the same across all my subdomains so this way i can only use one router for all of them instead of one router per subodmain.

Now my big problem come when i'm trying to do end to end testing.

I'm setting up my test like this :

  router := initRouter()
  w := httptest.NewRecorder()
  req, _ := http.NewRequest("POST", "/api/login", bytes.NewBuffer(jsonLogin))
  req.Header.Set("Content-Type", "application/json")
  router.ServeHTTP(w, req)
  assert.Equal(t, 200, w.Code)

with initRouter() returning a gin engine with all my routes and middlewares loaded and the rest as a basic test setup.

Obviously the test will fail as the gin Context won't ever receive a subdomain from context and act as if everything is coming from localhost:8000.

Is there a way to either :

  • "Mock" a subdomain so that the router think the call is coming from foo.localhost.com instead of localhost

  • Setup my test suit so that the test request are routed thought nginx.. i'd prefer solution 1 as this would be a mess to setup / maintain.

Edit :

As per the httptest doc i've tried to hard code foo.localhost as the param of the NewRequest but it doesn't really behave as i need it to behave :

NewRequest returns a new incoming server Request, suitable for passing to an http.Handler for testing.

The target is the RFC 7230 "request-target": it may be either a path or an absolute URL. If target is an absolute URL, the host name from the URL is used. Otherwise, "example.com" is used.

When hardcoding http://foo.localhost.com/api/login or foo.localhost.com/api/login as the request target it directly passes it to my router under "foo.localhost.com/api/login" while nginx would just hit the /api/login directly and parse from c.Request.Host

Edit 2:

I'm currently exploring setting the host manually using :

req.Header.Set("Host", "foo.localhost")

estabanf
  • 33
  • 4
  • Hi, welcome to StackOverflow. Please can you revise your post a little. The title needs to be a question - I don’t know the context well enough to frame it for you. – Tim Jul 06 '20 at 09:44
  • FTR: you can hardly call this an end-to-end test if you're not going through the nginx. – Peter Jul 06 '20 at 10:07
  • True. i guess it's end to end / integration test as far as the scope of my go app is concerned ? – estabanf Jul 06 '20 at 10:21

1 Answers1

2

The request returned by http.NewRequest isn't suitable for passing directly to ServeHTTP. Use one returned by httptest.NewRequest instead.

Simply set the Host field directly:

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestHelloWorld(t *testing.T) {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if r.Host != "foobar" {
            t.Errorf("Host is %q, want foobar", r.Host)
        }
    })

    w := httptest.NewRecorder()
    r := httptest.NewRequest("GET", "/api/login", nil)
    r.Host = "foobar"

    mux.ServeHTTP(w, r)
}
Peter
  • 29,454
  • 5
  • 48
  • 60