Fix bug that required id to get puzzle content

This commit is contained in:
Neale Pickett 2020-10-13 18:33:12 -06:00
parent b39e287e41
commit 8874aad7cb
7 changed files with 55 additions and 13 deletions

View File

@ -110,7 +110,9 @@ func (h *HTTPServer) StateHandler(mh MothRequestHandler, w http.ResponseWriter,
// RegisterHandler handles attempts to register a team
func (h *HTTPServer) RegisterHandler(mh MothRequestHandler, w http.ResponseWriter, req *http.Request) {
teamName := req.FormValue("name")
if err := mh.Register(teamName); err != nil {
if err := mh.Register(teamName); err == ErrAlreadyRegistered {
jsend.Sendf(w, jsend.Success, "already registered", "Team ID has already been registered")
} else if err != nil {
jsend.Sendf(w, jsend.Fail, "not registered", err.Error())
} else {
jsend.Sendf(w, jsend.Success, "registered", "Team ID registered")

View File

@ -66,6 +66,12 @@ func TestHttpd(t *testing.T) {
t.Error("Register failed")
}
thatt 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":"already registered","description":"Team ID has already been registered"}}` {
t.Error("Register failed", r.Body.String())
}
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]}}` {
@ -108,6 +114,10 @@ func TestHttpd(t *testing.T) {
time.Sleep(TestMaintenanceInterval)
if r := hs.TestRequest("/content/pategory/2/puzzle.json", nil); r.Result().StatusCode != 200 {
t.Error(r.Result())
}
state := StateExport{}
if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 {
t.Error(r.Result())

View File

@ -107,7 +107,7 @@ type MothRequestHandler struct {
// PuzzlesOpen opens a file associated with a puzzle.
// BUG(neale): Multiple providers with the same category name are not detected or handled well.
func (mh *MothRequestHandler) PuzzlesOpen(cat string, points int, path string) (r ReadSeekCloser, ts time.Time, err error) {
export := mh.ExportState()
export := mh.exportStateIfRegistered(true)
found := false
for _, p := range export.Puzzles[cat] {
if p == points {
@ -115,7 +115,7 @@ func (mh *MothRequestHandler) PuzzlesOpen(cat string, points int, path string) (
}
}
if !found {
return nil, time.Time{}, fmt.Errorf("Category not found")
return nil, time.Time{}, fmt.Errorf("Puzzle does not exist or is locked")
}
// Try every provider until someone doesn't return an error
@ -172,11 +172,15 @@ func (mh *MothRequestHandler) Register(teamName string) error {
// the anonymized team name for this teamID has the special value "self".
// If not, the puzzles list is empty.
func (mh *MothRequestHandler) ExportState() *StateExport {
return mh.exportStateIfRegistered(false)
}
func (mh *MothRequestHandler) exportStateIfRegistered(override bool) *StateExport {
export := StateExport{}
export.Config = mh.Config
teamName, err := mh.State.TeamName(mh.teamID)
registered := (err == nil)
registered := override || (err == nil)
export.Messages = mh.State.Messages()
export.TeamNames = map[string]string{"self": teamName}
@ -209,7 +213,6 @@ func (mh *MothRequestHandler) ExportState() *StateExport {
// We used to hand this out to everyone,
// but then we got a bad reputation on some secretive blacklist,
// and now the Navy can't register for events.
for _, provider := range mh.PuzzleProviders {
for _, category := range provider.Inventory() {
// Append sentry (end of puzzles)

View File

@ -34,6 +34,7 @@ func TestServer(t *testing.T) {
server := NewTestServer()
handler := server.NewHandler(participantID, teamID)
anonHandler := server.NewHandler("badParticipantId", "badTeamId")
{
es := handler.ExportState()
@ -49,6 +50,12 @@ func TestServer(t *testing.T) {
if err := handler.Register(teamName); err != nil {
t.Error(err)
}
if err := handler.Register(teamName); err == nil {
t.Error("Registering twice should have raised an error")
} else if err != ErrAlreadyRegistered {
t.Error("Wrong error for duplicate registration:", err)
}
if r, _, err := handler.ThemeOpen("/index.html"); err != nil {
t.Error(err)
} else if contents, err := ioutil.ReadAll(r); err != nil {
@ -89,12 +96,12 @@ func TestServer(t *testing.T) {
r.Close()
}
if r, _, err := handler.PuzzlesOpen("pategory", 2, "puzzles.json"); err == nil {
if r, _, err := handler.PuzzlesOpen("pategory", 2, "puzzle.json"); err == nil {
t.Error("Opening locked puzzle shouldn't work")
r.Close()
}
if r, _, err := handler.PuzzlesOpen("pategory", 20, "puzzles.json"); err == nil {
if r, _, err := handler.PuzzlesOpen("pategory", 20, "puzzle.json"); err == nil {
t.Error("Opening non-existent puzzle shouldn't work")
r.Close()
}
@ -109,6 +116,22 @@ func TestServer(t *testing.T) {
if len(es.PointsLog) != 1 {
t.Error("I didn't get my points!")
}
if len(es.Puzzles["pategory"]) != 2 {
t.Error("The next puzzle didn't unlock!")
} else if es.Puzzles["pategory"][1] != 2 {
t.Error("The 2-point puzzle should have unlocked!")
}
if r, _, err := handler.PuzzlesOpen("pategory", 2, "puzzle.json"); err != nil {
t.Error("Opening unlocked puzzle should work")
} else {
r.Close()
}
if r, _, err := anonHandler.PuzzlesOpen("pategory", 2, "puzzle.json"); err != nil {
t.Error("Opening unlocked puzzle anonymously should work")
} else {
r.Close()
}
// BUG(neale): We aren't currently testing the various ways to disable the server
}

View File

@ -2,6 +2,7 @@ package main
import (
"bufio"
"errors"
"fmt"
"log"
"math/rand"
@ -23,6 +24,9 @@ const DistinguishableChars = "34678abcdefhikmnpqrtwxy="
// This is also a valid RFC3339 format.
const RFC3339Space = "2006-01-02 15:04:05Z07:00"
// ErrAlreadyRegistered means a team cannot be registered because it was registered previously.
var ErrAlreadyRegistered = errors.New("Team ID has already been registered")
// State defines the current state of a MOTH instance.
// We use the filesystem for synchronization between threads.
// The only thing State methods need to know is the path to the state directory.
@ -127,7 +131,7 @@ func (s *State) TeamName(teamID string) (string, error) {
}
// SetTeamName writes out team name.
// This can only be done once.
// This can only be done once per team.
func (s *State) SetTeamName(teamID, teamName string) error {
idsFile, err := s.Open("teamids.txt")
if err != nil {
@ -149,7 +153,7 @@ func (s *State) SetTeamName(teamID, teamName string) error {
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")
return ErrAlreadyRegistered
} else if err != nil {
return err
}

View File

@ -59,7 +59,7 @@ function renderPuzzles(obj) {
pdiv.appendChild(l)
for (let puzzle of puzzles) {
let points = puzzle
let id = puzzle
let id = null
if (Array.isArray(puzzle)) {
points = puzzle[0]
@ -80,7 +80,7 @@ function renderPuzzles(obj) {
let url = new URL("puzzle.html", window.location)
url.searchParams.set("cat", cat)
url.searchParams.set("points", points)
url.searchParams.set("pid", id)
if (id) { url.searchParams.set("pid", id) }
a.href = url.toString()
}
}

View File

@ -204,8 +204,8 @@ function init() {
let points = params.get("points")
let puzzleId = params.get("pid")
if (categoryName && points && puzzleId) {
loadPuzzle(categoryName, points, puzzleId)
if (categoryName && points) {
loadPuzzle(categoryName, points, puzzleId || points)
}
let teamId = sessionStorage.getItem("id")