bingobot/internal/state/state_test.go

347 lines
7.3 KiB
Go

package state
import (
"fmt"
"os"
"testing"
"time"
"gitlab.com/whom/bingobot/internal/logging"
"gitlab.com/whom/bingobot/pkg/docbuf"
)
/* WARNING:
* Cannot run these tests in parallel!
* limitation of SetupTest and CleanupTest
*/
const TestTok = "TEST_NAME"
var loggingInitialized = false
func SetupTest(t *testing.T) {
maxEventsInMemory = 271
var err error
// have to set up logger
if !loggingInitialized {
logging.Init()
loggingInitialized = true
}
b := docbuf.NewReadWriteSeekString()
eventStream, err = docbuf.NewDocumentBuffer(300, &b)
if err != nil {
t.Fatalf("error allocating buffer: %e", err)
}
}
func TestEventMarshalUnmarshal(t *testing.T) {
tm := time.Now()
cev := ChallengeEvent{UserEvent{
uid: TestTok,
created: tm,
}}
vev := VoteEvent(map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusFinalized,
VoteResultKey: VoteResultFail,
})
cstr, err := EventToString(cev);
if err != nil {
t.Fatalf("error marshalling challenge: %e, %s", err, cstr)
}
vstr, err := EventToString(vev);
if err != nil {
t.Fatalf("error marshalling vote: %e, %s", err, vstr)
}
if ev, err := EventFromString(cstr); err != nil ||
ev.Data()[UserEventUserKey] != cev.Data()[UserEventUserKey] ||
ev.Data()[UserEventCreatedKey] != cev.Data()[UserEventCreatedKey] {
t.Fatalf("error unmarshalling challenge: %e, %v!=%v", err, ev, cev)
}
if ev, err := EventFromString(vstr); err != nil ||
fmt.Sprint(ev) != fmt.Sprint(vev) {
t.Fatalf("error unmarshalling vote: %e, %v!=%v", err, ev, vev)
}
}
func TestPubSub(t *testing.T) {
SetupTest(t)
c, e := UserActive.Subscribe()
if e != nil {
t.Errorf("Error subscribing to UserActive events: %e", e)
}
old, _ := time.Parse(
time.RFC3339,
VeryOldVote,
)
for i := range 270 {
if err := PublishEvent(UserActiveEvent{UserEvent{
uid: fmt.Sprintf("%d", i),
created: old,
}}); err != nil {
t.Errorf("Failed to add event: %e", err)
}
}
PublishEvent(UserActiveEvent{UserEvent{
uid: fmt.Sprintf(TestTok),
created: time.Now(),
}})
PublishEvent(ChallengeEvent{UserEvent{
uid: fmt.Sprintf(TestTok),
created: time.Now(),
}})
Loop:
for i := 0; true; i++ {
select {
case e, ok := <-c:
if !ok {
t.Fatalf("Subscription Channel Closed")
}
if e.Type() != UserActive {
t.Fatalf("Non UserActive Event in UserActive subscription: %v", e.Type())
}
default:
if i == maxEventsInMemory {
break Loop
} else {
t.Fatalf("Unexpected number of events in channel: %d", i)
}
}
}
PublishEvent(UserActiveEvent{UserEvent{
uid: "uniqueToken",
created: time.Now(),
}})
select {
case e, ok := <-c:
if !ok || e.Data()[UserEventUserKey] != "uniqueToken" {
t.Fatalf("didnt read correct event from channel: %v", e)
}
default:
t.Fatalf("New event not published to subscription!")
}
}
func TestFilterCache(t *testing.T) {
SetupTest(t)
old, _ := time.Parse(
time.RFC3339,
VeryOldVote,
)
for i := range 270 {
if err := PublishEvent(UserActiveEvent{UserEvent{
uid: fmt.Sprintf("%d", i),
created: old,
}}); err != nil {
t.Errorf("Failed to add event: %e", err)
}
}
PublishEvent(UserActiveEvent{UserEvent{
uid: fmt.Sprintf(TestTok),
created: time.Now(),
}})
PublishEvent(ChallengeEvent{UserEvent{
uid: fmt.Sprintf(TestTok),
created: time.Now(),
}})
events, err := GetMatchingEvents(
UserActive,
map[string]string{
UserEventUserKey: TestTok,
},
)
if err != nil {
t.Errorf("Error filtering events: %e", err)
}
if len(events) != 1 {
t.Errorf("Got too many events from filter: %d", len(events))
}
if events[0].Type() != UserActive {
t.Errorf("Got wrong event!: %+v", events[0])
}
}
func TestVoteEventValidations(t *testing.T) {
var err error
if err = VoteEvent(
map[string]string{
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusInProgress,
},
).Validate(); err.Error() != VoteMissingKeyError+VoteActionKey {
t.Errorf("Unexpected error from validation: %e", err)
}
if err = VoteEvent(
map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusInProgress,
},
).Validate(); err != nil {
t.Errorf("Unexpected error: %e", err)
}
if err = VoteEvent(
map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: "s",
},
).Validate(); err.Error() != VoteBadStatusError+"s" {
t.Errorf("Unexpected or no error: %e", err)
}
if err = VoteEvent(
map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusInProgress,
VoteResultKey: VoteResultFail,
},
).Validate(); err.Error() != VoteNotFinishedError {
t.Errorf("Unexpected or no error: %e", err)
}
if err = VoteEvent(
map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusFinalized,
},
).Validate(); err.Error() != VoteMissingResultError {
t.Errorf("Unexpected or no error: %e", err)
}
if err = VoteEvent(
map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusFinalized,
VoteResultKey: "r",
},
).Validate(); err.Error() != VoteBadResultError+"r" {
t.Errorf("Unexpected or no error: %e", err)
}
if err = VoteEvent(
map[string]string{
VoteActionKey: "a",
VoteRequesterKey: "r",
VoteCreatedKey: "c",
VoteStatusKey: VoteStatusFinalized,
VoteResultKey: VoteResultFail,
},
).Validate(); err != nil {
t.Errorf("Unexpected or no error: %e", err)
}
}
func TestEventReplay(t *testing.T) {
tmpTestFile, err := os.CreateTemp("", "TestEventReplayBackingStore")
if err != nil {
t.Fatalf("failed setting up tempfile: %s", err.Error())
}
if err := tmpTestFile.Close(); err != nil {
t.Fatalf("failed to close initial handle to tempfile: %s", err.Error())
}
if err := Init(2, tmpTestFile.Name()); err != nil {
t.Fatalf("failed initializing state machine: %s", err.Error())
}
if err := PublishEvent(TestEvent{
ID: 1,
Dispose: true,
}); err != nil {
t.Fatalf("failed to publish event 1: %s", err.Error())
}
if err := PublishEvent(TestEvent{
ID: 2,
Dispose: false,
}); err != nil {
t.Fatalf("failed to publish event 2: %s", err.Error())
}
if err := PublishEvent(TestEvent{
ID: 3,
Dispose: true,
}); err != nil {
t.Fatalf("failed to publish event 3: %s", err.Error())
}
if err := PublishEvent(TestEvent{
ID: 4,
Dispose: false,
}); err != nil {
t.Fatalf("failed to publish event 4: %s", err.Error())
}
sub, err := Test.Subscribe()
if err := Start(); err != nil {
t.Fatalf("failed to start state machine: %s", err.Error())
}
select {
case ev1 := <- sub:
if ev1.Type() != Test {
t.Fatalf("wrong kind of event somehow: %+v", ev1)
}
if ev1.(TestEvent).ID != 2 {
t.Fatalf("misordered event: %+v", ev1)
}
default:
t.Fatal("no events available from replay")
}
select {
case ev2 := <- sub:
if ev2.Type() != Test {
t.Fatalf("wrong kind of event somehow: %+v", ev2)
}
if ev2.(TestEvent).ID != 4 {
t.Fatalf("misordered event: %+v", ev2)
}
default:
t.Fatal("only one event made it out")
}
select {
case <- sub:
t.Fatalf("superfluous events left in subscription")
default:
}
}