add more tests
This commit is contained in:
parent
74add408e6
commit
4a528fe85a
9 changed files with 266 additions and 55 deletions
|
@ -17,17 +17,22 @@ func main() {
|
|||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
playerStore := store.NewPlayerStore(db)
|
||||
vehicleStore := store.NewVehicleStore(db)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
run(ctx, playerStore, vehicleStore)
|
||||
}
|
||||
|
||||
func run(ctx context.Context, ps store.IPlayerStore, vs store.IVehicleStore) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
playerStore := store.NewPlayerStore(db)
|
||||
i, err := playerStore.Prune(ctx)
|
||||
i, err := ps.Prune(ctx)
|
||||
if err != nil {
|
||||
log.Println("pruner: playerStore.Prune failed")
|
||||
}
|
||||
|
@ -38,9 +43,7 @@ func main() {
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
vehicleStore := store.NewVehicleStore(db)
|
||||
i, err := vehicleStore.Prune(ctx)
|
||||
i, err := vs.Prune(ctx)
|
||||
if err != nil {
|
||||
log.Println("pruner: vehicleStore.Prune failed")
|
||||
}
|
||||
|
|
22
cmd/pruner/pruner_test.go
Normal file
22
cmd/pruner/pruner_test.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/genudine/saerro-go/store/storemock"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
ps := new(storemock.MockPlayerStore)
|
||||
vs := new(storemock.MockVehicleStore)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
ps.On("Prune", ctx).Return(30, nil)
|
||||
vs.On("Prune", ctx).Return(30, nil)
|
||||
|
||||
run(ctx, ps, vs)
|
||||
}
|
128
cmd/ws/eventhandler/ess_bench_test.go
Normal file
128
cmd/ws/eventhandler/ess_bench_test.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package eventhandler_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/genudine/saerro-go/cmd/ws/eventhandler"
|
||||
"github.com/genudine/saerro-go/translators"
|
||||
"github.com/genudine/saerro-go/types"
|
||||
"github.com/genudine/saerro-go/util"
|
||||
)
|
||||
|
||||
const PreloadedCharacterCount = 45
|
||||
|
||||
var (
|
||||
characterStore = make([]string, PreloadedCharacterCount)
|
||||
db *sql.DB
|
||||
)
|
||||
|
||||
func BenchmarkESS(b *testing.B) {
|
||||
events, eh := prebench(b)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
event := events[i]
|
||||
|
||||
eh.HandleEvent(context.Background(), event)
|
||||
}
|
||||
}
|
||||
|
||||
func prebench(b *testing.B) ([]types.ESSEvent, eventhandler.EventHandler) {
|
||||
b.Helper()
|
||||
b.StopTimer()
|
||||
|
||||
// Create our character pool
|
||||
for i := 0; i < PreloadedCharacterCount; i++ {
|
||||
characterStore[i] = mkRandomCharacterID()
|
||||
}
|
||||
|
||||
// Create events
|
||||
events := []types.ESSEvent{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
events = append(events, mkRandomEvent())
|
||||
}
|
||||
|
||||
if db == nil {
|
||||
var err error
|
||||
db, err = util.GetDBConnection(os.Getenv("DB_ADDR"))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
eh := eventhandler.NewEventHandler(db)
|
||||
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
|
||||
return events, eh
|
||||
}
|
||||
|
||||
func mkRandomEvent() types.ESSEvent {
|
||||
w := rand.Intn(4)
|
||||
z := rand.Intn(7)
|
||||
|
||||
switch rand.Intn(2) {
|
||||
case 0:
|
||||
return mkRandomDeathEvent(w, z)
|
||||
default:
|
||||
return mkRandomExpEvent(w, z)
|
||||
}
|
||||
}
|
||||
|
||||
func mkRandomDeathEvent(world, zone int) types.ESSEvent {
|
||||
return types.ESSEvent{
|
||||
EventName: "Death",
|
||||
WorldID: uint16(world),
|
||||
ZoneID: uint32(zone),
|
||||
|
||||
CharacterID: getRandomCharacterID(),
|
||||
CharacterLoadoutID: mkRandomLoadout(),
|
||||
TeamID: mkRandomFaction(),
|
||||
|
||||
AttackerCharacterID: getRandomCharacterID(),
|
||||
AttackerLoadoutID: mkRandomLoadout(),
|
||||
AttackerTeamID: mkRandomFaction(),
|
||||
}
|
||||
}
|
||||
|
||||
func mkRandomExpEvent(world, zone int) types.ESSEvent {
|
||||
return types.ESSEvent{
|
||||
EventName: "GainExperience",
|
||||
WorldID: uint16(world),
|
||||
ZoneID: uint32(zone),
|
||||
|
||||
CharacterID: getRandomCharacterID(),
|
||||
LoadoutID: mkRandomLoadout(),
|
||||
TeamID: mkRandomFaction(),
|
||||
|
||||
ExperienceID: rand.Uint32() % 256,
|
||||
}
|
||||
}
|
||||
|
||||
func getRandomCharacterID() string {
|
||||
i := rand.Intn(PreloadedCharacterCount)
|
||||
return characterStore[i]
|
||||
}
|
||||
|
||||
func mkRandomCharacterID() string {
|
||||
return strconv.Itoa(rand.Int())
|
||||
}
|
||||
|
||||
func mkRandomFaction() types.Faction {
|
||||
return types.Faction(rand.Intn(4))
|
||||
}
|
||||
|
||||
func mkRandomLoadout() uint16 {
|
||||
for {
|
||||
i := rand.Intn(46)
|
||||
_, ok := translators.LoadoutMap[uint16(i)]
|
||||
if ok {
|
||||
return uint16(i)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,8 +9,15 @@ import (
|
|||
"github.com/genudine/saerro-go/types"
|
||||
)
|
||||
|
||||
type IEventHandler interface {
|
||||
HandleEvent(ctx context.Context, event types.ESSEvent)
|
||||
HandleDeath(ctx context.Context, event types.ESSEvent)
|
||||
HandleExperience(ctx context.Context, event types.ESSEvent)
|
||||
HandleAnalytics(ctx context.Context, event types.ESSEvent)
|
||||
}
|
||||
|
||||
type EventHandler struct {
|
||||
Ingest *ingest.Ingest
|
||||
Ingest ingest.IIngest
|
||||
}
|
||||
|
||||
func NewEventHandler(db *sql.DB) EventHandler {
|
||||
|
|
|
@ -5,32 +5,28 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/avast/retry-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/genudine/saerro-go/cmd/ws/ingest"
|
||||
"github.com/genudine/saerro-go/store"
|
||||
"github.com/genudine/saerro-go/translators"
|
||||
"github.com/genudine/saerro-go/store/storemock"
|
||||
"github.com/genudine/saerro-go/types"
|
||||
"github.com/genudine/saerro-go/util/testutil"
|
||||
)
|
||||
|
||||
func getEventHandlerTestShim(t *testing.T) (EventHandler, context.Context) {
|
||||
func getEventHandlerTestShim(t *testing.T) (EventHandler, context.Context, *storemock.MockPlayerStore) {
|
||||
t.Helper()
|
||||
|
||||
db := testutil.GetTestDB(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
ps := new(storemock.MockPlayerStore)
|
||||
|
||||
return EventHandler{
|
||||
Ingest: &ingest.Ingest{
|
||||
PlayerStore: store.NewPlayerStore(db),
|
||||
PlayerStore: ps,
|
||||
},
|
||||
}, ctx
|
||||
}, ctx, ps
|
||||
}
|
||||
|
||||
func TestHandleDeath(t *testing.T) {
|
||||
eh, ctx := getEventHandlerTestShim(t)
|
||||
eh, ctx, ps := getEventHandlerTestShim(t)
|
||||
|
||||
event := types.ESSEvent{
|
||||
EventName: "Death",
|
||||
|
@ -46,21 +42,17 @@ func TestHandleDeath(t *testing.T) {
|
|||
AttackerTeamID: types.TR,
|
||||
}
|
||||
|
||||
p1 := types.PopEventFromESSEvent(event, false).ToPlayer()
|
||||
p2 := types.PopEventFromESSEvent(event, true).ToPlayer()
|
||||
|
||||
ps.On("Insert", ctx, p1).Return(nil)
|
||||
ps.On("Insert", ctx, p2).Return(nil)
|
||||
|
||||
eh.HandleDeath(ctx, event)
|
||||
|
||||
player1, err := eh.Ingest.PlayerStore.GetOne(ctx, event.CharacterID)
|
||||
assert.NoError(t, err, "player1 fetch failed")
|
||||
assert.Equal(t, event.CharacterID, player1.CharacterID)
|
||||
assert.Equal(t, string(translators.ClassFromLoadout(event.LoadoutID)), player1.ClassName)
|
||||
|
||||
player2, err := eh.Ingest.PlayerStore.GetOne(ctx, event.AttackerCharacterID)
|
||||
assert.NoError(t, err, "player2 fetch failed")
|
||||
assert.Equal(t, event.AttackerCharacterID, player2.CharacterID)
|
||||
assert.Equal(t, string(translators.ClassFromLoadout(event.AttackerLoadoutID)), player2.ClassName)
|
||||
}
|
||||
|
||||
func TestHandleExperience(t *testing.T) {
|
||||
eh, ctx := getEventHandlerTestShim(t)
|
||||
eh, ctx, ps := getEventHandlerTestShim(t)
|
||||
|
||||
event := types.ESSEvent{
|
||||
EventName: "GainExperience",
|
||||
|
@ -74,15 +66,14 @@ func TestHandleExperience(t *testing.T) {
|
|||
ExperienceID: 674,
|
||||
}
|
||||
|
||||
p := types.PopEventFromESSEvent(event, false).ToPlayer()
|
||||
ps.On("Insert", ctx, p).Return(nil)
|
||||
|
||||
eh.HandleExperience(ctx, event)
|
||||
player, err := eh.Ingest.PlayerStore.GetOne(ctx, event.CharacterID)
|
||||
assert.NoError(t, err, "player fetch check failed")
|
||||
assert.Equal(t, event.CharacterID, player.CharacterID)
|
||||
assert.Equal(t, string(translators.ClassFromLoadout(event.LoadoutID)), player.ClassName)
|
||||
}
|
||||
|
||||
func TestHandleAnalytics(t *testing.T) {
|
||||
eh, ctx := getEventHandlerTestShim(t)
|
||||
eh, ctx, _ := getEventHandlerTestShim(t)
|
||||
event := types.ESSEvent{
|
||||
EventName: "GainExperience",
|
||||
WorldID: 17,
|
||||
|
@ -99,7 +90,7 @@ func TestHandleAnalytics(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHandleEvent(t *testing.T) {
|
||||
eh, ctx := getEventHandlerTestShim(t)
|
||||
eh, ctx, ps := getEventHandlerTestShim(t)
|
||||
|
||||
events := []types.ESSEvent{
|
||||
{
|
||||
|
@ -107,9 +98,9 @@ func TestHandleEvent(t *testing.T) {
|
|||
WorldID: 17,
|
||||
ZoneID: 2,
|
||||
|
||||
CharacterID: "LyytisDoll",
|
||||
LoadoutID: 3,
|
||||
TeamID: types.NC,
|
||||
CharacterID: "LyytisDoll",
|
||||
CharacterLoadoutID: 3,
|
||||
TeamID: types.NC,
|
||||
|
||||
AttackerCharacterID: "Lyyti",
|
||||
AttackerLoadoutID: 3,
|
||||
|
@ -126,19 +117,19 @@ func TestHandleEvent(t *testing.T) {
|
|||
|
||||
ExperienceID: 201,
|
||||
},
|
||||
{
|
||||
EventName: "",
|
||||
},
|
||||
}
|
||||
|
||||
p1 := types.PopEventFromESSEvent(events[0], false).ToPlayer()
|
||||
ps.On("Insert", ctx, p1).Return(nil).Once()
|
||||
p2 := types.PopEventFromESSEvent(events[0], true).ToPlayer()
|
||||
ps.On("Insert", ctx, p2).Return(nil).Once()
|
||||
p3 := types.PopEventFromESSEvent(events[1], false).ToPlayer()
|
||||
ps.On("Insert", ctx, p3).Return(nil).Once()
|
||||
|
||||
for _, event := range events {
|
||||
eh.HandleEvent(ctx, event)
|
||||
}
|
||||
|
||||
checkPlayers := []string{"LyytisDoll", "Lyyti", "DollNC"}
|
||||
for _, id := range checkPlayers {
|
||||
// eventual consistency <333
|
||||
err := retry.Do(func() error {
|
||||
_, err := eh.Ingest.PlayerStore.GetOne(ctx, id)
|
||||
return err
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,13 @@ import (
|
|||
"github.com/genudine/saerro-go/types"
|
||||
)
|
||||
|
||||
type IIngest interface {
|
||||
TrackPop(context.Context, types.PopEvent)
|
||||
}
|
||||
|
||||
type Ingest struct {
|
||||
PlayerStore store.IPlayerStore
|
||||
PlayerStore store.IPlayerStore
|
||||
VehicleStore store.IVehicleStore
|
||||
}
|
||||
|
||||
func (i *Ingest) TrackPop(ctx context.Context, event types.PopEvent) {
|
||||
|
|
|
@ -12,23 +12,25 @@ import (
|
|||
"github.com/genudine/saerro-go/types"
|
||||
)
|
||||
|
||||
func mkIngest(t *testing.T) (context.Context, *ingest.Ingest, *storemock.MockPlayerStore) {
|
||||
func mkIngest(t *testing.T) (context.Context, *ingest.Ingest, *storemock.MockPlayerStore, *storemock.MockVehicleStore) {
|
||||
t.Helper()
|
||||
|
||||
ps := new(storemock.MockPlayerStore)
|
||||
vs := new(storemock.MockVehicleStore)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
i := &ingest.Ingest{
|
||||
PlayerStore: ps,
|
||||
PlayerStore: ps,
|
||||
VehicleStore: vs,
|
||||
}
|
||||
|
||||
return ctx, i, ps
|
||||
return ctx, i, ps, vs
|
||||
}
|
||||
|
||||
func TestTrackPopHappyPath(t *testing.T) {
|
||||
ctx, i, ps := mkIngest(t)
|
||||
ctx, i, ps, _ := mkIngest(t)
|
||||
|
||||
// Combat Medic on Emerald
|
||||
event := types.PopEvent{
|
||||
|
@ -48,7 +50,7 @@ func TestTrackPopHappyPath(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTrackPopFixup(t *testing.T) {
|
||||
ctx, i, ps := mkIngest(t)
|
||||
ctx, i, ps, _ := mkIngest(t)
|
||||
|
||||
event := types.PopEvent{
|
||||
WorldID: 17,
|
||||
|
@ -68,7 +70,7 @@ func TestTrackPopFixup(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTrackPopFixupFailed(t *testing.T) {
|
||||
ctx, i, ps := mkIngest(t)
|
||||
ctx, i, ps, _ := mkIngest(t)
|
||||
|
||||
event := types.PopEvent{
|
||||
WorldID: 17,
|
||||
|
|
45
store/storemock/vehiclestore.go
Normal file
45
store/storemock/vehiclestore.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package storemock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/genudine/saerro-go/types"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockVehicleStore struct {
|
||||
mock.Mock
|
||||
|
||||
DB *sql.DB
|
||||
RanMigration bool
|
||||
}
|
||||
|
||||
func (m *MockVehicleStore) IsMigrated(ctx context.Context) bool {
|
||||
args := m.Called(ctx)
|
||||
return args.Bool(0)
|
||||
}
|
||||
|
||||
func (m *MockVehicleStore) RunMigration(ctx context.Context, force bool) {
|
||||
m.Called(ctx, force)
|
||||
}
|
||||
|
||||
func (m *MockVehicleStore) Insert(ctx context.Context, vehicle *types.Vehicle) error {
|
||||
args := m.Called(ctx, vehicle)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockVehicleStore) GetOne(ctx context.Context, id string) (*types.Vehicle, error) {
|
||||
args := m.Called(ctx, id)
|
||||
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
return args.Get(0).(*types.Vehicle), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockVehicleStore) Prune(ctx context.Context) (int64, error) {
|
||||
args := m.Called(ctx)
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
|
@ -11,6 +11,14 @@ import (
|
|||
"github.com/avast/retry-go"
|
||||
)
|
||||
|
||||
type IVehicleStore interface {
|
||||
IsMigrated(context.Context) bool
|
||||
RunMigration(context.Context, bool)
|
||||
Insert(context.Context, *types.Vehicle) error
|
||||
GetOne(context.Context, string) (*types.Vehicle, error)
|
||||
Prune(context.Context) (int64, error)
|
||||
}
|
||||
|
||||
type VehicleStore struct {
|
||||
DB *sql.DB
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue