From b269cc9229671acd5644257c77fd523c6e9a3840 Mon Sep 17 00:00:00 2001
From: Oleksandr Redko <oleksandr.red+github@gmail.com>
Date: Thu, 31 Oct 2024 13:57:46 +0200
Subject: [PATCH] feat: use context for HTTP request in discord (#5232)

The PR adds using `context.Context` in the `POST` request.

Context is a request scoped paradigm according to [the
doc](https://pkg.go.dev/context).

The PR adds `API` field into the `Config` for utilizing it in tests.
---
 internal/pipe/discord/discord.go      | 11 ++++--
 internal/pipe/discord/discord_test.go | 50 +++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/internal/pipe/discord/discord.go b/internal/pipe/discord/discord.go
index 670d342e8..f867c09b6 100644
--- a/internal/pipe/discord/discord.go
+++ b/internal/pipe/discord/discord.go
@@ -27,6 +27,7 @@ func (Pipe) String() string                 { return "discord" }
 func (Pipe) Skip(ctx *context.Context) bool { return !ctx.Config.Announce.Discord.Enabled }
 
 type Config struct {
+	API          string `env:"DISCORD_API" envDefault:"https://discord.com/api"`
 	WebhookID    string `env:"DISCORD_WEBHOOK_ID,notEmpty"`
 	WebhookToken string `env:"DISCORD_WEBHOOK_TOKEN,notEmpty"`
 }
@@ -65,7 +66,7 @@ func (p Pipe) Announce(ctx *context.Context) error {
 		return fmt.Errorf("discord: %w", err)
 	}
 
-	u, err := url.Parse("https://discord.com/api")
+	u, err := url.Parse(cfg.API)
 	if err != nil {
 		return fmt.Errorf("discord: %w", err)
 	}
@@ -87,7 +88,13 @@ func (p Pipe) Announce(ctx *context.Context) error {
 		return fmt.Errorf("discord: %w", err)
 	}
 
-	resp, err := http.Post(u.String(), "application/json", bytes.NewReader(bts))
+	req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), bytes.NewReader(bts))
+	if err != nil {
+		return fmt.Errorf("discord: %w", err)
+	}
+	req.Header.Set("Content-Type", "application/json")
+
+	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 		return fmt.Errorf("discord: %w", err)
 	}
diff --git a/internal/pipe/discord/discord_test.go b/internal/pipe/discord/discord_test.go
index d7aaf198a..042af7b0e 100644
--- a/internal/pipe/discord/discord_test.go
+++ b/internal/pipe/discord/discord_test.go
@@ -1,11 +1,17 @@
 package discord
 
 import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"strconv"
 	"testing"
 
 	"github.com/goreleaser/goreleaser/v2/internal/testctx"
 	"github.com/goreleaser/goreleaser/v2/internal/testlib"
 	"github.com/goreleaser/goreleaser/v2/pkg/config"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
@@ -40,6 +46,50 @@ func TestAnnounceMissingEnv(t *testing.T) {
 	require.EqualError(t, Pipe{}.Announce(ctx), `discord: env: environment variable "DISCORD_WEBHOOK_ID" should not be empty; environment variable "DISCORD_WEBHOOK_TOKEN" should not be empty`)
 }
 
+func TestAnnounce(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.Method != http.MethodPost {
+			w.WriteHeader(http.StatusMethodNotAllowed)
+			return
+		}
+		if r.URL.Path != "/webhooks/id/token" {
+			w.WriteHeader(http.StatusNotFound)
+			return
+		}
+
+		wm := &WebhookMessageCreate{}
+
+		body, _ := io.ReadAll(r.Body)
+		err := json.Unmarshal(body, wm)
+		assert.NoError(t, err)
+		assert.Equal(t, defaultColor, strconv.Itoa(wm.Embeds[0].Color))
+		assert.Equal(t, "Honk v1.0.0 is out! Check it out at https://github.com/honk/honk/releases/tag/v1.0.0", wm.Embeds[0].Description)
+
+		w.WriteHeader(http.StatusNoContent)
+	}))
+	defer ts.Close()
+
+	ctx := testctx.NewWithCfg(config.Project{
+		ProjectName: "Honk",
+		Announce: config.Announce{
+			Discord: config.Discord{
+				Enabled: true,
+			},
+		},
+	})
+
+	ctx.Git.CurrentTag = "v1.0.0"
+	ctx.ReleaseURL = "https://github.com/honk/honk/releases/tag/v1.0.0"
+	ctx.Git.URL = "https://github.com/honk/honk"
+
+	t.Setenv("DISCORD_API", ts.URL)
+	t.Setenv("DISCORD_WEBHOOK_ID", "id")
+	t.Setenv("DISCORD_WEBHOOK_TOKEN", "token")
+
+	require.NoError(t, Pipe{}.Default(ctx))
+	require.NoError(t, Pipe{}.Announce(ctx))
+}
+
 func TestSkip(t *testing.T) {
 	t.Run("skip", func(t *testing.T) {
 		require.True(t, Pipe{}.Skip(testctx.New()))