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 // RegisterHandler handles attempts to register a team
func (h *HTTPServer) RegisterHandler(mh MothRequestHandler, w http.ResponseWriter, req *http.Request) { func (h *HTTPServer) RegisterHandler(mh MothRequestHandler, w http.ResponseWriter, req *http.Request) {
teamName := req.FormValue("name") 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()) jsend.Sendf(w, jsend.Fail, "not registered", err.Error())
} else { } else {
jsend.Sendf(w, jsend.Success, "registered", "Team ID registered") jsend.Sendf(w, jsend.Success, "registered", "Team ID registered")

View File

@ -66,6 +66,12 @@ func TestHttpd(t *testing.T) {
t.Error("Register failed") 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 { if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 {
t.Error(r.Result()) t.Error(r.Result())
} else if r.Body.String() != `{"Config":{"Devel":false},"Messages":"messages.html","TeamNames":{"self":"GoTeam"},"PointsLog":[],"Puzzles":{"pategory":[1]}}` { } 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) time.Sleep(TestMaintenanceInterval)
if r := hs.TestRequest("/content/pategory/2/puzzle.json", nil); r.Result().StatusCode != 200 {
t.Error(r.Result())
}
state := StateExport{} state := StateExport{}
if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 { if r := hs.TestRequest("/state", nil); r.Result().StatusCode != 200 {
t.Error(r.Result()) t.Error(r.Result())

View File

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

View File

@ -34,6 +34,7 @@ func TestServer(t *testing.T) {
server := NewTestServer() server := NewTestServer()
handler := server.NewHandler(participantID, teamID) handler := server.NewHandler(participantID, teamID)
anonHandler := server.NewHandler("badParticipantId", "badTeamId")
{ {
es := handler.ExportState() es := handler.ExportState()
@ -49,6 +50,12 @@ func TestServer(t *testing.T) {
if err := handler.Register(teamName); err != nil { if err := handler.Register(teamName); err != nil {
t.Error(err) 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 { if r, _, err := handler.ThemeOpen("/index.html"); err != nil {
t.Error(err) t.Error(err)
} else if contents, err := ioutil.ReadAll(r); err != nil { } else if contents, err := ioutil.ReadAll(r); err != nil {
@ -89,12 +96,12 @@ func TestServer(t *testing.T) {
r.Close() 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") t.Error("Opening locked puzzle shouldn't work")
r.Close() 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") t.Error("Opening non-existent puzzle shouldn't work")
r.Close() r.Close()
} }
@ -109,6 +116,22 @@ func TestServer(t *testing.T) {
if len(es.PointsLog) != 1 { if len(es.PointsLog) != 1 {
t.Error("I didn't get my points!") 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 // BUG(neale): We aren't currently testing the various ways to disable the server
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"bufio" "bufio"
"errors"
"fmt" "fmt"
"log" "log"
"math/rand" "math/rand"
@ -23,6 +24,9 @@ const DistinguishableChars = "34678abcdefhikmnpqrtwxy="
// This is also a valid RFC3339 format. // This is also a valid RFC3339 format.
const RFC3339Space = "2006-01-02 15:04:05Z07:00" 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. // State defines the current state of a MOTH instance.
// We use the filesystem for synchronization between threads. // We use the filesystem for synchronization between threads.
// The only thing State methods need to know is the path to the state directory. // 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. // 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 { func (s *State) SetTeamName(teamID, teamName string) error {
idsFile, err := s.Open("teamids.txt") idsFile, err := s.Open("teamids.txt")
if err != nil { if err != nil {
@ -149,7 +153,7 @@ func (s *State) SetTeamName(teamID, teamName string) error {
teamFilename := filepath.Join("teams", teamID) teamFilename := filepath.Join("teams", teamID)
teamFile, err := s.Fs.OpenFile(teamFilename, os.O_CREATE|os.O_EXCL, 0644) teamFile, err := s.Fs.OpenFile(teamFilename, os.O_CREATE|os.O_EXCL, 0644)
if os.IsExist(err) { if os.IsExist(err) {
return fmt.Errorf("Team ID is already registered") return ErrAlreadyRegistered
} else if err != nil { } else if err != nil {
return err return err
} }

View File

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

View File

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