347 lines
7.3 KiB
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:
|
|
}
|
|
}
|