2019-08-22 09:28:43 +00:00
|
|
|
package draftbot_test
|
2019-06-28 09:03:42 +00:00
|
|
|
|
|
|
|
import (
|
2019-06-28 21:07:05 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"os"
|
2019-06-28 09:03:42 +00:00
|
|
|
"testing"
|
2019-06-28 14:15:25 +00:00
|
|
|
"time"
|
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
"git.fromouter.space/mcg/draft"
|
2019-06-28 14:15:25 +00:00
|
|
|
"git.fromouter.space/mcg/draft/mlp"
|
2019-06-28 21:07:05 +00:00
|
|
|
mlptestdata "git.fromouter.space/mcg/draft/mlp/testdata"
|
|
|
|
|
|
|
|
draftbot "git.fromouter.space/mcg/mlp-server-tools/draftbot"
|
|
|
|
testdata "git.fromouter.space/mcg/mlp-server-tools/draftbot/testdata"
|
2019-06-28 09:03:42 +00:00
|
|
|
|
|
|
|
"git.fromouter.space/mcg/cardgage/client/bot"
|
2019-06-28 14:15:25 +00:00
|
|
|
lobby "git.fromouter.space/mcg/cardgage/lobby/proto"
|
2019-06-28 09:03:42 +00:00
|
|
|
room "git.fromouter.space/mcg/cardgage/room/api"
|
|
|
|
)
|
|
|
|
|
2019-06-28 14:15:25 +00:00
|
|
|
const TestBotName = "test-bot"
|
|
|
|
const TestRoomName = "test-room"
|
|
|
|
|
2019-06-28 09:03:42 +00:00
|
|
|
type MockServer struct {
|
2019-06-28 21:07:05 +00:00
|
|
|
in chan room.ServerMessage
|
|
|
|
out chan room.BotMessage
|
|
|
|
t *testing.T
|
|
|
|
timeout time.Duration
|
|
|
|
roomName string
|
2019-06-28 09:03:42 +00:00
|
|
|
}
|
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
func makeMockServer(t *testing.T, timeout int) *MockServer {
|
2019-06-28 09:03:42 +00:00
|
|
|
srv := &MockServer{
|
2019-07-01 11:02:40 +00:00
|
|
|
in: make(chan room.ServerMessage, 99),
|
|
|
|
out: make(chan room.BotMessage, 99),
|
2019-06-28 21:07:05 +00:00
|
|
|
t: t,
|
|
|
|
timeout: time.Duration(timeout) * time.Second,
|
|
|
|
roomName: TestRoomName,
|
2019-06-28 09:03:42 +00:00
|
|
|
}
|
|
|
|
return srv
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockServer) Send(msg room.BotMessage) {
|
|
|
|
m.out <- msg
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockServer) Bind(fn bot.MessageHandler) {
|
|
|
|
for msg := range m.in {
|
|
|
|
fn(msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDraftSession(t *testing.T) {
|
2019-06-28 16:19:02 +00:00
|
|
|
mock := makeMockServer(t, 5)
|
|
|
|
drafter := draftbot.NewDraftBot(mock, TestBotName)
|
|
|
|
go mock.Bind(drafter.OnMessage)
|
2019-06-28 09:03:42 +00:00
|
|
|
|
2019-06-28 14:15:25 +00:00
|
|
|
// Create a new room
|
|
|
|
mock.in <- room.ServerMessage{
|
|
|
|
RoomID: TestRoomName,
|
|
|
|
Type: room.MsgEvent,
|
|
|
|
Event: &room.Event{
|
|
|
|
Type: room.EvtNewRoom,
|
|
|
|
Room: &lobby.Room{
|
|
|
|
Id: TestRoomName,
|
|
|
|
Name: "Test draft room",
|
|
|
|
Creator: "test-owner",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new session with a fake message from owner
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.message("test-owner", "create", draftbot.SessionOptions{
|
2019-06-28 14:15:25 +00:00
|
|
|
Players: 8, // Two players, six bots
|
2019-06-28 16:19:02 +00:00
|
|
|
Options: draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftSet,
|
|
|
|
Positioning: draftbot.PosEven,
|
2019-06-28 14:15:25 +00:00
|
|
|
Set: mlp.SetAbsoluteDiscord,
|
|
|
|
PackCount: 4,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("session-open")
|
2019-06-28 14:15:25 +00:00
|
|
|
|
|
|
|
// Join session as owner
|
|
|
|
mock.message("test-owner", "join", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("player-joined-session")
|
2019-06-28 14:15:25 +00:00
|
|
|
|
|
|
|
// .. and as second player
|
|
|
|
mock.message("test-guest", "join", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("player-joined-session")
|
2019-06-28 14:15:25 +00:00
|
|
|
|
|
|
|
// Try to start the session
|
|
|
|
mock.message("test-owner", "start", nil)
|
|
|
|
|
|
|
|
// These two can happen in any order, so they get their own special check
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.multiexpect("session-start", "draft-order")
|
|
|
|
|
|
|
|
// Pick card for each player
|
|
|
|
for packi := 0; packi < 4; packi++ {
|
|
|
|
mock.expect("draft-newpack")
|
|
|
|
for cardi := 0; cardi < 12; cardi++ {
|
2019-08-26 09:18:14 +00:00
|
|
|
mock.expect("draft-newpick")
|
2019-06-28 16:19:02 +00:00
|
|
|
// Get packs
|
2019-08-26 09:18:14 +00:00
|
|
|
msg1 := mock.expect("draft-availablepicks")
|
2019-06-28 16:19:02 +00:00
|
|
|
pack1 := msg1.Data.(draft.Pack)
|
2019-08-26 09:18:14 +00:00
|
|
|
msg2 := mock.expect("draft-availablepicks")
|
2019-06-28 16:19:02 +00:00
|
|
|
pack2 := msg2.Data.(draft.Pack)
|
|
|
|
// Pick first card in each pack
|
|
|
|
mock.message(msg1.To, "pick", pack1[0].ID)
|
|
|
|
mock.message(msg2.To, "pick", pack2[0].ID)
|
|
|
|
// Intercept picked events
|
|
|
|
mock.multiexpect("card-picked", "card-picked")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mock.expect("draft-finish")
|
2019-06-28 14:15:25 +00:00
|
|
|
|
|
|
|
// Close the room
|
|
|
|
mock.in <- room.ServerMessage{
|
|
|
|
RoomID: TestRoomName,
|
|
|
|
Type: room.MsgEvent,
|
|
|
|
Event: &room.Event{
|
|
|
|
Type: room.EvtRoomClosed,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDraftSessionButEverythingGoesWrong(t *testing.T) {
|
2019-06-28 16:19:02 +00:00
|
|
|
mock := makeMockServer(t, 5)
|
|
|
|
drafter := draftbot.NewDraftBot(mock, TestBotName)
|
|
|
|
go mock.Bind(drafter.OnMessage)
|
2019-06-28 14:15:25 +00:00
|
|
|
|
|
|
|
// Create a new room
|
|
|
|
mock.in <- room.ServerMessage{
|
|
|
|
Type: room.MsgEvent,
|
|
|
|
Event: &room.Event{
|
|
|
|
Type: room.EvtNewRoom,
|
|
|
|
Room: &lobby.Room{
|
|
|
|
Id: TestRoomName,
|
|
|
|
Name: "Test draft room",
|
|
|
|
Creator: "test-owner",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try creating a new session as NOT the owner
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.message("test-guest", "create", draftbot.SessionOptions{
|
2019-06-28 14:29:19 +00:00
|
|
|
Players: 8,
|
2019-06-28 16:19:02 +00:00
|
|
|
Options: draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftSet,
|
|
|
|
Positioning: draftbot.PosEven,
|
2019-06-28 14:15:25 +00:00
|
|
|
Set: mlp.SetAbsoluteDiscord,
|
|
|
|
PackCount: 4,
|
|
|
|
},
|
|
|
|
})
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("must-be-owner")
|
2019-06-28 14:15:25 +00:00
|
|
|
|
2019-06-28 14:29:19 +00:00
|
|
|
// Try creating a session with an invalid type
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.message("test-owner", "create", draftbot.SessionOptions{
|
2019-06-28 14:29:19 +00:00
|
|
|
Players: 8,
|
2019-06-28 16:19:02 +00:00
|
|
|
Options: draftbot.DraftOptions{
|
2019-06-28 14:29:19 +00:00
|
|
|
Type: "lolwhat",
|
|
|
|
},
|
|
|
|
})
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("session-create-error")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
// Try creating a session with invalid data
|
|
|
|
mock.message("test-owner", "create", 42)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("invalid-data")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
// Try starting a session that doesn't exist
|
|
|
|
mock.message("test-owner", "start", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("command-unavailable")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
// Try creating the session twice
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.message("test-owner", "create", draftbot.SessionOptions{
|
2019-06-28 14:29:19 +00:00
|
|
|
Players: 2,
|
2019-06-28 16:19:02 +00:00
|
|
|
Options: draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftSet,
|
|
|
|
Positioning: draftbot.PosEven,
|
2019-06-28 14:29:19 +00:00
|
|
|
Set: mlp.SetAbsoluteDiscord,
|
|
|
|
PackCount: 4,
|
|
|
|
},
|
|
|
|
})
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("session-open")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.message("test-owner", "create", draftbot.SessionOptions{
|
2019-06-28 14:29:19 +00:00
|
|
|
Players: 2,
|
2019-06-28 16:19:02 +00:00
|
|
|
Options: draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftSet,
|
|
|
|
Positioning: draftbot.PosEven,
|
2019-06-28 14:29:19 +00:00
|
|
|
Set: mlp.SetAbsoluteDiscord,
|
|
|
|
PackCount: 4,
|
|
|
|
},
|
|
|
|
})
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("command-unavailable")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
// Try to start session when no-one has joined
|
|
|
|
mock.message("test-owner", "start", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("session-start-error")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
// Try to make too many players join
|
|
|
|
mock.message("a", "join", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("player-joined-session")
|
2019-06-28 14:29:19 +00:00
|
|
|
mock.message("b", "join", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("player-joined-session")
|
2019-06-28 14:29:19 +00:00
|
|
|
mock.message("c", "join", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("session-full")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
// Try to make someone join a session that already started
|
|
|
|
mock.message("test-owner", "start", nil)
|
2019-08-26 09:18:14 +00:00
|
|
|
mock.multiexpect("session-start", "draft-order", "draft-newpack", "draft-newpick", "draft-availablepicks", "draft-availablepicks")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
mock.message("c", "join", nil)
|
2019-06-28 16:19:02 +00:00
|
|
|
mock.expect("session-already-started")
|
2019-06-28 14:29:19 +00:00
|
|
|
|
|
|
|
//TODO More picking, etc shenanigans
|
2019-06-28 14:15:25 +00:00
|
|
|
}
|
|
|
|
|
2019-06-28 21:07:05 +00:00
|
|
|
func TestDraftTypes(t *testing.T) {
|
|
|
|
// Load all sets into memory
|
|
|
|
err := mlp.LoadAllSets()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Could not load MLP sets needed for some drafts: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
mock := makeMockServer(t, 5)
|
|
|
|
drafter := draftbot.NewDraftBot(mock, TestBotName)
|
|
|
|
go mock.Bind(drafter.OnMessage)
|
|
|
|
|
|
|
|
makeSession := func(roomName string, options draftbot.DraftOptions) {
|
|
|
|
// Create a new room
|
|
|
|
mock.in <- room.ServerMessage{
|
|
|
|
RoomID: roomName,
|
|
|
|
Type: room.MsgEvent,
|
|
|
|
Event: &room.Event{
|
|
|
|
Type: room.EvtNewRoom,
|
|
|
|
Room: &lobby.Room{
|
|
|
|
Id: roomName,
|
|
|
|
Name: "Test draft room",
|
|
|
|
Creator: "test-owner",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
mock.roomName = roomName
|
|
|
|
|
|
|
|
// Create new session
|
|
|
|
mock.message("test-owner", "create", draftbot.SessionOptions{
|
|
|
|
Players: 8, // Two players, six bots
|
|
|
|
Options: options,
|
|
|
|
})
|
|
|
|
|
|
|
|
mock.expect("session-open")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make Set draft session
|
|
|
|
makeSession("set", draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftSet,
|
|
|
|
Positioning: draftbot.PosEven,
|
|
|
|
Set: mlp.SetAbsoluteDiscord,
|
|
|
|
PackCount: 4,
|
|
|
|
})
|
|
|
|
|
|
|
|
// Make Block draft session
|
|
|
|
makeSession("block", draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftBlock,
|
|
|
|
Positioning: draftbot.PosRandom,
|
|
|
|
Block: mlp.BlockDefenders,
|
|
|
|
})
|
|
|
|
|
|
|
|
// Make a plain Cube draft session
|
|
|
|
makeSession("cube", draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftCube,
|
|
|
|
Positioning: draftbot.PosEven,
|
|
|
|
CubeURL: mlp.HTTPSource + "cube",
|
|
|
|
PackSize: 4,
|
|
|
|
})
|
|
|
|
|
|
|
|
// Make a I8PCube draft session
|
|
|
|
makeSession("i8pcube", draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftI8PCube,
|
|
|
|
Positioning: draftbot.PosEven,
|
|
|
|
CubeURL: mlp.HTTPSource + "i8pcube",
|
|
|
|
MainCount: 1,
|
|
|
|
ProblemCount: 1,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDraftPositioning(t *testing.T) {
|
|
|
|
mock := makeMockServer(t, 5)
|
|
|
|
drafter := draftbot.NewDraftBot(mock, TestBotName)
|
|
|
|
go mock.Bind(drafter.OnMessage)
|
|
|
|
|
|
|
|
makeSession := func(roomName string, pos string) {
|
|
|
|
// Create a new room
|
|
|
|
mock.in <- room.ServerMessage{
|
|
|
|
RoomID: roomName,
|
|
|
|
Type: room.MsgEvent,
|
|
|
|
Event: &room.Event{
|
|
|
|
Type: room.EvtNewRoom,
|
|
|
|
Room: &lobby.Room{
|
|
|
|
Id: roomName,
|
|
|
|
Name: "Test draft room",
|
|
|
|
Creator: "test-owner",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
mock.roomName = roomName
|
|
|
|
|
|
|
|
// Create new session
|
|
|
|
mock.message("test-owner", "create", draftbot.SessionOptions{
|
|
|
|
Players: 8, // Two players, six bots
|
|
|
|
Options: draftbot.DraftOptions{
|
|
|
|
Type: draftbot.DraftSet,
|
|
|
|
Positioning: pos,
|
|
|
|
Set: mlp.SetAbsoluteDiscord,
|
|
|
|
PackCount: 4,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
mock.expect("session-open")
|
|
|
|
|
|
|
|
// Make two random players join
|
|
|
|
mock.message("a", "join", nil)
|
|
|
|
mock.expect("player-joined-session")
|
|
|
|
mock.message("b", "join", nil)
|
|
|
|
mock.expect("player-joined-session")
|
|
|
|
|
|
|
|
// Start the session
|
|
|
|
mock.message("test-owner", "start", nil)
|
2019-08-26 09:18:14 +00:00
|
|
|
mock.multiexpect("session-start", "draft-order", "draft-newpack", "draft-newpick", "draft-availablepicks", "draft-availablepicks")
|
2019-06-28 21:07:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
makeSession("even", draftbot.PosEven)
|
|
|
|
makeSession("random", draftbot.PosRandom)
|
|
|
|
}
|
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
func (m *MockServer) expect(typ string) *room.Message {
|
2019-06-28 14:15:25 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case msg := <-m.out:
|
|
|
|
// Skip all actions
|
|
|
|
if msg.Type != room.MsgMessage {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check expected type
|
|
|
|
if msg.Message.Type != typ {
|
|
|
|
// Oh noes
|
2019-06-28 21:07:05 +00:00
|
|
|
m.t.Fatalf("Expected message \"%s\" but got \"%s\" (%v)", typ, msg.Message.Type, msg.Message.Data)
|
2019-06-28 14:15:25 +00:00
|
|
|
}
|
2019-06-28 16:19:02 +00:00
|
|
|
return msg.Message
|
|
|
|
case <-time.After(m.timeout):
|
|
|
|
m.t.Fatalf("Expected message \"%s\" but found nothing (timeout)!", typ)
|
|
|
|
return nil
|
2019-06-28 14:15:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
func (m *MockServer) multiexpect(types ...string) {
|
2019-06-28 14:15:25 +00:00
|
|
|
for {
|
|
|
|
if len(types) < 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case msg := <-m.out:
|
2019-06-28 16:19:02 +00:00
|
|
|
|
2019-06-28 14:15:25 +00:00
|
|
|
// Skip all actions
|
|
|
|
if msg.Type != room.MsgMessage {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-06-28 16:19:02 +00:00
|
|
|
m.t.Logf("-> [%s] %s", msg.Message.Type, msg.Message.Message)
|
|
|
|
|
2019-06-28 14:15:25 +00:00
|
|
|
// Check expected type
|
|
|
|
found := false
|
|
|
|
for i, typ := range types {
|
|
|
|
if typ == msg.Message.Type {
|
|
|
|
found = true
|
|
|
|
types[i] = types[len(types)-1]
|
|
|
|
types = types[:len(types)-1]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
// Oh noes
|
2019-06-28 16:19:02 +00:00
|
|
|
m.t.Fatalf("Expected one of %s but got \"%s\"", types, msg.Message.Type)
|
2019-06-28 14:15:25 +00:00
|
|
|
}
|
2019-06-28 16:19:02 +00:00
|
|
|
case <-time.After(m.timeout):
|
|
|
|
m.t.Fatalf("Expected one of %s but found nothing (timeout)!", types)
|
2019-06-28 14:15:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockServer) message(from string, typ string, data interface{}) {
|
2019-06-28 16:19:02 +00:00
|
|
|
m.t.Logf("<- <%s> %s (%v)", from, typ, data)
|
2019-06-28 14:15:25 +00:00
|
|
|
m.in <- room.ServerMessage{
|
2019-06-28 21:07:05 +00:00
|
|
|
RoomID: m.roomName,
|
2019-06-28 14:15:25 +00:00
|
|
|
Type: room.MsgMessage,
|
|
|
|
Message: &room.Message{
|
|
|
|
From: from,
|
|
|
|
To: TestBotName,
|
|
|
|
Type: typ,
|
|
|
|
Data: data,
|
|
|
|
},
|
|
|
|
}
|
2019-06-28 09:03:42 +00:00
|
|
|
}
|
2019-06-28 21:07:05 +00:00
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
|
|
switch req.URL.Path {
|
|
|
|
case "/nopenope.json":
|
|
|
|
// 404
|
|
|
|
http.Error(res, "Not found", http.StatusNotFound)
|
|
|
|
case "/broken.json":
|
|
|
|
// Broken response
|
|
|
|
fmt.Fprintf(res, "{{{{")
|
|
|
|
case "/pr.json":
|
|
|
|
fmt.Fprintf(res, mlptestdata.SetPremiere)
|
|
|
|
case "/cube":
|
|
|
|
fmt.Fprintf(res, testdata.TestGenericCubeFile)
|
|
|
|
case "/i8pcube":
|
|
|
|
fmt.Fprintf(res, testdata.TestI8PCubeFile)
|
|
|
|
default:
|
|
|
|
fmt.Fprintf(res, mlptestdata.SetFriendForever)
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
mlp.HTTPSource = testServer.URL + "/"
|
|
|
|
defer testServer.Close()
|
|
|
|
os.Exit(m.Run())
|
|
|
|
}
|