moth/cmd/mothd/state_test.go

340 lines
7.8 KiB
Go
Raw Permalink Normal View History

2019-12-01 18:58:09 -07:00
package main
import (
2019-12-01 20:53:13 -07:00
"bytes"
2020-08-21 17:02:38 -06:00
"fmt"
2019-12-01 18:58:09 -07:00
"os"
2020-08-19 15:38:13 -06:00
"strings"
2019-12-01 18:58:09 -07:00
"testing"
2020-08-18 17:04:23 -06:00
"time"
2020-08-14 20:26:04 -06:00
"github.com/spf13/afero"
2019-12-01 18:58:09 -07:00
)
2020-08-17 17:43:57 -06:00
func NewTestState() *State {
s := NewState(new(afero.MemMapFs))
2020-08-18 17:04:23 -06:00
s.refresh()
2020-08-17 17:43:57 -06:00
return s
}
2022-11-29 15:48:35 -07:00
func slurp(c chan bool) {
for range c {
// Nothing
}
}
2019-12-01 18:58:09 -07:00
func TestState(t *testing.T) {
2020-08-17 17:43:57 -06:00
s := NewTestState()
2022-11-29 15:48:35 -07:00
defer close(s.refreshNow)
go slurp(s.refreshNow)
2019-12-01 18:58:09 -07:00
mustExist := func(path string) {
2020-08-17 17:43:57 -06:00
_, err := s.Fs.Stat(path)
2019-12-01 18:58:09 -07:00
if os.IsNotExist(err) {
t.Errorf("File %s does not exist", path)
}
}
2019-12-01 20:53:13 -07:00
pl := s.PointsLog()
if len(pl) != 0 {
t.Errorf("Empty points log is not empty")
}
2019-12-01 20:47:46 -07:00
2019-12-01 18:58:09 -07:00
mustExist("initialized")
mustExist("hours.txt")
2019-12-01 20:53:13 -07:00
2020-08-17 17:43:57 -06:00
teamIDsBuf, err := afero.ReadFile(s.Fs, "teamids.txt")
2019-12-01 20:47:46 -07:00
if err != nil {
2019-12-01 20:53:13 -07:00
t.Errorf("Reading teamids.txt: %v", err)
2019-12-01 20:47:46 -07:00
}
2019-12-01 20:53:13 -07:00
2020-08-17 17:43:57 -06:00
teamIDs := bytes.Split(teamIDsBuf, []byte("\n"))
if (len(teamIDs) != 101) || (len(teamIDs[100]) > 0) {
t.Errorf("There weren't 100 teamIDs, there were %d", len(teamIDs))
2019-12-01 20:47:46 -07:00
}
2020-08-17 17:43:57 -06:00
teamID := string(teamIDs[0])
2019-12-01 20:53:13 -07:00
2020-08-21 17:02:38 -06:00
if _, err := s.TeamName(teamID); err == nil {
t.Errorf("Bad team ID lookup didn't return error")
}
2019-12-01 20:47:46 -07:00
if err := s.SetTeamName("bad team ID", "bad team name"); err == nil {
2019-12-01 20:53:13 -07:00
t.Errorf("Setting bad team ID didn't raise an error")
2019-12-01 20:47:46 -07:00
}
2019-12-01 20:53:13 -07:00
teamName := "My Team"
if err := s.SetTeamName(teamID, teamName); err != nil {
2021-10-13 16:43:51 -06:00
t.Errorf("Setting team name: %v", err)
2019-12-01 20:47:46 -07:00
}
2020-08-21 17:02:38 -06:00
if err := s.SetTeamName(teamID, "wat"); err == nil {
t.Errorf("Registering team a second time didn't fail")
}
2021-10-26 12:48:23 -06:00
s.refresh()
if name, err := s.TeamName(teamID); err != nil {
t.Error(err)
} else if name != teamName {
t.Error("Incorrect team name:", name)
}
2019-12-07 21:17:13 -07:00
2019-12-05 21:50:43 -07:00
category := "poot"
points := 3928
2020-08-21 17:02:38 -06:00
if err := s.AwardPoints(teamID, category, points); err != nil {
t.Error(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()
}
2021-10-26 12:48:23 -06:00
s.AwardPoints(teamID, category, points)
2020-08-18 17:04:23 -06:00
s.refresh()
2021-10-26 12:48:23 -06:00
pl = s.PointsLog()
if len(pl) != 1 {
for i, award := range pl {
t.Logf("pl[%d] == %s", i, award.String())
}
t.Errorf("After awarding duplicate points, points log has length %d", len(pl))
} else if (pl[0].TeamID != teamID) || (pl[0].Category != category) || (pl[0].Points != points) {
t.Errorf("Incorrect logged award %v", pl)
}
2019-12-07 21:17:13 -07:00
2020-08-21 17:02:38 -06:00
if err := s.AwardPoints(teamID, category, points); err == nil {
2021-10-26 12:48:23 -06:00
t.Error("Duplicate points award after refresh didn't fail")
2020-08-21 17:02:38 -06:00
}
2020-10-14 09:46:51 -06:00
if err := s.AwardPoints(teamID, category, points+1); err != nil {
t.Error("Awarding more points:", err)
}
2021-10-26 12:48:23 -06:00
s.refresh()
if len(s.PointsLog()) != 2 {
t.Errorf("There should be two awards")
2019-12-05 21:50:43 -07:00
}
2019-12-07 21:17:13 -07:00
2020-08-21 17:02:38 -06:00
afero.WriteFile(s, "points.log", []byte("intentional parse error\n"), 0644)
2021-10-26 12:48:23 -06:00
s.refresh()
2020-08-21 17:02:38 -06:00
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()
2021-10-26 12:48:23 -06:00
if len(s.PointsLog()) != 1 {
t.Log(s.PointsLog())
2020-08-21 17:02:38 -06:00
t.Error("Intentional parse error screws up all parsing")
}
2020-08-17 17:43:57 -06:00
s.Fs.Remove("initialized")
2020-08-18 17:04:23 -06:00
s.refresh()
2019-12-07 21:17:13 -07:00
2019-12-05 21:54:32 -07:00
pl = s.PointsLog()
if len(pl) != 0 {
t.Errorf("After reinitialization, points log has length %d", len(pl))
}
2020-08-21 17:02:38 -06:00
2019-12-01 18:58:09 -07:00
}
2020-08-18 17:04:23 -06:00
// Out of order points insertion, issue #168
func TestStateOutOfOrderAward(t *testing.T) {
s := NewTestState()
category := "meow"
points := 100
now := time.Now().Unix()
if err := s.awardPointsAtTime(now+20, "AA", category, points); err != nil {
t.Error("Awarding points to team ZZ:", err)
}
if err := s.awardPointsAtTime(now+10, "ZZ", category, points); err != nil {
t.Error("Awarding points to team AA:", err)
}
s.refresh()
pl := s.PointsLog()
if len(pl) != 2 {
t.Error("Wrong length for points log")
}
if pl[0].TeamID != "ZZ" {
t.Error("Out of order points insertion not properly sorted in points log")
}
}
2020-08-18 17:04:23 -06:00
func TestStateEvents(t *testing.T) {
s := NewTestState()
s.LogEvent("moo", "", "", 0)
s.LogEvent("moo 2", "", "", 0)
2020-08-18 17:04:23 -06:00
if msg := <-s.eventStream; strings.Join(msg[1:], ":") != "init:::0" {
2020-10-14 18:20:49 -06:00
t.Error("Wrong message from event stream:", msg)
2020-08-18 17:04:23 -06:00
}
if msg := <-s.eventStream; !strings.HasPrefix(msg[5], "state/hours.txt") {
t.Error("Wrong message from event stream:", msg[5])
2022-11-29 15:48:35 -07:00
}
if msg := <-s.eventStream; strings.Join(msg[1:], ":") != "moo:::0" {
2020-10-14 18:20:49 -06:00
t.Error("Wrong message from event stream:", msg)
}
if msg := <-s.eventStream; strings.Join(msg[1:], ":") != "moo 2:::0" {
2020-10-14 18:20:49 -06:00
t.Error("Wrong message from event stream:", msg)
2020-08-18 17:04:23 -06:00
}
}
2020-08-21 17:02:38 -06:00
func TestStateDisabled(t *testing.T) {
s := NewTestState()
s.refresh()
if !s.Enabled() {
2020-08-21 17:02:38 -06:00
t.Error("Brand new state is disabled")
}
hoursFile, err := s.Create("hours.txt")
2020-08-21 17:02:38 -06:00
if err != nil {
t.Error(err)
}
defer hoursFile.Close()
2022-11-29 15:48:35 -07:00
s.refresh()
if !s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("Empty hours.txt not enabled")
}
2020-08-21 17:02:38 -06:00
fmt.Fprintln(hoursFile, "- 1970-01-01T01:01:01Z")
hoursFile.Sync()
s.refresh()
if s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("1970-01-01")
2020-08-21 17:02:38 -06:00
}
2022-11-29 15:48:35 -07:00
fmt.Fprintln(hoursFile, "+ 1970-01-02 01:01:01+05:00")
2020-08-21 17:02:38 -06:00
hoursFile.Sync()
s.refresh()
if !s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("1970-01-02")
}
fmt.Fprintln(hoursFile, "-")
hoursFile.Sync()
s.refresh()
if s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("bare -")
}
fmt.Fprintln(hoursFile, "+")
hoursFile.Sync()
s.refresh()
if !s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("bare +")
2020-08-21 17:02:38 -06:00
}
fmt.Fprintln(hoursFile, "")
fmt.Fprintln(hoursFile, "# Comment")
hoursFile.Sync()
s.refresh()
if !s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("Comment")
2020-08-21 17:02:38 -06:00
}
fmt.Fprintln(hoursFile, "intentional parse error")
hoursFile.Sync()
s.refresh()
if !s.Enabled() {
2020-08-21 17:02:38 -06:00
t.Error("intentional parse error")
}
fmt.Fprintln(hoursFile, "- 1980-01-01T01:01:01Z")
hoursFile.Sync()
s.refresh()
if s.Enabled() {
2022-11-29 15:48:35 -07:00
t.Error("1980-01-01")
2020-08-21 17:02:38 -06:00
}
if err := s.Remove("hours.txt"); err != nil {
2020-08-21 17:02:38 -06:00
t.Error(err)
}
s.refresh()
if !s.Enabled() {
t.Error("Removing `hours.txt` disabled event")
2020-08-21 17:02:38 -06:00
}
s.Remove("initialized")
s.refresh()
if !s.Enabled() {
2020-09-17 19:48:17 -06:00
t.Error("Re-initializing didn't start event")
2020-08-21 17:02:38 -06:00
}
}
2020-08-18 17:04:23 -06:00
func TestStateMaintainer(t *testing.T) {
2020-08-19 15:38:13 -06:00
updateInterval := 10 * time.Millisecond
2020-08-18 17:04:23 -06:00
s := NewTestState()
2020-08-19 15:38:13 -06:00
go s.Maintain(updateInterval)
if _, err := s.Stat("initialized"); err != nil {
t.Error(err)
}
teamIDLines, err := afero.ReadFile(s, "teamids.txt")
if err != nil {
t.Error(err)
}
teamIDList := strings.Split(string(teamIDLines), "\n")
if len(teamIDList) != 101 {
t.Error("TeamIDList length is", len(teamIDList))
}
teamID := teamIDList[0]
if len(teamID) < 6 {
t.Error("Team ID too short:", teamID)
}
2020-08-18 17:04:23 -06:00
s.LogEvent("Hello!", "", "", 0)
2020-08-19 15:38:13 -06:00
if len(s.PointsLog()) != 0 {
t.Error("Points log is not empty")
}
if err := s.SetTeamName(teamID, "The Patricks"); err != nil {
t.Error(err)
}
if err := s.AwardPoints(teamID, "pategory", 31337); err != nil {
t.Error(err)
}
time.Sleep(updateInterval)
pl := s.PointsLog()
if len(pl) != 1 {
t.Error("Points log should have one entry")
}
if (pl[0].Category != "pategory") || (pl[0].TeamID != teamID) {
t.Error("Wrong points event was recorded")
}
time.Sleep(updateInterval)
eventLog, err := afero.ReadFile(s.Fs, "events.csv")
2020-08-19 15:38:13 -06:00
if err != nil {
t.Error(err)
2022-11-29 15:48:35 -07:00
} else if events := strings.Split(string(eventLog), "\n"); len(events) != 4 {
2020-10-14 18:20:49 -06:00
t.Log("Events:", events)
t.Error("Wrong event log length:", len(events))
2022-11-29 15:48:35 -07:00
} else if events[3] != "" {
t.Error("Event log didn't end with newline", events)
2020-08-18 17:04:23 -06:00
}
}
func TestDevelState(t *testing.T) {
s := NewTestState()
ds := NewDevelState(s)
if err := ds.SetTeamName("boog", "The Boog Team"); err != ErrAlreadyRegistered {
t.Error("Registering a team that doesn't exist", err)
} else if err == nil {
t.Error("Registering a team that doesn't exist didn't return ErrAlreadyRegistered")
}
if n, err := ds.TeamName("boog"); err != nil {
t.Error("Devel State returned error on team name lookup")
} else if n != "«devel:boog»" {
t.Error("Wrong team name", n)
}
if err := ds.AwardPoints("blerg", "dog", 82); err != nil {
t.Error("Devel State AwardPoints returned an error", err)
}
}