From dfbb9e2bca5ea58dc1553beaff801e1fee4910e5 Mon Sep 17 00:00:00 2001 From: noe Date: Wed, 26 Mar 2025 13:35:47 -0700 Subject: [PATCH] redo discord mocks --- discord/api_mock.go | 30 ---------------- discord/clientmock/discord_client_mock.go | 42 +++++++++++++++++++++++ discord/{api.go => discord_client.go} | 19 +++++++--- discord/guild.go | 31 +++++++++++++++-- discord/guild_mock.go | 19 ---------- discord/guild_test.go | 31 +++++++++++++++++ discord/guildservice.go | 28 +++++++++++++-- discord/guildservice_mock.go | 24 ------------- discord/guildservice_test.go | 27 +++++++++++++++ discord/member.go | 1 + discord/member_mock.go | 17 --------- discord/member_test.go | 1 + discord/utils_test.go | 16 +++++++++ interactions/interactions_test.go | 5 ++- testing/testing.go | 2 +- types/fixtures/guild.go | 9 +++++ types/fixtures/member.go | 12 +++++++ types/fixtures/user.go | 10 ++++++ types/guild.go | 4 ++- 19 files changed, 223 insertions(+), 105 deletions(-) delete mode 100644 discord/api_mock.go create mode 100644 discord/clientmock/discord_client_mock.go rename discord/{api.go => discord_client.go} (70%) delete mode 100644 discord/guild_mock.go create mode 100644 discord/guild_test.go delete mode 100644 discord/guildservice_mock.go create mode 100644 discord/guildservice_test.go delete mode 100644 discord/member_mock.go create mode 100644 discord/member_test.go create mode 100644 discord/utils_test.go create mode 100644 types/fixtures/guild.go create mode 100644 types/fixtures/member.go create mode 100644 types/fixtures/user.go diff --git a/discord/api_mock.go b/discord/api_mock.go deleted file mode 100644 index 2f90c47..0000000 --- a/discord/api_mock.go +++ /dev/null @@ -1,30 +0,0 @@ -package discord - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - - "github.com/stretchr/testify/mock" -) - -type DiscordClientMock struct { - mock.Mock -} - -func (c *DiscordClientMock) Do(req *http.Request) (*http.Response, error) { - args := c.Called(req) - - return args.Get(0).(*http.Response), args.Error(1) -} - -func (c *DiscordClientMock) MockResponse(statusCode int, data any) *http.Response { - body := bytes.Buffer{} - json.NewEncoder(&body).Encode(data) - - return &http.Response{ - StatusCode: statusCode, - Body: io.NopCloser(&body), - } -} diff --git a/discord/clientmock/discord_client_mock.go b/discord/clientmock/discord_client_mock.go new file mode 100644 index 0000000..c35a22e --- /dev/null +++ b/discord/clientmock/discord_client_mock.go @@ -0,0 +1,42 @@ +package clientmock + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "regexp" + "strings" + + "github.com/stretchr/testify/mock" +) + +type DiscordClientMock struct { + mock.Mock +} + +func (c *DiscordClientMock) Do(req *http.Request) (*http.Response, error) { + args := c.Called(req) + + return args.Get(0).(*http.Response), args.Error(1) +} + +func (c *DiscordClientMock) BotAuth(req *http.Request) { + c.Called(req) +} + +func (c *DiscordClientMock) MockResponse(method, path string, statusCode int, data any) { + body := bytes.Buffer{} + json.NewEncoder(&body).Encode(data) + + r := &http.Response{ + StatusCode: statusCode, + Body: io.NopCloser(&body), + } + + pathMatcher := regexp.MustCompile(strings.ReplaceAll(path, "*", "[a-z0-9_-]+")) + + c.On("Do", mock.MatchedBy(func(req *http.Request) bool { + return req.Method == method && pathMatcher.MatchString(req.URL.Path) + })).Return(r, nil) +} diff --git a/discord/api.go b/discord/discord_client.go similarity index 70% rename from discord/api.go rename to discord/discord_client.go index 0716fe7..e5b446f 100644 --- a/discord/api.go +++ b/discord/discord_client.go @@ -5,12 +5,15 @@ import ( "net/http" "net/url" "time" + + "github.com/goccy/go-json" ) const DiscordBaseUrl = "https://discord.com/api/v10" type IDiscordClient interface { Do(req *http.Request) (*http.Response, error) + BotAuth(req *http.Request) } type DiscordClient struct { @@ -36,7 +39,11 @@ func (d *DiscordClient) Do(req *http.Request) (*http.Response, error) { return d.Client.Do(req) } -func NewRequest(method string, path string, botToken string) *http.Request { +func (d *DiscordClient) BotAuth(req *http.Request) { + req.Header.Set("Authorization", fmt.Sprintf("Bot %s", d.BotToken)) +} + +func NewRequest(method string, path string) *http.Request { url, err := url.Parse(fmt.Sprintf("%s%s", DiscordBaseUrl, path)) if err != nil { panic("discord/api: wtf couldnt join a string??") @@ -45,13 +52,15 @@ func NewRequest(method string, path string, botToken string) *http.Request { req := &http.Request{ Method: method, URL: url, + Header: http.Header{}, } req.Header.Set("User-Agent", "Roleypoly (https://roleypoly.com, v4)") - if botToken != "" { - req.Header.Set("Authorization", fmt.Sprintf("Bot %s", botToken)) - } - return req } + +func OutputResponse(resp *http.Response, dst any) error { + // TODO: more checks? + return json.NewDecoder(resp.Body).Decode(dst) +} diff --git a/discord/guild.go b/discord/guild.go index 7897cdb..55a0152 100644 --- a/discord/guild.go +++ b/discord/guild.go @@ -1,12 +1,39 @@ package discord -import "git.sapphic.engineer/roleypoly/v4/types" +import ( + "fmt" + + "git.sapphic.engineer/roleypoly/v4/types" + "git.sapphic.engineer/roleypoly/v4/utils" +) type IGuild interface { GetMember(memberID string) (IMember, error) } type Guild struct { - Client IDiscordClient types.DiscordGuild + + Client IDiscordClient `json:"-"` +} + +func (g *Guild) GetMember(memberID string) (*Member, error) { + guildID := g.ID + req := NewRequest("GET", utils.J("guilds", guildID, "members", memberID)) + g.Client.BotAuth(req) + + resp, err := g.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("Guild.GetMember: request failed: %w", err) + } + + member := &Member{ + Client: g.Client, + } + err = OutputResponse(resp, member) + if err != nil { + return nil, fmt.Errorf("Guild.GetMember: response decode failed: %w", err) + } + + return member, nil } diff --git a/discord/guild_mock.go b/discord/guild_mock.go deleted file mode 100644 index e9e0635..0000000 --- a/discord/guild_mock.go +++ /dev/null @@ -1,19 +0,0 @@ -package discord - -import ( - "github.com/stretchr/testify/mock" -) - -type GuildMock struct { - mock.Mock -} - -func (g *GuildMock) GetMember(memberID string) (IMember, error) { - args := g.Called(memberID) - - if args.Get(0) == nil { - return nil, args.Error(1) - } - - return args.Get(0).(IMember), args.Error(1) -} diff --git a/discord/guild_test.go b/discord/guild_test.go new file mode 100644 index 0000000..fe7eade --- /dev/null +++ b/discord/guild_test.go @@ -0,0 +1,31 @@ +package discord_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "git.sapphic.engineer/roleypoly/v4/discord" + "git.sapphic.engineer/roleypoly/v4/types/fixtures" + "git.sapphic.engineer/roleypoly/v4/utils" +) + +var ( + fixtureMember = &discord.Member{ + DiscordMember: fixtures.Member, + } +) + +func TestGetMember(t *testing.T) { + dc := defaultMocks(t) + g := discord.Guild{ + Client: dc, + DiscordGuild: fixtures.Guild, + } + + dc.MockResponse("GET", utils.J("guilds", fixtures.Guild.ID, "members", fixtureMember.User.ID), 200, fixtureMember) + + m, err := g.GetMember("41666") + assert.Nil(t, err) + assert.Equal(t, "Doll", m.Nick) +} diff --git a/discord/guildservice.go b/discord/guildservice.go index 79278ac..a118074 100644 --- a/discord/guildservice.go +++ b/discord/guildservice.go @@ -1,5 +1,11 @@ package discord +import ( + "fmt" + + "git.sapphic.engineer/roleypoly/v4/utils" +) + type IGuildService interface { Client() IDiscordClient GetGuild(guildID string) (IGuild, error) @@ -19,7 +25,23 @@ func (gs *GuildService) Client() IDiscordClient { return gs.client } -func (gs *GuildService) GetGuild(guildID string) (IGuild, error) { - // gs.client - return nil, nil +func (gs *GuildService) GetGuild(guildID string) (*Guild, error) { + + req := NewRequest("GET", utils.J("guilds", guildID)) + gs.client.BotAuth(req) + + resp, err := gs.client.Do(req) + if err != nil { + return nil, fmt.Errorf("GuildService.GetGuild: request failed: %w", err) + } + + guild := &Guild{ + Client: gs.client, + } + err = OutputResponse(resp, guild) + if err != nil { + return nil, fmt.Errorf("GuildService.GetGuild: response decode failed: %w", err) + } + + return guild, nil } diff --git a/discord/guildservice_mock.go b/discord/guildservice_mock.go deleted file mode 100644 index b126c3a..0000000 --- a/discord/guildservice_mock.go +++ /dev/null @@ -1,24 +0,0 @@ -package discord - -import ( - "github.com/stretchr/testify/mock" -) - -type GuildServiceMock struct { - mock.Mock -} - -func (gs *GuildServiceMock) GetGuild(guildID string) (IGuild, error) { - args := gs.Called(guildID) - - if args.Get(0) == nil { - return nil, args.Error(1) - } - - return args.Get(0).(IGuild), args.Error(1) -} - -func (gs *GuildServiceMock) Client() IDiscordClient { - args := gs.Called() - return args.Get(0).(IDiscordClient) -} diff --git a/discord/guildservice_test.go b/discord/guildservice_test.go new file mode 100644 index 0000000..d742987 --- /dev/null +++ b/discord/guildservice_test.go @@ -0,0 +1,27 @@ +package discord_test + +import ( + "testing" + + "git.sapphic.engineer/roleypoly/v4/discord" + "git.sapphic.engineer/roleypoly/v4/types/fixtures" + "git.sapphic.engineer/roleypoly/v4/utils" + "github.com/stretchr/testify/assert" +) + +var ( + fixtureGuild = &discord.Guild{ + DiscordGuild: fixtures.Guild, + } +) + +func TestGetGuild(t *testing.T) { + dc := defaultMocks(t) + gs := discord.NewGuildService(dc) + + dc.MockResponse("GET", utils.J("guilds", fixtures.Guild.ID), 200, fixtureGuild) + + g, err := gs.GetGuild("1312") + assert.Nil(t, err) + assert.Equal(t, "1312", g.ID) +} diff --git a/discord/member.go b/discord/member.go index d3849c4..25d30ce 100644 --- a/discord/member.go +++ b/discord/member.go @@ -8,6 +8,7 @@ type IMember interface { } type Member struct { + Client IDiscordClient types.DiscordMember } diff --git a/discord/member_mock.go b/discord/member_mock.go deleted file mode 100644 index 5a68725..0000000 --- a/discord/member_mock.go +++ /dev/null @@ -1,17 +0,0 @@ -package discord - -import "github.com/stretchr/testify/mock" - -type MemberMock struct { - mock.Mock -} - -func (m *MemberMock) GetRoles() error { - args := m.Called() - return args.Error(0) -} - -func (m *MemberMock) PatchRoles() error { - args := m.Called() - return args.Error(0) -} diff --git a/discord/member_test.go b/discord/member_test.go new file mode 100644 index 0000000..d36e7e6 --- /dev/null +++ b/discord/member_test.go @@ -0,0 +1 @@ +package discord_test diff --git a/discord/utils_test.go b/discord/utils_test.go new file mode 100644 index 0000000..87dadf2 --- /dev/null +++ b/discord/utils_test.go @@ -0,0 +1,16 @@ +package discord_test + +import ( + "testing" + + "git.sapphic.engineer/roleypoly/v4/discord/clientmock" + "github.com/stretchr/testify/mock" +) + +func defaultMocks(t *testing.T) *clientmock.DiscordClientMock { + dc := &clientmock.DiscordClientMock{} + + dc.On("BotAuth", mock.AnythingOfType("*http.Request")) + + return dc +} diff --git a/interactions/interactions_test.go b/interactions/interactions_test.go index e698ce8..1f94aa2 100644 --- a/interactions/interactions_test.go +++ b/interactions/interactions_test.go @@ -56,9 +56,8 @@ func TestPostHandlerSigFail(t *testing.T) { app := roleypoly.CreateFiberApp() i.Routes(app) - // TODO: make real interaction - body, err := json.Marshal(map[string]string{ - "temp": "temp", + body, err := json.Marshal(map[string]any{ + "type": 1, }) if err != nil { t.Error(err) diff --git a/testing/testing.go b/testing/testing.go index 88c8897..a2e48a1 100644 --- a/testing/testing.go +++ b/testing/testing.go @@ -10,7 +10,7 @@ import ( ) type TestingController struct { - Guilds discord.IGuildService + Guilds *discord.GuildService } func (t *TestingController) Routes(r fiber.Router) { diff --git a/types/fixtures/guild.go b/types/fixtures/guild.go new file mode 100644 index 0000000..4d28426 --- /dev/null +++ b/types/fixtures/guild.go @@ -0,0 +1,9 @@ +package fixtures + +import "git.sapphic.engineer/roleypoly/v4/types" + +var ( + Guild = types.DiscordGuild{ + ID: "1312", + } +) diff --git a/types/fixtures/member.go b/types/fixtures/member.go new file mode 100644 index 0000000..b8d3679 --- /dev/null +++ b/types/fixtures/member.go @@ -0,0 +1,12 @@ +package fixtures + +import "git.sapphic.engineer/roleypoly/v4/types" + +var ( + Member = types.DiscordMember{ + User: User, + Nick: "Doll", + Permissions: 0, + Roles: []string{"role1", "role2"}, + } +) diff --git a/types/fixtures/user.go b/types/fixtures/user.go new file mode 100644 index 0000000..9928b57 --- /dev/null +++ b/types/fixtures/user.go @@ -0,0 +1,10 @@ +package fixtures + +import "git.sapphic.engineer/roleypoly/v4/types" + +var ( + User = types.DiscordUser{ + Username: "41666", + ID: "41666", + } +) diff --git a/types/guild.go b/types/guild.go index ea6c5e3..ff81a56 100644 --- a/types/guild.go +++ b/types/guild.go @@ -1,3 +1,5 @@ package types -type DiscordGuild struct{} +type DiscordGuild struct { + ID string `json:"id"` +}