mirror of https://github.com/dirtbags/moth.git
Loads more tests!
This commit is contained in:
parent
b1b7fec6d1
commit
8f85b02935
|
@ -140,7 +140,7 @@ func (h *HTTPServer) ContentHandler(mh MothRequestHandler, w http.ResponseWriter
|
|||
filename := parts[2]
|
||||
|
||||
if filename == "" {
|
||||
filename = "puzzles.json"
|
||||
filename = "puzzle.json"
|
||||
}
|
||||
|
||||
points, _ := strconv.Atoi(pointsStr)
|
||||
|
|
|
@ -1,10 +1,125 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestHttpd(t *testing.T) {
|
||||
//emptyBody := bytes.NewReader([]byte{})
|
||||
//request := httptest.NewRequest("GET", "/", emptyBody)
|
||||
const TestParticipantID = "shipox"
|
||||
|
||||
func (hs *HTTPServer) TestRequest(path string, args map[string]string) *httptest.ResponseRecorder {
|
||||
vals := url.Values{}
|
||||
vals.Set("pid", TestParticipantID)
|
||||
vals.Set("id", TestTeamID)
|
||||
if args != nil {
|
||||
for k, v := range args {
|
||||
vals.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(
|
||||
"GET",
|
||||
fmt.Sprintf("%s?%s", path, vals.Encode()),
|
||||
bytes.NewReader([]byte{}),
|
||||
)
|
||||
hs.ServeHTTP(recorder, request)
|
||||
return recorder
|
||||
}
|
||||
|
||||
func TestHttpd(t *testing.T) {
|
||||
hs := NewHTTPServer("/", NewTestServer())
|
||||
|
||||
if r := hs.TestRequest("/", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/index.html", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
if r := hs.TestRequest("/rolodex.html", nil); r.Result().StatusCode != 404 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"Config":{"Devel":false},"Messages":"messages.html","TeamNames":{"self":""},"PointsLog":[],"Puzzles":{"pategory":[1]}}` {
|
||||
t.Error("Unexpected state")
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/register", map[string]string{"id": "bad team id", "name": "GoTeam"}); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"status":"fail","data":{"short":"not registered","description":"Team ID not found in list of valid Team IDs"}}` {
|
||||
t.Error("Register bad team ID failed")
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/register", map[string]string{"name": "GoTeam"}); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"status":"success","data":{"short":"registered","description":"Team ID registered"}}` {
|
||||
t.Error("Register failed")
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"Config":{"Devel":false},"Messages":"messages.html","TeamNames":{"self":"GoTeam"},"PointsLog":[],"Puzzles":{"pategory":[1]}}` {
|
||||
t.Error("Unexpected state", r.Body.String())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/content/pategory", nil); r.Result().StatusCode != 404 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/content/pategory/1/not-here", nil); r.Result().StatusCode != 404 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/content/pategory/2/moo.txt", nil); r.Result().StatusCode != 404 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/content/pategory/1/", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/content/pategory/1/moo.txt", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `moo` {
|
||||
t.Error("Unexpected body", r.Body.String())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/answer", map[string]string{"cat": "pategory", "points": "1", "answer": "moo"}); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"status":"fail","data":{"short":"not accepted","description":"Invalid answer"}}` {
|
||||
t.Error("Unexpected body", r.Body.String())
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/answer", map[string]string{"cat": "pategory", "points": "1", "answer": "answer123"}); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"status":"success","data":{"short":"accepted","description":"1 points awarded in pategory"}}` {
|
||||
t.Error("Unexpected body", r.Body.String())
|
||||
}
|
||||
|
||||
time.Sleep(TestMaintenanceInterval)
|
||||
|
||||
state := StateExport{}
|
||||
if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if err := json.Unmarshal(r.Body.Bytes(), &state); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(state.PointsLog) != 1 {
|
||||
t.Error("Points log wrong length")
|
||||
} else if len(state.Puzzles["pategory"]) != 2 {
|
||||
t.Error("Didn't unlock next puzzle")
|
||||
}
|
||||
|
||||
if r := hs.TestRequest("/answer", map[string]string{"cat": "pategory", "points": "1", "answer": "answer123"}); r.Result().StatusCode != 200 {
|
||||
t.Error(r.Result())
|
||||
} else if r.Body.String() != `{"status":"fail","data":{"short":"not accepted","description":"Points already awarded to this team in this category"}}` {
|
||||
t.Error("Unexpected body", r.Body.String())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,12 @@ import (
|
|||
var testFiles = []struct {
|
||||
Name, Body string
|
||||
}{
|
||||
{"puzzles.txt", "1"},
|
||||
{"answers.txt", "1 answer123\n1 answer456\n"},
|
||||
{"puzzles.txt", "1\n2\n"},
|
||||
{"answers.txt", "1 answer123\n1 answer456\n2 wat\n"},
|
||||
{"content/1/puzzle.json", `{"name": "moo"}`},
|
||||
{"content/1/moo.txt", `moo`},
|
||||
{"content/2/puzzle.json", `{}`},
|
||||
{"content/2/moo.txt", `moo`},
|
||||
}
|
||||
|
||||
func (m *Mothballs) createMothball(cat string) {
|
||||
|
@ -58,6 +60,16 @@ func TestMothballs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if f, _, err := m.Open("nealegory", 1, "puzzle.json"); err == nil {
|
||||
f.Close()
|
||||
t.Error("You can't open a puzzle in a nealegory, that doesn't even rhyme!")
|
||||
}
|
||||
|
||||
if f, _, err := m.Open("pategory", 1, "bozo"); err == nil {
|
||||
f.Close()
|
||||
t.Error("This file shouldn't exist")
|
||||
}
|
||||
|
||||
if err := m.CheckAnswer("pategory", 1, "answer"); err == nil {
|
||||
t.Error("Wrong answer marked right")
|
||||
}
|
||||
|
@ -67,6 +79,11 @@ func TestMothballs(t *testing.T) {
|
|||
if err := m.CheckAnswer("pategory", 1, "answer456"); err != nil {
|
||||
t.Error("Right answer marked wrong", err)
|
||||
}
|
||||
if err := m.CheckAnswer("nealegory", 1, "moo"); err == nil {
|
||||
t.Error("Checking answer in non-existent category should fail")
|
||||
} else if err.Error() != "No such category: nealegory" {
|
||||
t.Error("Wrong error message")
|
||||
}
|
||||
|
||||
m.createMothball("test2")
|
||||
m.Fs.Remove("pategory.mb")
|
||||
|
|
|
@ -101,9 +101,7 @@ type MothRequestHandler struct {
|
|||
// PuzzlesOpen opens a file associated with a puzzle.
|
||||
func (mh *MothRequestHandler) PuzzlesOpen(cat string, points int, path string) (ReadSeekCloser, time.Time, error) {
|
||||
export := mh.ExportState()
|
||||
fmt.Println(export.Puzzles)
|
||||
for _, p := range export.Puzzles[cat] {
|
||||
fmt.Println(points, p)
|
||||
if p == points {
|
||||
return mh.Puzzles.Open(cat, points, path)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ func NewTestServer() *MothServer {
|
|||
go state.Maintain(TestMaintenanceInterval)
|
||||
|
||||
theme := NewTestTheme()
|
||||
afero.WriteFile(theme.Fs, "index.html", []byte("index.html"), 0644)
|
||||
afero.WriteFile(theme.Fs, "/index.html", []byte("index.html"), 0644)
|
||||
go theme.Maintain(TestMaintenanceInterval)
|
||||
|
||||
return NewMothServer(puzzles, theme, state)
|
||||
|
@ -37,7 +37,7 @@ func TestServer(t *testing.T) {
|
|||
if err := handler.Register(teamName); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if r, _, err := handler.ThemeOpen("index.html"); err != nil {
|
||||
if r, _, err := handler.ThemeOpen("/index.html"); err != nil {
|
||||
t.Error(err)
|
||||
} else if contents, err := ioutil.ReadAll(r); err != nil {
|
||||
t.Error(err)
|
||||
|
@ -68,9 +68,23 @@ func TestServer(t *testing.T) {
|
|||
if r, _, err := handler.PuzzlesOpen("pategory", 1, "moo.txt"); err != nil {
|
||||
t.Error(err)
|
||||
} else if contents, err := ioutil.ReadAll(r); err != nil {
|
||||
r.Close()
|
||||
t.Error(err)
|
||||
} else if string(contents) != "moo" {
|
||||
r.Close()
|
||||
t.Error("moo.txt has wrong contents", contents)
|
||||
} else {
|
||||
r.Close()
|
||||
}
|
||||
|
||||
if r, _, err := handler.PuzzlesOpen("pategory", 2, "puzzles.json"); err == nil {
|
||||
t.Error("Opening locked puzzle shouldn't work")
|
||||
r.Close()
|
||||
}
|
||||
|
||||
if r, _, err := handler.PuzzlesOpen("pategory", 20, "puzzles.json"); err == nil {
|
||||
t.Error("Opening non-existent puzzle shouldn't work")
|
||||
r.Close()
|
||||
}
|
||||
|
||||
if err := handler.CheckAnswer("pategory", 1, "answer123"); err != nil {
|
||||
|
@ -83,4 +97,6 @@ func TestServer(t *testing.T) {
|
|||
if len(es.PointsLog) != 1 {
|
||||
t.Error("I didn't get my points!")
|
||||
}
|
||||
|
||||
// BUG(neale): We aren't currently testing the various ways to disable the server
|
||||
}
|
||||
|
|
|
@ -49,18 +49,12 @@ func NewState(fs afero.Fs) *State {
|
|||
|
||||
// updateEnabled checks a few things to see if this state directory is "enabled".
|
||||
func (s *State) updateEnabled() {
|
||||
if _, err := s.Stat("enabled"); os.IsNotExist(err) {
|
||||
s.Enabled = false
|
||||
log.Println("Suspended: enabled file missing")
|
||||
return
|
||||
}
|
||||
|
||||
nextEnabled := true
|
||||
untilFile, err := s.Open("hours")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
why := "`state/enabled` present, `state/hours` missing"
|
||||
|
||||
if untilFile, err := s.Open("hours"); err == nil {
|
||||
defer untilFile.Close()
|
||||
why = "`state/hours` present"
|
||||
|
||||
scanner := bufio.NewScanner(untilFile)
|
||||
for scanner.Scan() {
|
||||
|
@ -92,9 +86,22 @@ func (s *State) updateEnabled() {
|
|||
nextEnabled = thisEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := s.Stat("enabled"); os.IsNotExist(err) {
|
||||
dirs, _ := afero.ReadDir(s, ".")
|
||||
for _, dir := range dirs {
|
||||
log.Println(dir.Name())
|
||||
}
|
||||
|
||||
log.Print(s, err)
|
||||
nextEnabled = false
|
||||
why = "`state/enabled` missing"
|
||||
}
|
||||
|
||||
if nextEnabled != s.Enabled {
|
||||
s.Enabled = nextEnabled
|
||||
log.Println("Setting enabled to", s.Enabled, "based on hours file")
|
||||
log.Printf("Setting enabled=%v: %s", s.Enabled, why)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,28 +122,33 @@ func (s *State) TeamName(teamID string) (string, error) {
|
|||
// SetTeamName writes out team name.
|
||||
// This can only be done once.
|
||||
func (s *State) SetTeamName(teamID, teamName string) error {
|
||||
f, err := s.Open("teamids.txt")
|
||||
idsFile, err := s.Open("teamids.txt")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Team IDs file does not exist")
|
||||
}
|
||||
defer idsFile.Close()
|
||||
found := false
|
||||
scanner := bufio.NewScanner(f)
|
||||
scanner := bufio.NewScanner(idsFile)
|
||||
for scanner.Scan() {
|
||||
if scanner.Text() == teamID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
if !found {
|
||||
return fmt.Errorf("Team ID not found in list of valid Team IDs")
|
||||
}
|
||||
|
||||
teamFile := filepath.Join("teams", teamID)
|
||||
if err := afero.WriteFile(s, teamFile, []byte(teamName), os.ModeExclusive|0644); os.IsExist(err) {
|
||||
teamFilename := filepath.Join("teams", teamID)
|
||||
teamFile, err := s.Fs.OpenFile(teamFilename, os.O_CREATE|os.O_EXCL, 0644)
|
||||
if os.IsExist(err) {
|
||||
return fmt.Errorf("Team ID is already registered")
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
defer teamFile.Close()
|
||||
fmt.Fprintln(teamFile, teamName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PointsLog retrieves the current points log.
|
||||
|
@ -152,6 +164,7 @@ func (s *State) PointsLog() award.List {
|
|||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
log.Println(line)
|
||||
cur, err := award.Parse(line)
|
||||
if err != nil {
|
||||
log.Printf("Skipping malformed award line %s: %s", line, err)
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -46,6 +47,10 @@ func TestState(t *testing.T) {
|
|||
}
|
||||
teamID := string(teamIDs[0])
|
||||
|
||||
if _, err := s.TeamName(teamID); err == nil {
|
||||
t.Errorf("Bad team ID lookup didn't return error")
|
||||
}
|
||||
|
||||
if err := s.SetTeamName("bad team ID", "bad team name"); err == nil {
|
||||
t.Errorf("Setting bad team ID didn't raise an error")
|
||||
}
|
||||
|
@ -53,12 +58,31 @@ func TestState(t *testing.T) {
|
|||
if err := s.SetTeamName(teamID, "My Team"); err != nil {
|
||||
t.Errorf("Setting team name: %v", err)
|
||||
}
|
||||
if err := s.SetTeamName(teamID, "wat"); err == nil {
|
||||
t.Errorf("Registering team a second time didn't fail")
|
||||
}
|
||||
|
||||
category := "poot"
|
||||
points := 3928
|
||||
s.AwardPoints(teamID, category, points)
|
||||
if err := s.AwardPoints(teamID, category, points); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := s.AwardPoints(teamID, category, points); err != nil {
|
||||
t.Error("Two awards before refresh:", err)
|
||||
}
|
||||
// Flex duplicate detection with different timestamp
|
||||
if f, err := s.Create("points.new/moo"); err != nil {
|
||||
t.Error("Creating duplicate points file:", err)
|
||||
} else {
|
||||
fmt.Fprintln(f, time.Now().Unix()+1, teamID, category, points)
|
||||
f.Close()
|
||||
}
|
||||
s.refresh()
|
||||
|
||||
if err := s.AwardPoints(teamID, category, points); err == nil {
|
||||
t.Error("Duplicate points award didn't fail")
|
||||
}
|
||||
|
||||
pl = s.PointsLog()
|
||||
if len(pl) != 1 {
|
||||
t.Errorf("After awarding points, points log has length %d", len(pl))
|
||||
|
@ -66,6 +90,18 @@ func TestState(t *testing.T) {
|
|||
t.Errorf("Incorrect logged award %v", pl)
|
||||
}
|
||||
|
||||
afero.WriteFile(s, "points.log", []byte("intentional parse error\n"), 0644)
|
||||
if len(s.PointsLog()) != 0 {
|
||||
t.Errorf("Intentional parse error breaks pointslog")
|
||||
}
|
||||
if err := s.AwardPoints(teamID, category, points); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
s.refresh()
|
||||
if len(s.PointsLog()) != 1 {
|
||||
t.Error("Intentional parse error screws up all parsing")
|
||||
}
|
||||
|
||||
s.Fs.Remove("initialized")
|
||||
s.refresh()
|
||||
|
||||
|
@ -73,6 +109,7 @@ func TestState(t *testing.T) {
|
|||
if len(pl) != 0 {
|
||||
t.Errorf("After reinitialization, points log has length %d", len(pl))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStateEvents(t *testing.T) {
|
||||
|
@ -88,6 +125,79 @@ func TestStateEvents(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStateDisabled(t *testing.T) {
|
||||
s := NewTestState()
|
||||
s.refresh()
|
||||
|
||||
if !s.Enabled {
|
||||
t.Error("Brand new state is disabled")
|
||||
}
|
||||
|
||||
hoursFile, err := s.Create("hours")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer hoursFile.Close()
|
||||
|
||||
fmt.Fprintln(hoursFile, "- 1970-01-01T01:01:01Z")
|
||||
hoursFile.Sync()
|
||||
s.refresh()
|
||||
if s.Enabled {
|
||||
t.Error("Disabling 1970-01-01")
|
||||
}
|
||||
|
||||
fmt.Fprintln(hoursFile, "+ 1970-01-01T01:01:01+05:00")
|
||||
hoursFile.Sync()
|
||||
s.refresh()
|
||||
if !s.Enabled {
|
||||
t.Error("Enabling 1970-01-02")
|
||||
}
|
||||
|
||||
fmt.Fprintln(hoursFile, "")
|
||||
fmt.Fprintln(hoursFile, "# Comment")
|
||||
hoursFile.Sync()
|
||||
s.refresh()
|
||||
if !s.Enabled {
|
||||
t.Error("Comments")
|
||||
}
|
||||
|
||||
fmt.Fprintln(hoursFile, "intentional parse error")
|
||||
hoursFile.Sync()
|
||||
s.refresh()
|
||||
if !s.Enabled {
|
||||
t.Error("intentional parse error")
|
||||
}
|
||||
|
||||
fmt.Fprintln(hoursFile, "- 1980-01-01T01:01:01Z")
|
||||
hoursFile.Sync()
|
||||
s.refresh()
|
||||
if s.Enabled {
|
||||
t.Error("Disabling 1980-01-01")
|
||||
}
|
||||
|
||||
if err := s.Remove("hours"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
s.refresh()
|
||||
if !s.Enabled {
|
||||
t.Error("Removing `hours` disabled event")
|
||||
}
|
||||
|
||||
if err := s.Remove("enabled"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
s.refresh()
|
||||
if s.Enabled {
|
||||
t.Error("Removing `enabled` didn't disable")
|
||||
}
|
||||
|
||||
s.Remove("initialized")
|
||||
s.refresh()
|
||||
if !s.Enabled {
|
||||
t.Error("Re-initalizing didn't start event")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateMaintainer(t *testing.T) {
|
||||
updateInterval := 10 * time.Millisecond
|
||||
|
||||
|
|
|
@ -31,4 +31,9 @@ func TestTheme(t *testing.T) {
|
|||
} else if !timestamp.Equal(fileInfo.ModTime()) {
|
||||
t.Error("Timestamp compared wrong")
|
||||
}
|
||||
|
||||
if f, _, err := s.Open("nofile"); err == nil {
|
||||
f.Close()
|
||||
t.Error("Opening non-existent file didn't return an error")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue