bingobot/internal/state/state_test.go
Ava Affine 0a29b35f4b Refactor state module to leverage DocumentBuffer
This commit refactors the state module to remove the eventCache and
introduce an eventStream instead. The eventStream is not a static array
but a DocumentBuffer, and thus many functions had to be altered. This
commit is best reviewed side by side with a before/after view instead of
just looking at the diff.

An Init() function is added to initialize the DocumentBuffer with config
values.

Facilities are added to both Event and EventType to allow for parsing
them to and from string documents.

a MakeEvent() function is added to create events of proper subtype based
on a general data map input.

ApplyToEvents() is added, which wraps around DocumentBuffer's Apply() method
to alter the filter input type with one that Marshals and Unmarshals events.

GetMatchingEvents() is now a proof of concept for DocumentBuffer's Apply()

PruneCache() is now no longer needed.

Signed-off-by: Ava Affine <ava@sunnypup.io>
2024-12-05 16:40:34 -08:00

272 lines
5.7 KiB
Go

package state
import (
"fmt"
"testing"
"time"
"gitlab.com/whom/bingobot/internal/docbuf"
"gitlab.com/whom/bingobot/internal/logging"
)
/* 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)
}
t.Logf("cstr: %s\n", cstr)
vstr, err := EventToString(vev);
if err != nil {
t.Fatalf("error marshalling vote: %e, %s", err, vstr)
}
t.Logf("vstr: %s\n", 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)
}
}