mirror of https://github.com/dirtbags/moth.git
Merge branch '179-intermittent-test-failures-on-gitlab-ci' into 'main'
Resolve "Intermittent test failures on gitlab CI" Closes #179 See merge request devs/moth!177
This commit is contained in:
commit
a387a06ae5
|
@ -4,13 +4,14 @@ stages:
|
||||||
|
|
||||||
test:
|
test:
|
||||||
stage: test
|
stage: test
|
||||||
|
|
||||||
image: golang:1.18
|
image: golang:1.18
|
||||||
only:
|
only:
|
||||||
refs:
|
refs:
|
||||||
- main
|
- main
|
||||||
- merge_requests
|
- merge_requests
|
||||||
script:
|
script:
|
||||||
- go test ./...
|
- go test -race ./...
|
||||||
|
|
||||||
push:
|
push:
|
||||||
stage: push
|
stage: push
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
@ -34,8 +33,7 @@ func (hs *HTTPServer) TestRequest(path string, args map[string]string) *httptest
|
||||||
|
|
||||||
func TestHttpd(t *testing.T) {
|
func TestHttpd(t *testing.T) {
|
||||||
server := NewTestServer()
|
server := NewTestServer()
|
||||||
hs := NewHTTPServer("/", server)
|
hs := NewHTTPServer("/", server.MothServer)
|
||||||
stateProvider := server.State.(*State)
|
|
||||||
|
|
||||||
if r := hs.TestRequest("/", nil); r.Result().StatusCode != 200 {
|
if r := hs.TestRequest("/", nil); r.Result().StatusCode != 200 {
|
||||||
t.Error(r.Result())
|
t.Error(r.Result())
|
||||||
|
@ -72,7 +70,7 @@ func TestHttpd(t *testing.T) {
|
||||||
t.Error("Register failed", r.Body.String())
|
t.Error("Register failed", r.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(TestMaintenanceInterval)
|
server.refresh()
|
||||||
|
|
||||||
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())
|
||||||
|
@ -114,8 +112,7 @@ func TestHttpd(t *testing.T) {
|
||||||
t.Error("Unexpected body", r.Body.String())
|
t.Error("Unexpected body", r.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(TestMaintenanceInterval)
|
server.refresh()
|
||||||
stateProvider.refresh()
|
|
||||||
|
|
||||||
if r := hs.TestRequest("/content/pategory/2/puzzle.json", nil); r.Result().StatusCode != 200 {
|
if r := hs.TestRequest("/content/pategory/2/puzzle.json", nil); r.Result().StatusCode != 200 {
|
||||||
t.Error(r.Result())
|
t.Error(r.Result())
|
||||||
|
@ -127,7 +124,7 @@ func TestHttpd(t *testing.T) {
|
||||||
} else if err := json.Unmarshal(r.Body.Bytes(), &state); err != nil {
|
} else if err := json.Unmarshal(r.Body.Bytes(), &state); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else if len(state.PointsLog) != 1 {
|
} else if len(state.PointsLog) != 1 {
|
||||||
t.Error("Points log wrong length")
|
t.Errorf("Points log wrong length. Wanted 1, got %v (length %d)", state.PointsLog, len(state.PointsLog))
|
||||||
} else if len(state.Puzzles["pategory"]) != 2 {
|
} else if len(state.Puzzles["pategory"]) != 2 {
|
||||||
t.Error("Didn't unlock next puzzle")
|
t.Error("Didn't unlock next puzzle")
|
||||||
}
|
}
|
||||||
|
@ -143,7 +140,7 @@ func TestDevelMemHttpd(t *testing.T) {
|
||||||
srv := NewTestServer()
|
srv := NewTestServer()
|
||||||
|
|
||||||
{
|
{
|
||||||
hs := NewHTTPServer("/", srv)
|
hs := NewHTTPServer("/", srv.MothServer)
|
||||||
|
|
||||||
if r := hs.TestRequest("/mothballer/pategory.md", nil); r.Result().StatusCode != 404 {
|
if r := hs.TestRequest("/mothballer/pategory.md", nil); r.Result().StatusCode != 404 {
|
||||||
t.Error("Should have gotten a 404 for mothballer in prod mode")
|
t.Error("Should have gotten a 404 for mothballer in prod mode")
|
||||||
|
@ -152,7 +149,7 @@ func TestDevelMemHttpd(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
srv.Config.Devel = true
|
srv.Config.Devel = true
|
||||||
hs := NewHTTPServer("/", srv)
|
hs := NewHTTPServer("/", srv.MothServer)
|
||||||
|
|
||||||
if r := hs.TestRequest("/mothballer/pategory.md", nil); r.Result().StatusCode != 500 {
|
if r := hs.TestRequest("/mothballer/pategory.md", nil); r.Result().StatusCode != 500 {
|
||||||
t.Log(r.Body.String())
|
t.Log(r.Body.String())
|
||||||
|
|
|
@ -68,6 +68,9 @@ type Maintainer interface {
|
||||||
// It will only be called once, when execution begins.
|
// It will only be called once, when execution begins.
|
||||||
// It's okay to just exit if there's no maintenance to be done.
|
// It's okay to just exit if there's no maintenance to be done.
|
||||||
Maintain(updateInterval time.Duration)
|
Maintain(updateInterval time.Duration)
|
||||||
|
|
||||||
|
// refresh is a shortcut used internally for testing
|
||||||
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MothServer gathers together the providers that make up a MOTH server.
|
// MothServer gathers together the providers that make up a MOTH server.
|
||||||
|
|
|
@ -3,30 +3,40 @@ package main
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TestMaintenanceInterval = time.Millisecond * 1
|
|
||||||
const TestTeamID = "teamID"
|
const TestTeamID = "teamID"
|
||||||
|
|
||||||
func NewTestServer() *MothServer {
|
type TestServer struct {
|
||||||
|
*MothServer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestServer creates a new MothServer with NewTestMothballs and some initial state.
|
||||||
|
//
|
||||||
|
// See function definition for details.
|
||||||
|
func NewTestServer() TestServer {
|
||||||
puzzles := NewTestMothballs()
|
puzzles := NewTestMothballs()
|
||||||
puzzles.refresh()
|
puzzles.refresh()
|
||||||
go puzzles.Maintain(TestMaintenanceInterval)
|
|
||||||
|
|
||||||
state := NewTestState()
|
state := NewTestState()
|
||||||
afero.WriteFile(state, "teamids.txt", []byte("teamID\n"), 0644)
|
afero.WriteFile(state, "teamids.txt", []byte("teamID\n"), 0644)
|
||||||
afero.WriteFile(state, "messages.html", []byte("messages.html"), 0644)
|
afero.WriteFile(state, "messages.html", []byte("messages.html"), 0644)
|
||||||
state.refresh()
|
state.refresh()
|
||||||
go state.Maintain(TestMaintenanceInterval)
|
|
||||||
|
|
||||||
theme := NewTestTheme()
|
theme := NewTestTheme()
|
||||||
afero.WriteFile(theme.Fs, "/index.html", []byte("index.html"), 0644)
|
afero.WriteFile(theme.Fs, "/index.html", []byte("index.html"), 0644)
|
||||||
go theme.Maintain(TestMaintenanceInterval)
|
|
||||||
|
|
||||||
return NewMothServer(Configuration{}, theme, state, puzzles)
|
return TestServer{NewMothServer(Configuration{}, theme, state, puzzles)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts TestServer) refresh() {
|
||||||
|
ts.State.(*State).refresh()
|
||||||
|
for _, pp := range ts.PuzzleProviders {
|
||||||
|
pp.(*Mothballs).refresh()
|
||||||
|
}
|
||||||
|
ts.Theme.(*Theme).refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDevelServer(t *testing.T) {
|
func TestDevelServer(t *testing.T) {
|
||||||
|
@ -82,8 +92,7 @@ func TestProdServer(t *testing.T) {
|
||||||
t.Error("index.html wrong contents", contents)
|
t.Error("index.html wrong contents", contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for refresh to pick everything up
|
server.refresh()
|
||||||
time.Sleep(TestMaintenanceInterval)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
es := handler.ExportState()
|
es := handler.ExportState()
|
||||||
|
@ -136,7 +145,7 @@ func TestProdServer(t *testing.T) {
|
||||||
t.Error("Right answer marked wrong", err)
|
t.Error("Right answer marked wrong", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(TestMaintenanceInterval)
|
server.refresh()
|
||||||
|
|
||||||
{
|
{
|
||||||
es := handler.ExportState()
|
es := handler.ExportState()
|
||||||
|
@ -165,7 +174,7 @@ func TestProdServer(t *testing.T) {
|
||||||
t.Error("Right answer marked wrong:", err)
|
t.Error("Right answer marked wrong:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(TestMaintenanceInterval)
|
server.refresh()
|
||||||
|
|
||||||
{
|
{
|
||||||
es := anonHandler.ExportState()
|
es := anonHandler.ExportState()
|
||||||
|
|
|
@ -38,3 +38,7 @@ func (t *Theme) Open(name string) (ReadSeekCloser, time.Time, error) {
|
||||||
func (t *Theme) Maintain(i time.Duration) {
|
func (t *Theme) Maintain(i time.Duration) {
|
||||||
// No periodic tasks for a theme
|
// No periodic tasks for a theme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Theme) refresh() {
|
||||||
|
// Nothing to do for a theme
|
||||||
|
}
|
||||||
|
|
|
@ -79,3 +79,7 @@ func (p TranspilerProvider) Mothball(cat string, w io.Writer) error {
|
||||||
func (p TranspilerProvider) Maintain(updateInterval time.Duration) {
|
func (p TranspilerProvider) Maintain(updateInterval time.Duration) {
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p TranspilerProvider) refresh() {
|
||||||
|
// Nothing to do for a theme
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue