From 5e5592b0d44478463b971b8d3f7ee2a9d52b6cfd Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Sat, 29 Feb 2020 18:36:59 -0700 Subject: [PATCH] Stubby state, tests passing --- cmd/mothd/award.go | 15 ++++++++ cmd/mothd/award_test.go | 7 ++++ cmd/mothd/httpd.go | 32 +++++++++++------ cmd/mothd/jsend.go | 51 ++++++++++++++++++++++++++ cmd/mothd/state_test.go | 6 ++-- cmd/mothd/theme_test.go | 27 +++++--------- cmd/mothd/zipfs_test.go | 80 ++++------------------------------------- jsend/jsend.go | 39 -------------------- theme/moth.js | 1 - 9 files changed, 113 insertions(+), 145 deletions(-) create mode 100644 cmd/mothd/jsend.go delete mode 100644 jsend/jsend.go diff --git a/cmd/mothd/award.go b/cmd/mothd/award.go index 8fa7772..b07add6 100644 --- a/cmd/mothd/award.go +++ b/cmd/mothd/award.go @@ -3,6 +3,7 @@ package main import ( "fmt" "strings" + "encoding/json" ) type Award struct { @@ -32,6 +33,20 @@ func (a *Award) String() string { return fmt.Sprintf("%d %s %s %d", a.When, a.TeamId, a.Category, a.Points) } +func (a *Award) MarshalJSON() ([]byte, error) { + if a == nil { + return []byte("null"), nil + } + ao := []interface{}{ + a.When, + a.TeamId, + a.Category, + a.Points, + } + + return json.Marshal(ao) +} + func (a *Award) Same(o *Award) bool { switch { case a.TeamId != o.TeamId: diff --git a/cmd/mothd/award_test.go b/cmd/mothd/award_test.go index 2875557..6c24e21 100644 --- a/cmd/mothd/award_test.go +++ b/cmd/mothd/award_test.go @@ -25,6 +25,13 @@ func TestAward(t *testing.T) { t.Error("String conversion wonky") } + if ja, err := a.MarshalJSON(); err != nil { + t.Error(err) + } else if string(ja) != `[1536958399,"1a2b3c4d","counting",1]` { + t.Error("JSON wrong") + } + + if _, err := ParseAward("bad bad bad 1"); err == nil { t.Error("Not throwing error on bad timestamp") } diff --git a/cmd/mothd/httpd.go b/cmd/mothd/httpd.go index 26faf4f..29cdaa5 100644 --- a/cmd/mothd/httpd.go +++ b/cmd/mothd/httpd.go @@ -4,7 +4,6 @@ import ( "net/http" "log" "strings" - "github.com/dirtbags/moth/jsend" ) type HTTPServer struct { @@ -30,7 +29,6 @@ func NewHTTPServer(base string, theme ThemeProvider, state StateProvider, puzzle return h } - func (h *HTTPServer) Run(bindStr string) { log.Printf("Listening on %s", bindStr) log.Fatal(http.ListenAndServe(bindStr, h)) @@ -64,10 +62,6 @@ func (h *HTTPServer) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) { func (h *HTTPServer) ThemeHandler(w http.ResponseWriter, req *http.Request) { path := req.URL.Path - if strings.Contains(path, "..") { - http.Error(w, "Invalid URL path", http.StatusBadRequest) - return - } if path == "/" { path = "/index.html" } @@ -84,17 +78,35 @@ func (h *HTTPServer) ThemeHandler(w http.ResponseWriter, req *http.Request) { func (h *HTTPServer) StateHandler(w http.ResponseWriter, req *http.Request) { - jsend.Write(w, jsend.Fail, "unimplemented", "I haven't written this yet") + var state struct { + Config struct { + Devel bool `json:"devel"` + } `json:"config"` + Messages string `json:"messages"` + Teams []string `json:"teams"` + Points []Award `json:"points"` + Puzzles map[string][]int `json:"puzzles"` + } + state.Messages = "Hello world" + state.Teams = []string{"goobers"} + state.Points = []Award{{0, "0", "sequence", 1}} + state.Puzzles = map[string][]int{"sequence": {1}} + + JSONWrite(w, state) } func (h *HTTPServer) RegisterHandler(w http.ResponseWriter, req *http.Request) { - jsend.Write(w, jsend.Fail, "unimplemented", "I haven't written this yet") + JSendf(w, JSendFail, "unimplemented", "I haven't written this yet") } func (h *HTTPServer) AnswerHandler(w http.ResponseWriter, req *http.Request) { - jsend.Write(w, jsend.Fail, "unimplemented", "I haven't written this yet") + JSendf(w, JSendFail, "unimplemented", "I haven't written this yet") } func (h *HTTPServer) ContentHandler(w http.ResponseWriter, req *http.Request) { - jsend.Write(w, jsend.Fail, "unimplemented", "I haven't written this yet") + path := req.URL.Path + if path == "/" { + path = "/puzzle.json" + } + JSendf(w, JSendFail, "unimplemented", "I haven't written this yet") } diff --git a/cmd/mothd/jsend.go b/cmd/mothd/jsend.go new file mode 100644 index 0000000..d5036d6 --- /dev/null +++ b/cmd/mothd/jsend.go @@ -0,0 +1,51 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// This provides a JSend function for MOTH +// https://github.com/omniti-labs/jsend + +const ( + JSendSuccess = "success" + JSendFail = "fail" + JSendError = "error" +) + +func JSONWrite(w http.ResponseWriter, data interface{}) { + respBytes, err := json.Marshal(data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(respBytes))) + w.WriteHeader(http.StatusOK) // RFC2616 makes it pretty clear that 4xx codes are for the user-agent + w.Write(respBytes) +} + +func JSend(w http.ResponseWriter, status string, data interface{}) { + resp := struct{ + Status string `json:"status"` + Data interface{} `json:"data"` + }{} + resp.Status = status + resp.Data = data + + JSONWrite(w, resp) +} + +func JSendf(w http.ResponseWriter, status, short string, format string, a ...interface{}) { + data := struct{ + Short string `json:"short"` + Description string `json:"description"` + }{} + data.Short = short + data.Description = fmt.Sprintf(format, a...) + + JSend(w, status, data) +} diff --git a/cmd/mothd/state_test.go b/cmd/mothd/state_test.go index e9f74c4..f2d49e4 100644 --- a/cmd/mothd/state_test.go +++ b/cmd/mothd/state_test.go @@ -18,7 +18,7 @@ func TestState(t *testing.T) { } s := NewState(fs) - s.Cleanup() + s.Update() pl := s.PointsLog() if len(pl) != 0 { @@ -51,7 +51,7 @@ func TestState(t *testing.T) { category := "poot" points := 3928 s.AwardPoints(teamId, category, points) - s.Cleanup() + s.Update() pl = s.PointsLog() if len(pl) != 1 { @@ -61,7 +61,7 @@ func TestState(t *testing.T) { } fs.Remove("initialized") - s.Cleanup() + s.Update() pl = s.PointsLog() if len(pl) != 0 { diff --git a/cmd/mothd/theme_test.go b/cmd/mothd/theme_test.go index b64a2fe..28f3fe9 100644 --- a/cmd/mothd/theme_test.go +++ b/cmd/mothd/theme_test.go @@ -2,31 +2,22 @@ package main import ( "github.com/spf13/afero" - "net/http" - "net/http/httptest" "testing" + "io/ioutil" ) func TestTheme(t *testing.T) { fs := new(afero.MemMapFs) - afero.WriteFile(fs, "/index.html", []byte("index"), 0644) - afero.WriteFile(fs, "/moo.html", []byte("moo"), 0644) + index := "this is the index" + afero.WriteFile(fs, "/index.html", []byte(index), 0644) s := NewTheme(fs) - req, err := http.NewRequest("GET", "/", nil) - if err != nil { - t.Fatal(err) - } - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(s.staticHandler) - handler.ServeHTTP(rr, req) - if rr.Code != http.StatusOK { - t.Errorf("Handler returned wrong code: %v", rr.Code) - } - - if rr.Body.String() != "index" { - t.Errorf("Handler returned wrong content: %v", rr.Body.String()) + if f, err := s.Open("/index.html"); err != nil { + t.Error(err) + } else if buf, err := ioutil.ReadAll(f); err != nil { + t.Error(err) + } else if string(buf) != index { + t.Error("Read wrong value from index") } } diff --git a/cmd/mothd/zipfs_test.go b/cmd/mothd/zipfs_test.go index b6898f0..8b42169 100644 --- a/cmd/mothd/zipfs_test.go +++ b/cmd/mothd/zipfs_test.go @@ -4,88 +4,20 @@ import ( "archive/zip" "fmt" "io" - "io/ioutil" - "math/rand" - "os" + "github.com/spf13/afero" "testing" - "time" ) -func TestZipPerformance(t *testing.T) { - // I get 4.8s for 10,000 reads - if os.Getenv("BENCHMARK") == "" { - return - } - - rng := rand.New(rand.NewSource(rand.Int63())) - - tf, err := ioutil.TempFile("", "zipfs") - if err != nil { - t.Error(err) - return - } - defer os.Remove(tf.Name()) - - w := zip.NewWriter(tf) - for i := 0; i < 100; i += 1 { - fsize := 1000 - switch { - case i % 10 == 0: - fsize = 400000 - case i % 20 == 6: - fsize = 5000000 - case i == 80: - fsize = 1000000000 - } - - f, err := w.Create(fmt.Sprintf("%d.bin", i)) - if err != nil { - t.Fatal(err) - return - } - if _, err := io.CopyN(f, rng, int64(fsize)); err != nil { - t.Error(err) - } - } - w.Close() - - tfsize, err := tf.Seek(0, 2) - if err != nil { - t.Fatal(err) - } - - startTime := time.Now() - nReads := 10000 - for i := 0; i < 10000; i += 1 { - r, err := zip.NewReader(tf, tfsize) - if err != nil { - t.Error(err) - return - } - filenum := rng.Intn(len(r.File)) - f, err := r.File[filenum].Open() - if err != nil { - t.Error(err) - continue - } - buf, err := ioutil.ReadAll(f) - if err != nil { - t.Error(err) - } - t.Log("Read file of size", len(buf)) - f.Close() - } - t.Log(nReads, "reads took", time.Since(startTime)) - t.Error("moo") -} func TestZipfs(t *testing.T) { - tf, err := ioutil.TempFile("", "zipfs") + fs := new(afero.MemMapFs) + + tf, err := fs.Create("/test.zip") if err != nil { t.Error(err) return } - defer os.Remove(tf.Name()) + defer fs.Remove(tf.Name()) w := zip.NewWriter(tf) f, err := w.Create("moo.txt") @@ -105,7 +37,7 @@ func TestZipfs(t *testing.T) { tf.Close() // Now read it in - mb, err := OpenZipfs(tf.Name()) + mb, err := OpenZipfs(fs, tf.Name()) if err != nil { t.Error(err) return diff --git a/jsend/jsend.go b/jsend/jsend.go deleted file mode 100644 index 6027d7f..0000000 --- a/jsend/jsend.go +++ /dev/null @@ -1,39 +0,0 @@ -package jsend - -import ( - "encoding/json" - "fmt" - "net/http" -) - -// This provides a JSend function for MOTH -// https://github.com/omniti-labs/jsend - -const ( - Success = "success" - Fail = "fail" - Error = "error" -) - -func Write(w http.ResponseWriter, status, short string, format string, a ...interface{}) { - resp := struct{ - Status string `json:"status"` - Data struct { - Short string `json:"short"` - Description string `json:"description"` - } `json:"data"` - }{} - resp.Status = status - resp.Data.Short = short - resp.Data.Description = fmt.Sprintf(format, a...) - - respBytes, err := json.Marshal(resp) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) // RFC2616 makes it pretty clear that 4xx codes are for the user-agent - w.Write(respBytes) -} diff --git a/theme/moth.js b/theme/moth.js index 8f143b8..b29ac42 100644 --- a/theme/moth.js +++ b/theme/moth.js @@ -102,7 +102,6 @@ function renderState(obj) { renderNotices(obj.messages) } - function heartbeat() { let teamId = sessionStorage.id || "" let participantId = sessionStorage.pid