package beta import ( "errors" "testing" "github.com/bwmarrin/discordgo" ) const ( betaGuild = "1170934850157547600" otherGuild = "999000000000000001" testerRol = "555000000000000001" testerID = "777000000000000001" ) func member(roles ...string) *discordgo.Member { return &discordgo.Member{Roles: roles, User: &discordgo.User{ID: testerID}} } func interaction(guildID string, m *discordgo.Member) *discordgo.InteractionCreate { return &discordgo.InteractionCreate{Interaction: &discordgo.Interaction{GuildID: guildID, Member: m}} } // useLookup makes the beta server say who holds which roles, as Discord would. func useLookup(t *testing.T, rolesByUser map[string][]string, err error) *int { t.Helper() calls := 0 SetRolesLookup(func(guildID, userID string) ([]string, error) { calls++ if guildID != betaGuild { t.Errorf("the lookup must ask the beta server, asked %q", guildID) } return rolesByUser[userID], err }) t.Cleanup(func() { lookup.Store(nil) }) return &calls } func TestAllowedInTheBetaServer(t *testing.T) { cases := []struct { name string guild, role string // configuration inGuild string member *discordgo.Member want bool on, hasRole bool }{ {"tester in the beta server", betaGuild, testerRol, betaGuild, member("1", testerRol), true, true, true}, {"member without the role", betaGuild, testerRol, betaGuild, member("1", "2"), false, true, false}, {"member with no roles at all", betaGuild, testerRol, betaGuild, member(), false, true, false}, {"a DM has no member", betaGuild, testerRol, "", nil, false, false, false}, {"a nil member in the beta server", betaGuild, testerRol, betaGuild, nil, false, true, false}, {"no role configured: nobody", betaGuild, "", betaGuild, member(testerRol, ""), false, true, false}, {"no beta server configured: nobody", "", testerRol, betaGuild, member(testerRol), false, false, false}, {"a role ID that isn't a number is dropped", betaGuild, "tester-role", betaGuild, member("tester-role"), false, true, false}, {"a server ID that isn't a number is dropped", "my-server", testerRol, "my-server", member(testerRol), false, false, false}, {"a role that only contains the ID isn't a match", betaGuild, testerRol, betaGuild, member(testerRol + "9"), false, true, false}, {"stray spaces in the configuration are ignored", " " + betaGuild + " ", " " + testerRol + "\n", betaGuild, member(testerRol), true, true, true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { Configure(tc.guild, tc.role, nil) if got := Allowed(tc.inGuild, tc.member); got != tc.want { t.Errorf("Allowed = %v, want %v", got, tc.want) } if got := AllowedInteraction(interaction(tc.inGuild, tc.member)); got != tc.want { t.Errorf("AllowedInteraction = %v, want %v", got, tc.want) } st := StatusOf(tc.inGuild, tc.member) if st.ServerEnabled != tc.on || st.HasRole != tc.hasRole { t.Errorf("StatusOf = %+v, want server on %v, tester %v", st, tc.on, tc.hasRole) } }) } } func TestOptedInServers(t *testing.T) { roles := map[string][]string{testerID: {"1", testerRol}} t.Run("a tester in an opted-in server is allowed", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) calls := useLookup(t, roles, nil) if !Allowed(otherGuild, member()) { // roles in this server don't matter t.Error("tester in an opted-in server should be allowed") } if *calls != 1 { t.Errorf("the beta server should be asked once, was asked %d times", *calls) } }) t.Run("the role in the other server doesn't count", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) useLookup(t, map[string][]string{}, nil) if Allowed(otherGuild, member(testerRol)) { t.Error("holding a role with that ID in another server must not make anyone a tester") } }) t.Run("a server that didn't opt in is refused, and nothing is looked up", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) calls := useLookup(t, roles, nil) if Allowed("123000000000000001", member()) { t.Error("a server that didn't opt in must be refused") } if *calls != 0 { t.Error("no lookup for a server where beta is off") } }) t.Run("a failing lookup fails closed", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) useLookup(t, roles, errors.New("Unknown Member")) if Allowed(otherGuild, member()) { t.Error("an error from Discord must mean no") } }) t.Run("no lookup set: testers only qualify in the beta server", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) lookup.Store(nil) if Allowed(otherGuild, member()) { t.Error("without a lookup there is no way to know") } if !Allowed(betaGuild, member(testerRol)) { t.Error("the beta server doesn't need the lookup") } }) t.Run("a user without an ID can't be looked up", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) useLookup(t, roles, nil) if Allowed(otherGuild, &discordgo.Member{}) || Allowed(otherGuild, &discordgo.Member{User: &discordgo.User{}}) { t.Error("no user, no access") } }) t.Run("no role configured: nobody, even when the server opted in", func(t *testing.T) { Configure(betaGuild, "", []string{otherGuild}) useLookup(t, map[string][]string{testerID: {""}}, nil) if Allowed(otherGuild, member()) { t.Error("without a role there are no testers") } }) t.Run("bad IDs in the list are dropped", func(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild, "not-an-id", "", " 5 "}) got := Servers() if len(got) != 2 || got[0] != "5" || got[1] != otherGuild { t.Errorf("Servers = %v", got) } }) t.Run("the beta server isn't listed twice", func(t *testing.T) { Configure(betaGuild, testerRol, []string{betaGuild, otherGuild}) if got := Servers(); len(got) != 1 || got[0] != otherGuild { t.Errorf("Servers = %v", got) } }) } func TestServerEnabledNeedsNoPerson(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) for guild, want := range map[string]bool{betaGuild: true, otherGuild: true, "123": false, "": false} { if got := ServerEnabled(guild); got != want { t.Errorf("ServerEnabled(%q) = %v, want %v", guild, got, want) } } current.Store(nil) if ServerEnabled(betaGuild) { t.Error("before Configure nothing is on") } } func TestNotConfiguredAtAll(t *testing.T) { current.Store(nil) if Allowed(betaGuild, member(testerRol)) || GuildID() != "" || len(Servers()) != 0 { t.Error("before Configure nothing is allowed and there's no beta server") } if AllowedInteraction(nil) || AllowedInteraction(&discordgo.InteractionCreate{}) { t.Error("a missing interaction is never allowed") } } func TestGateRunsTheHandlerOnlyForTesters(t *testing.T) { Configure(betaGuild, testerRol, []string{otherGuild}) useLookup(t, map[string][]string{}, nil) // nobody holds the role in the beta server var answered []string orig := respond respond = func(_ *discordgo.Session, _ *discordgo.InteractionCreate, msg string) { answered = append(answered, msg) } t.Cleanup(func() { respond = orig }) ran := 0 h := Gate(func(*discordgo.Session, *discordgo.InteractionCreate) { ran++ }) h(nil, interaction(betaGuild, member(testerRol))) if ran != 1 || len(answered) != 0 { t.Fatalf("a tester runs the handler without being refused: ran=%d answered=%v", ran, answered) } for name, i := range map[string]*discordgo.InteractionCreate{ "no role": interaction(betaGuild, member("1")), "opted-in, not a tester": interaction(otherGuild, member(testerRol)), "server not opted in": interaction("123000000000000001", member(testerRol)), "DM": interaction("", nil), } { before := ran h(nil, i) if ran != before { t.Errorf("%s: the handler must not run", name) } } if len(answered) != 4 || answered[0] != denied { t.Errorf("each refusal is answered with the beta message: %v", answered) } useLookup(t, map[string][]string{testerID: {testerRol}}, nil) before := ran h(nil, interaction(otherGuild, member())) if ran != before+1 { t.Error("a tester in an opted-in server runs the handler") } } func TestGuardOnItsOwn(t *testing.T) { Configure(betaGuild, testerRol, nil) n := 0 orig := respond respond = func(*discordgo.Session, *discordgo.InteractionCreate, string) { n++ } t.Cleanup(func() { respond = orig }) if !Guard(nil, interaction(betaGuild, member(testerRol))) || n != 0 { t.Error("a tester passes without a reply") } if Guard(nil, interaction(betaGuild, member())) || n != 1 { t.Error("someone else is refused with a reply") } }