Compare commits

...
5 Commits
Author SHA1 Message Date
vinchent 7a32f26bd5 context: implement a spy response writer to check if anything is written 2024-09-21 20:46:07 +02:00
vinchent c5582275e7 context: realistic usage of context.
Use a go routine for the work logic. Here sleep and append to string.
Inside the go routine, select for ctx.Done(). If happens, just stop and
return.

In the outside function, select for ctx.Done() too. If it happens, that
means the work logic has not finished before canceled.
2024-09-21 20:35:43 +02:00
vinchent c0db9ab22b context: refactorize assert functions 2024-09-21 20:26:22 +02:00
vinchent 0b181ccf0f context: setup timeout context 2024-09-21 20:20:15 +02:00
vinchent d49f952035 context: add basic context test 2024-09-21 20:07:09 +02:00
2 changed files with 121 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
package context
import (
"context"
"fmt"
"log"
"net/http"
)
type Store interface {
Fetch(ctx context.Context) (string, error)
}
func Server(store Store) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := store.Fetch(r.Context())
if err != nil {
log.Println(err)
return
}
fmt.Fprint(w, data)
}
}
+98
View File
@@ -0,0 +1,98 @@
package context
import (
"context"
"errors"
"log"
"net/http"
"net/http/httptest"
"testing"
"time"
)
type SpyStore struct {
response string
t *testing.T
}
func (s *SpyStore) Fetch(ctx context.Context) (string, error) {
data := make(chan string, 1)
go func() {
var result string
for _, c := range s.response {
select {
case <-ctx.Done():
log.Println("spy store got cancelled")
return
default:
time.Sleep(10 * time.Millisecond)
result += string(c)
}
}
data <- result
}()
select {
case <-ctx.Done():
return "", ctx.Err()
case res := <-data:
return res, nil
}
}
type SpyResponseWriter struct {
written bool
}
func (s *SpyResponseWriter) Header() http.Header {
s.written = true
return nil
}
func (s *SpyResponseWriter) Write([]byte) (int, error) {
s.written = true
return 0, errors.New("not implemented")
}
func (s *SpyResponseWriter) WriteHeader(statusCode int) {
s.written = true
}
func TestServer(t *testing.T) {
t.Run("basic get store", func(t *testing.T) {
data := "hello, world"
store := &SpyStore{response: data, t: t}
srv := Server(store)
request := httptest.NewRequest(http.MethodGet, "/", nil)
response := httptest.NewRecorder()
srv.ServeHTTP(response, request)
if response.Body.String() != data {
t.Errorf(`got "%s", want "%s"`, response.Body.String(), data)
}
})
t.Run("tells store to cancel work if request is cancelled", func(t *testing.T) {
data := "hello, world"
store := &SpyStore{response: data, t: t}
srv := Server(store)
request := httptest.NewRequest(http.MethodGet, "/", nil)
cancellingCtx, cancel := context.WithCancel(request.Context())
// Wait 5ms to call cancel
time.AfterFunc(5*time.Millisecond, cancel)
request = request.WithContext(cancellingCtx)
response := &SpyResponseWriter{}
srv.ServeHTTP(response, request)
if response.written {
t.Error("a response should not have been written")
}
})
}