Stubby state, tests passing

This commit is contained in:
Neale Pickett 2020-02-29 18:36:59 -07:00
parent 8ed07e2e29
commit 5e5592b0d4
9 changed files with 113 additions and 145 deletions

View File

@ -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:

View File

@ -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")
}

View File

@ -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")
}

51
cmd/mothd/jsend.go Normal file
View File

@ -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)
}

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -102,7 +102,6 @@ function renderState(obj) {
renderNotices(obj.messages)
}
function heartbeat() {
let teamId = sessionStorage.id || ""
let participantId = sessionStorage.pid