moth

Monarch Of The Hill game server
git clone https://git.woozle.org/neale/moth.git

moth / cmd / mothd
Neale Pickett  ·  2023-09-29

server_test.go

  1package main
  2
  3import (
  4	"io/ioutil"
  5	"testing"
  6
  7	"github.com/spf13/afero"
  8)
  9
 10const TestTeamID = "teamID"
 11
 12type TestServer struct {
 13	*MothServer
 14}
 15
 16// NewTestServer creates a new MothServer with NewTestMothballs and some initial state.
 17//
 18// See function definition for details.
 19func NewTestServer() TestServer {
 20	puzzles := NewTestMothballs()
 21	puzzles.refresh()
 22
 23	state := NewTestState()
 24	afero.WriteFile(state, "teamids.txt", []byte("teamID\n"), 0644)
 25	state.refresh()
 26
 27	theme := NewTestTheme()
 28	afero.WriteFile(theme.Fs, "/index.html", []byte("index.html"), 0644)
 29
 30	return TestServer{NewMothServer(Configuration{}, theme, state, puzzles)}
 31}
 32
 33func (ts TestServer) refresh() {
 34	ts.State.(*State).refresh()
 35	for _, pp := range ts.PuzzleProviders {
 36		pp.(*Mothballs).refresh()
 37	}
 38	ts.Theme.(*Theme).refresh()
 39}
 40
 41func TestDevelServer(t *testing.T) {
 42	server := NewTestServer()
 43	server.Config.Devel = true
 44	anonHandler := server.NewHandler("badTeamId")
 45
 46	{
 47		es := anonHandler.ExportState()
 48		if !es.Config.Devel {
 49			t.Error("Not marked as development server")
 50		}
 51		if len(es.Puzzles) != 1 {
 52			t.Error("Wrong puzzles for anonymous state on devel server:", es.Puzzles)
 53		}
 54	}
 55}
 56
 57func TestProdServer(t *testing.T) {
 58	teamName := "OurTeam"
 59	teamID := TestTeamID
 60
 61	server := NewTestServer()
 62	handler := server.NewHandler(teamID)
 63	anonHandler := server.NewHandler("badTeamId")
 64
 65	{
 66		es := handler.ExportState()
 67		if es.Config.Devel {
 68			t.Error("Marked as development server", es.Config)
 69		}
 70		if len(es.Puzzles) != 0 {
 71			t.Log("State", es)
 72			t.Error("Unauthenticated state has non-empty puzzles list")
 73		}
 74	}
 75
 76	if err := handler.Register(teamName); err != nil {
 77		t.Error(err)
 78	}
 79	if err := handler.Register(teamName); err == nil {
 80		t.Error("Registering twice should have raised an error")
 81	} else if err != ErrAlreadyRegistered {
 82		t.Error("Wrong error for duplicate registration:", err)
 83	}
 84
 85	if r, _, err := handler.ThemeOpen("/index.html"); err != nil {
 86		t.Error(err)
 87	} else if contents, err := ioutil.ReadAll(r); err != nil {
 88		t.Error(err)
 89	} else if string(contents) != "index.html" {
 90		t.Error("index.html wrong contents", contents)
 91	}
 92
 93	server.refresh()
 94
 95	{
 96		es := handler.ExportState()
 97		if es.Config.Devel {
 98			t.Error("Marked as development server", es.Config)
 99		}
100		if len(es.Puzzles) != 1 {
101			t.Error("Puzzle categories wrong length", len(es.Puzzles))
102		}
103		if len(es.PointsLog) != 0 {
104			t.Error("Points log not empty")
105		}
106		if len(es.TeamNames) != 1 {
107			t.Error("Wrong number of team names")
108		}
109		if es.TeamNames["self"] != teamName {
110			t.Error("TeamNames['self'] wrong")
111		}
112	}
113
114	if r, _, err := handler.PuzzlesOpen("pategory", 1, "moo.txt"); err != nil {
115		t.Error(err)
116	} else if contents, err := ioutil.ReadAll(r); err != nil {
117		r.Close()
118		t.Error(err)
119	} else if string(contents) != "moo" {
120		r.Close()
121		t.Error("moo.txt has wrong contents", contents)
122	} else {
123		r.Close()
124	}
125
126	if r, _, err := handler.PuzzlesOpen("pategory", 2, "puzzle.json"); err == nil {
127		t.Error("Opening locked puzzle shouldn't work")
128		r.Close()
129	}
130
131	if r, _, err := handler.PuzzlesOpen("pategory", 20, "puzzle.json"); err == nil {
132		t.Error("Opening non-existent puzzle shouldn't work")
133		r.Close()
134	}
135
136	if err := anonHandler.CheckAnswer("pategory", 1, "answer123"); err == nil {
137		t.Error("Invalid team ID was able to get points with correct answer")
138	}
139	if err := handler.CheckAnswer("pategory", 1, "answer123"); err != nil {
140		t.Error("Right answer marked wrong", err)
141	}
142
143	server.refresh()
144
145	{
146		es := handler.ExportState()
147		if len(es.PointsLog) != 1 {
148			t.Error("I didn't get my points!")
149		}
150		if len(es.Puzzles["pategory"]) != 2 {
151			t.Error("The next puzzle didn't unlock!")
152		} else if es.Puzzles["pategory"][1] != 2 {
153			t.Error("The 2-point puzzle should have unlocked!")
154		}
155	}
156
157	if r, _, err := handler.PuzzlesOpen("pategory", 2, "puzzle.json"); err != nil {
158		t.Error("Opening unlocked puzzle should work")
159	} else {
160		r.Close()
161	}
162	if r, _, err := anonHandler.PuzzlesOpen("pategory", 2, "puzzle.json"); err != nil {
163		t.Error("Opening unlocked puzzle anonymously should work")
164	} else {
165		r.Close()
166	}
167
168	if err := handler.CheckAnswer("pategory", 2, "wat"); err != nil {
169		t.Error("Right answer marked wrong:", err)
170	}
171
172	server.refresh()
173
174	{
175		es := anonHandler.ExportState()
176		if len(es.TeamNames) != 1 {
177			t.Error("Anonymous TeamNames is wrong:", es.TeamNames)
178		}
179		if len(es.PointsLog) != 2 {
180			t.Errorf("Points log wrong length: got %d, wanted 2", len(es.PointsLog))
181		} else if es.PointsLog[1].TeamID != "0" {
182			t.Error("Second point log didn't anonymize team ID correctly:", es.PointsLog[1])
183		}
184	}
185
186	{
187		es := handler.ExportState()
188		if len(es.TeamNames) != 1 {
189			t.Error("TeamNames is wrong:", es.TeamNames)
190		}
191	}
192
193	// BUG(neale): We aren't currently testing the various ways to disable the server
194}