@@ -7,12 +7,17 @@ package main
77import (
88 "bytes"
99 "context"
10+ "encoding/json"
1011 "fmt"
1112 "io/ioutil"
1213 "net/http"
1314 "net/http/httptest"
1415 "os"
16+ "sync"
1517 "testing"
18+
19+ "github.com/bradfitz/gomemcache/memcache"
20+ "github.com/google/go-cmp/cmp"
1621)
1722
1823type testLogger struct {
@@ -166,6 +171,7 @@ func TestCommandHandler(t *testing.T) {
166171 // Should we verify that s.log.Errorf was called
167172 // instead of just printing or failing the test?
168173 s .log = newStdLogger ()
174+ s .cache = new (inMemCache )
169175 return nil
170176 })
171177 if err != nil {
@@ -193,63 +199,103 @@ func TestCommandHandler(t *testing.T) {
193199 if r .Body == "allocate-memory-compile-error" {
194200 return & response {Errors : "cannot allocate memory" }, nil
195201 }
202+ if r .Body == "build-timeout-error" {
203+ return & response {Errors : goBuildTimeoutError }, nil
204+ }
205+ if r .Body == "run-timeout-error" {
206+ return & response {Errors : runTimeoutError }, nil
207+ }
196208 resp := & response {Events : []Event {{r .Body , "stdout" , 0 }}}
197209 return resp , nil
198210 })
199211
200212 testCases := []struct {
201- desc string
202- method string
203- statusCode int
204- reqBody []byte
205- respBody []byte
213+ desc string
214+ method string
215+ statusCode int
216+ reqBody []byte
217+ respBody []byte
218+ shouldCache bool
206219 }{
207- {"OPTIONS request" , http .MethodOptions , http .StatusOK , nil , nil },
208- {"GET request" , http .MethodGet , http .StatusBadRequest , nil , nil },
209- {"Empty POST" , http .MethodPost , http .StatusBadRequest , nil , nil },
210- {"Failed cmdFunc" , http .MethodPost , http .StatusInternalServerError , []byte (`{"Body":"fail"}` ), nil },
220+ {"OPTIONS request" , http .MethodOptions , http .StatusOK , nil , nil , false },
221+ {"GET request" , http .MethodGet , http .StatusBadRequest , nil , nil , false },
222+ {"Empty POST" , http .MethodPost , http .StatusBadRequest , nil , nil , false },
223+ {"Failed cmdFunc" , http .MethodPost , http .StatusInternalServerError , []byte (`{"Body":"fail"}` ), nil , false },
211224 {"Standard flow" , http .MethodPost , http .StatusOK ,
212225 []byte (`{"Body":"ok"}` ),
213226 []byte (`{"Errors":"","Events":[{"Message":"ok","Kind":"stdout","Delay":0}],"Status":0,"IsTest":false,"TestsFailed":0}
214227` ),
215- },
216- {"Errors in response" , http .MethodPost , http .StatusOK ,
228+ true },
229+ {"Cache-able Errors in response" , http .MethodPost , http .StatusOK ,
217230 []byte (`{"Body":"error"}` ),
218231 []byte (`{"Errors":"errors","Events":null,"Status":0,"IsTest":false,"TestsFailed":0}
219232` ),
220- },
233+ true },
221234 {"Out of memory error in response body event message" , http .MethodPost , http .StatusInternalServerError ,
222- []byte (`{"Body":"oom-error"}` ), nil },
235+ []byte (`{"Body":"oom-error"}` ), nil , false },
223236 {"Cannot allocate memory error in response body event message" , http .MethodPost , http .StatusInternalServerError ,
224- []byte (`{"Body":"allocate-memory-error"}` ), nil },
237+ []byte (`{"Body":"allocate-memory-error"}` ), nil , false },
225238 {"Out of memory error in response errors" , http .MethodPost , http .StatusInternalServerError ,
226- []byte (`{"Body":"oom-compile-error"}` ), nil },
239+ []byte (`{"Body":"oom-compile-error"}` ), nil , false },
227240 {"Cannot allocate memory error in response errors" , http .MethodPost , http .StatusInternalServerError ,
228- []byte (`{"Body":"allocate-memory-compile-error"}` ), nil },
241+ []byte (`{"Body":"allocate-memory-compile-error"}` ), nil , false },
242+ {
243+ desc : "Build timeout error" ,
244+ method : http .MethodPost ,
245+ statusCode : http .StatusOK ,
246+ reqBody : []byte (`{"Body":"build-timeout-error"}` ),
247+ respBody : []byte (fmt .Sprintln (`{"Errors":"timeout running go build","Events":null,"Status":0,"IsTest":false,"TestsFailed":0}` )),
248+ },
249+ {
250+ desc : "Run timeout error" ,
251+ method : http .MethodPost ,
252+ statusCode : http .StatusOK ,
253+ reqBody : []byte (`{"Body":"run-timeout-error"}` ),
254+ respBody : []byte (fmt .Sprintln (`{"Errors":"timeout running program","Events":null,"Status":0,"IsTest":false,"TestsFailed":0}` )),
255+ },
229256 }
230257
231258 for _ , tc := range testCases {
232- req := httptest .NewRequest (tc .method , "/compile" , bytes .NewReader (tc .reqBody ))
233- w := httptest .NewRecorder ()
234- testHandler (w , req )
235- resp := w .Result ()
236- corsHeader := "Access-Control-Allow-Origin"
237- if got , want := resp .Header .Get (corsHeader ), "*" ; got != want {
238- t .Errorf ("%s: %q header: got %q; want %q" , tc .desc , corsHeader , got , want )
239- }
240- if got , want := resp .StatusCode , tc .statusCode ; got != want {
241- t .Errorf ("%s: got unexpected status code %d; want %d" , tc .desc , got , want )
242- }
243- if tc .respBody != nil {
244- defer resp .Body .Close ()
245- b , err := ioutil .ReadAll (resp .Body )
246- if err != nil {
247- t .Errorf ("%s: ioutil.ReadAll(resp.Body): %v" , tc .desc , err )
259+ t .Run (tc .desc , func (t * testing.T ) {
260+ req := httptest .NewRequest (tc .method , "/compile" , bytes .NewReader (tc .reqBody ))
261+ w := httptest .NewRecorder ()
262+ testHandler (w , req )
263+ resp := w .Result ()
264+ corsHeader := "Access-Control-Allow-Origin"
265+ if got , want := resp .Header .Get (corsHeader ), "*" ; got != want {
266+ t .Errorf ("%s: %q header: got %q; want %q" , tc .desc , corsHeader , got , want )
248267 }
249- if ! bytes . Equal ( b , tc .respBody ) {
250- t .Errorf ("%s: got unexpected body %q ; want %q " , tc .desc , b , tc . respBody )
268+ if got , want := resp . StatusCode , tc .statusCode ; got != want {
269+ t .Errorf ("%s: got unexpected status code %d ; want %d " , tc .desc , got , want )
251270 }
252- }
271+ if tc .respBody != nil {
272+ defer resp .Body .Close ()
273+ b , err := ioutil .ReadAll (resp .Body )
274+ if err != nil {
275+ t .Errorf ("%s: ioutil.ReadAll(resp.Body): %v" , tc .desc , err )
276+ }
277+ if ! bytes .Equal (b , tc .respBody ) {
278+ t .Errorf ("%s: got unexpected body %q; want %q" , tc .desc , b , tc .respBody )
279+ }
280+ }
281+
282+ // Test caching semantics.
283+ sbreq := new (request ) // A sandbox request, used in the cache key.
284+ json .Unmarshal (tc .reqBody , sbreq ) // Ignore errors, request may be empty.
285+ gotCache := new (response )
286+ if err := s .cache .Get (cacheKey ("test" , sbreq .Body ), gotCache ); (err == nil ) != tc .shouldCache {
287+ t .Errorf ("s.cache.Get(%q, %v) = %v, shouldCache: %v" , cacheKey ("test" , sbreq .Body ), gotCache , err , tc .shouldCache )
288+ }
289+ wantCache := new (response )
290+ if tc .shouldCache {
291+ if err := json .Unmarshal (tc .respBody , wantCache ); err != nil {
292+ t .Errorf ("json.Unmarshal(%q, %v) = %v, wanted no error" , tc .respBody , wantCache , err )
293+ }
294+ }
295+ if diff := cmp .Diff (wantCache , gotCache ); diff != "" {
296+ t .Errorf ("s.Cache.Get(%q) mismatch (-want +got):\n %s" , cacheKey ("test" , sbreq .Body ), diff )
297+ }
298+ })
253299 }
254300}
255301
@@ -315,3 +361,36 @@ func TestPlaygroundGoproxy(t *testing.T) {
315361 })
316362 }
317363}
364+
365+ // inMemCache is a responseCache backed by a map. It is only suitable for testing.
366+ type inMemCache struct {
367+ l sync.Mutex
368+ m map [string ]* response
369+ }
370+
371+ // Set implements the responseCache interface.
372+ // Set stores a *response in the cache. It panics for other types to ensure test failure.
373+ func (i * inMemCache ) Set (key string , v interface {}) error {
374+ i .l .Lock ()
375+ defer i .l .Unlock ()
376+ if i .m == nil {
377+ i .m = make (map [string ]* response )
378+ }
379+ i .m [key ] = v .(* response )
380+ return nil
381+ }
382+
383+ // Get implements the responseCache interface.
384+ // Get fetches a *response from the cache, or returns a memcache.ErrcacheMiss.
385+ // It panics for other types to ensure test failure.
386+ func (i * inMemCache ) Get (key string , v interface {}) error {
387+ i .l .Lock ()
388+ defer i .l .Unlock ()
389+ target := v .(* response )
390+ got , ok := i .m [key ]
391+ if ! ok {
392+ return memcache .ErrCacheMiss
393+ }
394+ * target = * got
395+ return nil
396+ }
0 commit comments