mirror of https://github.com/dirtbags/moth.git
Stubby state, tests passing
This commit is contained in:
parent
8ed07e2e29
commit
5e5592b0d4
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -102,7 +102,6 @@ function renderState(obj) {
|
|||
renderNotices(obj.messages)
|
||||
}
|
||||
|
||||
|
||||
function heartbeat() {
|
||||
let teamId = sessionStorage.id || ""
|
||||
let participantId = sessionStorage.pid
|
||||
|
|
Loading…
Reference in New Issue