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