package main_test import ( "testing" "time" "git.fromouter.space/mcg/draft" "git.fromouter.space/mcg/draft/mlp" "git.fromouter.space/mcg/cardgage/client/bot" lobby "git.fromouter.space/mcg/cardgage/lobby/proto" room "git.fromouter.space/mcg/cardgage/room/api" draftbot "git.fromouter.space/mcg/mlp-server-tools/draftbot" ) const TestBotName = "test-bot" const TestRoomName = "test-room" type MockServer struct { in chan room.ServerMessage out chan room.BotMessage t *testing.T timeout time.Duration } func makeMockServer(t *testing.T, timeout int) *MockServer { in := make(chan room.ServerMessage, 99) out := make(chan room.BotMessage, 99) srv := &MockServer{ in: in, out: out, t: t, timeout: time.Duration(timeout) * time.Second, } 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) { mock := makeMockServer(t, 5) drafter := draftbot.NewDraftBot(mock, TestBotName) go mock.Bind(drafter.OnMessage) // 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 mock.message("test-owner", "create", draftbot.SessionOptions{ Players: 8, // Two players, six bots Options: draftbot.DraftOptions{ Type: draftbot.DraftSet, Positioning: draftbot.PosEven, Set: mlp.SetAbsoluteDiscord, PackCount: 4, }, }) mock.expect("session-open") // Join session as owner mock.message("test-owner", "join", nil) mock.expect("player-joined-session") // .. and as second player mock.message("test-guest", "join", nil) mock.expect("player-joined-session") // 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 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++ { // Get packs msg1 := mock.expect("draft-newpick") pack1 := msg1.Data.(draft.Pack) msg2 := mock.expect("draft-newpick") 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") // Close the room mock.in <- room.ServerMessage{ RoomID: TestRoomName, Type: room.MsgEvent, Event: &room.Event{ Type: room.EvtRoomClosed, }, } } func TestDraftSessionButEverythingGoesWrong(t *testing.T) { mock := makeMockServer(t, 5) drafter := draftbot.NewDraftBot(mock, TestBotName) go mock.Bind(drafter.OnMessage) // 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 mock.message("test-guest", "create", draftbot.SessionOptions{ Players: 8, Options: draftbot.DraftOptions{ Type: draftbot.DraftSet, Positioning: draftbot.PosEven, Set: mlp.SetAbsoluteDiscord, PackCount: 4, }, }) mock.expect("must-be-owner") // Try creating a session with an invalid type mock.message("test-owner", "create", draftbot.SessionOptions{ Players: 8, Options: draftbot.DraftOptions{ Type: "lolwhat", }, }) mock.expect("session-create-error") // Try creating a session with invalid data mock.message("test-owner", "create", 42) mock.expect("invalid-data") // Try starting a session that doesn't exist mock.message("test-owner", "start", nil) mock.expect("command-unavailable") // Try creating the session twice mock.message("test-owner", "create", draftbot.SessionOptions{ Players: 2, Options: draftbot.DraftOptions{ Type: draftbot.DraftSet, Positioning: draftbot.PosEven, Set: mlp.SetAbsoluteDiscord, PackCount: 4, }, }) mock.expect("session-open") mock.message("test-owner", "create", draftbot.SessionOptions{ Players: 2, Options: draftbot.DraftOptions{ Type: draftbot.DraftSet, Positioning: draftbot.PosEven, Set: mlp.SetAbsoluteDiscord, PackCount: 4, }, }) mock.expect("command-unavailable") // Try to start session when no-one has joined mock.message("test-owner", "start", nil) mock.expect("session-start-error") // Try to make too many players join mock.message("a", "join", nil) mock.expect("player-joined-session") mock.message("b", "join", nil) mock.expect("player-joined-session") mock.message("c", "join", nil) mock.expect("session-full") // Try to make someone join a session that already started mock.message("test-owner", "start", nil) mock.multiexpect("session-start", "draft-order", "draft-newpack", "draft-newpick", "draft-newpick") mock.message("c", "join", nil) mock.expect("session-already-started") //TODO More picking, etc shenanigans } func (m *MockServer) expect(typ string) *room.Message { 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 m.t.Fatalf("Expected message \"%s\" but got \"%s\"", typ, msg.Message.Type) } return msg.Message case <-time.After(m.timeout): m.t.Fatalf("Expected message \"%s\" but found nothing (timeout)!", typ) return nil } } } func (m *MockServer) multiexpect(types ...string) { for { if len(types) < 1 { return } select { case msg := <-m.out: // Skip all actions if msg.Type != room.MsgMessage { continue } m.t.Logf("-> [%s] %s", msg.Message.Type, msg.Message.Message) // 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 m.t.Fatalf("Expected one of %s but got \"%s\"", types, msg.Message.Type) } case <-time.After(m.timeout): m.t.Fatalf("Expected one of %s but found nothing (timeout)!", types) } } } func (m *MockServer) message(from string, typ string, data interface{}) { m.t.Logf("<- <%s> %s (%v)", from, typ, data) m.in <- room.ServerMessage{ RoomID: TestRoomName, Type: room.MsgMessage, Message: &room.Message{ From: from, To: TestBotName, Type: typ, Data: data, }, } }