Compare commits

..

2 Commits

Author SHA1 Message Date
Neale Pickett 59a6aef007 lowercase error string 2023-09-29 15:38:44 -06:00
Neale Pickett 79799bf1c2 State: add "Enabeled", remove "Messages"
Fixes #164
2023-09-29 15:37:18 -06:00
9 changed files with 32 additions and 53 deletions

View File

@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Reworked the built-in theme
- [moth.mjs](theme/moth.mjs) is now the standard MOTH library for ECMAScript
- Devel mode no longer accepts an empty team ID
- messages.html moved into theme
- Exported state now includes "Enabled" boolean
## [v4.4.9] - 2022-05-12
### Changed

View File

@ -45,7 +45,7 @@ func TestHttpd(t *testing.T) {
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":{},"PointsLog":[],"Puzzles":{}}` {
} else if r.Body.String() != `{"Config":{"Devel":false},"TeamNames":{},"PointsLog":[],"Puzzles":{}}` {
t.Error("Unexpected state", r.Body.String())
}
@ -71,7 +71,7 @@ func TestHttpd(t *testing.T) {
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]}}` {
} else if r.Body.String() != `{"Config":{"Devel":false},"TeamNames":{"self":"GoTeam"},"PointsLog":[],"Puzzles":{"pategory":[1]}}` {
t.Error("Unexpected state", r.Body.String())
}

View File

@ -30,7 +30,7 @@ type Configuration struct {
// StateExport is given to clients requesting the current state.
type StateExport struct {
Config Configuration
Messages string
Enabled bool
TeamNames map[string]string
PointsLog award.List
Puzzles map[string][]int
@ -53,7 +53,7 @@ type ThemeProvider interface {
// StateProvider defines what's required to provide MOTH state.
type StateProvider interface {
Messages() string
Enabled() bool
PointsLog() award.List
TeamName(teamID string) (string, error)
SetTeamName(teamID, teamName string) error
@ -194,7 +194,7 @@ func (mh *MothRequestHandler) exportStateIfRegistered(forceRegistered bool) *Sta
teamName, err := mh.State.TeamName(mh.teamID)
registered := forceRegistered || mh.Config.Devel || (err == nil)
export.Messages = mh.State.Messages()
export.Enabled = mh.State.Enabled()
export.TeamNames = make(map[string]string)
// Anonymize team IDs in points log, and write out team names

View File

@ -22,7 +22,6 @@ func NewTestServer() TestServer {
state := NewTestState()
afero.WriteFile(state, "teamids.txt", []byte("teamID\n"), 0644)
afero.WriteFile(state, "messages.html", []byte("messages.html"), 0644)
state.refresh()
theme := NewTestTheme()
@ -101,9 +100,6 @@ func TestProdServer(t *testing.T) {
if len(es.Puzzles) != 1 {
t.Error("Puzzle categories wrong length", len(es.Puzzles))
}
if es.Messages != "messages.html" {
t.Error("Messages has wrong contents")
}
if len(es.PointsLog) != 0 {
t.Error("Points log not empty")
}

View File

@ -37,7 +37,7 @@ type State struct {
afero.Fs
// Enabled tracks whether the current State system is processing updates
Enabled bool
enabled bool
enabledWhy string
refreshNow chan bool
@ -49,7 +49,6 @@ type State struct {
teamNamesLastChange time.Time
teamNames map[string]string
pointsLog award.List
messages string
lock sync.RWMutex
}
@ -57,7 +56,7 @@ type State struct {
func NewState(fs afero.Fs) *State {
s := &State{
Fs: fs,
Enabled: true,
enabled: true,
refreshNow: make(chan bool, 5),
eventStream: make(chan []string, 80),
@ -117,11 +116,11 @@ func (s *State) updateEnabled() {
}
}
if (nextEnabled != s.Enabled) || (why != s.enabledWhy) {
s.Enabled = nextEnabled
if (nextEnabled != s.enabled) || (why != s.enabledWhy) {
s.enabled = nextEnabled
s.enabledWhy = why
log.Printf("Setting enabled=%v: %s", s.Enabled, s.enabledWhy)
if s.Enabled {
log.Printf("Setting enabled=%v: %s", s.enabled, s.enabledWhy)
if s.enabled {
s.LogEvent("enabled", "", "", 0, s.enabledWhy)
} else {
s.LogEvent("disabled", "", "", 0, s.enabledWhy)
@ -193,11 +192,9 @@ func (s *State) PointsLog() award.List {
return ret
}
// Messages retrieves the current messages.
func (s *State) Messages() string {
s.lock.RLock() // It's not clear to me that this actually needs to happen
defer s.lock.RUnlock()
return s.messages
// Enabled returns true if the server is in "enabled" state
func (s *State) Enabled() bool {
return s.enabled
}
// AwardPoints gives points to teamID in category.
@ -313,7 +310,6 @@ func (s *State) maybeInitialize() {
s.Remove("hours.txt")
s.Remove("points.log")
s.Remove("events.csv")
s.Remove("messages.html")
s.Remove("mothd.log")
s.RemoveAll("points.tmp")
s.RemoveAll("points.new")
@ -369,11 +365,6 @@ func (s *State) maybeInitialize() {
f.Close()
}
if f, err := s.Create("messages.html"); err == nil {
fmt.Fprintln(f, "<!-- messages.html: put client broadcast messages here. -->")
f.Close()
}
if f, err := s.Create("points.log"); err == nil {
f.Close()
}
@ -465,16 +456,12 @@ func (s *State) updateCaches() {
}
}
}
if bMessages, err := afero.ReadFile(s, "messages.html"); err == nil {
s.messages = string(bMessages)
}
}
func (s *State) refresh() {
s.maybeInitialize()
s.updateEnabled()
if s.Enabled {
if s.enabled {
s.collectPoints()
}
s.updateCaches()
@ -523,7 +510,7 @@ func (ds *DevelState) TeamName(teamID string) (string, error) {
return name, nil
}
if teamID == "" {
return "", fmt.Errorf("Empty team ID")
return "", fmt.Errorf("empty team ID")
}
return fmt.Sprintf("«devel:%s»", teamID), nil
}

View File

@ -185,7 +185,7 @@ func TestStateDisabled(t *testing.T) {
s := NewTestState()
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("Brand new state is disabled")
}
@ -195,35 +195,35 @@ func TestStateDisabled(t *testing.T) {
}
defer hoursFile.Close()
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("Empty hours.txt not enabled")
}
fmt.Fprintln(hoursFile, "- 1970-01-01T01:01:01Z")
hoursFile.Sync()
s.refresh()
if s.Enabled {
if s.Enabled() {
t.Error("1970-01-01")
}
fmt.Fprintln(hoursFile, "+ 1970-01-02 01:01:01+05:00")
hoursFile.Sync()
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("1970-01-02")
}
fmt.Fprintln(hoursFile, "-")
hoursFile.Sync()
s.refresh()
if s.Enabled {
if s.Enabled() {
t.Error("bare -")
}
fmt.Fprintln(hoursFile, "+")
hoursFile.Sync()
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("bare +")
}
@ -231,21 +231,21 @@ func TestStateDisabled(t *testing.T) {
fmt.Fprintln(hoursFile, "# Comment")
hoursFile.Sync()
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("Comment")
}
fmt.Fprintln(hoursFile, "intentional parse error")
hoursFile.Sync()
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("intentional parse error")
}
fmt.Fprintln(hoursFile, "- 1980-01-01T01:01:01Z")
hoursFile.Sync()
s.refresh()
if s.Enabled {
if s.Enabled() {
t.Error("1980-01-01")
}
@ -253,13 +253,13 @@ func TestStateDisabled(t *testing.T) {
t.Error(err)
}
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("Removing `hours.txt` disabled event")
}
s.Remove("initialized")
s.refresh()
if !s.Enabled {
if !s.Enabled() {
t.Error("Re-initializing didn't start event")
}
}

View File

@ -53,7 +53,6 @@ Returns the current Moth event state as a JSON object.
"Config": {
"Devel": false // true means this is a development server
},
"Messages: "HTML to be rendered as broadcast messages",
"TeamNames": {
"self": "Requesting team name", // Only if regestered team id is a provided
"0": "Team 1 Name",
@ -91,7 +90,6 @@ Content-Type: application/json
{"Config":
{"Devel":false},
"Messages":"<p>Welcome to the event!</p><p>Event ends at 19:00!</p>",
"TeamNames":{
"0":"Mike and Jack",
"12":"Team 2",

View File

@ -167,9 +167,7 @@ class App {
}
function init() {
window.app = {
server: new App()
}
window.app = new App()
}
common.WhenDOMLoaded(init)

View File

@ -367,10 +367,8 @@ class State {
Devel: obj.Config.Devel,
}
/** Global messages, in HTML
* @type {string}
*/
this.Messages = obj.Messages
/** True if the server is in enabled state */
this.Enabled = obj.Enabled
/** Map from Team ID to Team Name
* @type {Object.<string,string>}