I have a HTTP handler that sets a context deadline on each request:
func submitHandler(stream chan data) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel()
// read request body, etc.
select {
case stream <- req:
w.WriteHeader(http.StatusNoContent)
case <-ctx.Done():
err := ctx.Err()
if err == context.DeadlineExceeded {
w.WriteHeader(http.StatusRequestTimeout)
}
log.Printf("context done: %v", err)
}
}
}
I am easily able to test the http.StatusNoContent header, but I am unsure about how to test the <-ctx.Done() case in the select statement.
In my test case I have built a mock context.Context and passed it to the req.WithContext() method on my mock http.Request, however, the status code returned is always http.StatusNoContent which leads me to believe the select statement is always falling into the first case in my test.
type mockContext struct{}
func (ctx mockContext) Deadline() (deadline time.Time, ok bool) {
return deadline, ok
}
func (ctx mockContext) Done() <-chan struct{} {
ch := make(chan struct{})
close(ch)
return ch
}
func (ctx mockContext) Err() error {
return context.DeadlineExceeded
}
func (ctx mockContext) Value(key interface{}) interface{} {
return nil
}
func TestHandler(t *testing.T) {
stream := make(chan data, 1)
defer close(stream)
handler := submitHandler(stream)
req, err := http.NewRequest(http.MethodPost, "/submit", nil)
if err != nil {
t.Fatal(err)
}
req = req.WithContext(mockContext{})
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusRequestTimeout {
t.Errorf("expected status code: %d, got: %d", http.StatusRequestTimeout, rec.Code)
}
}
How could I mock the context deadline has exceeded?