From 50c7850dda4ab2552485006ead58cf61a7a28266 Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 11 Sep 2021 11:01:59 +0900 Subject: [PATCH 1/2] Fix to use constructor --- internal/server/slack/deploy.go | 153 +++++++++++------------------- internal/server/slack/rollback.go | 106 ++++++++------------- 2 files changed, 93 insertions(+), 166 deletions(-) diff --git a/internal/server/slack/deploy.go b/internal/server/slack/deploy.go index 267e5a31..58ebded6 100644 --- a/internal/server/slack/deploy.go +++ b/internal/server/slack/deploy.go @@ -173,109 +173,64 @@ func buildDeployView(callbackID string, c *vo.Config, perms []*ent.Perm) slack.M return slack.ModalViewRequest{ Type: slack.VTModal, CallbackID: callbackID, - Title: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Deploy", - }, - Submit: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Submit", - }, - Close: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Close", - }, + Title: slack.NewTextBlockObject(slack.PlainTextType, "Deploy", false, false), + Submit: slack.NewTextBlockObject(slack.PlainTextType, "Submit", false, false), + Close: slack.NewTextBlockObject(slack.PlainTextType, "Close", false, false), Blocks: slack.Blocks{ BlockSet: []slack.Block{ + slack.NewInputBlock( + blockEnv, + slack.NewTextBlockObject(slack.PlainTextType, "Environment", false, false), + slack.NewOptionsSelectBlockElement( + slack.OptTypeStatic, + slack.NewTextBlockObject(slack.PlainTextType, "Select target environment", false, false), + actionEnv, + envs..., + ), + ), + slack.NewInputBlock( + blockType, + slack.NewTextBlockObject(slack.PlainTextType, "Type", false, false), + slack.NewOptionsSelectBlockElement( + slack.OptTypeStatic, + slack.NewTextBlockObject(slack.PlainTextType, "Select your ref type", false, false), + actionType, + slack.NewOptionBlockObject( + "commit", + slack.NewTextBlockObject(slack.PlainTextType, "Commit", false, false), + nil, + ), + slack.NewOptionBlockObject( + "branch", + slack.NewTextBlockObject(slack.PlainTextType, "Branch", false, false), + nil, + ), + slack.NewOptionBlockObject( + "tag", + slack.NewTextBlockObject(slack.PlainTextType, "Tag", false, false), + nil, + ), + ), + ), + slack.NewInputBlock( + blockRef, + slack.NewTextBlockObject(slack.PlainTextType, "Ref", false, false), + slack.NewPlainTextInputBlockElement( + slack.NewTextBlockObject(slack.PlainTextType, "E.g. Commit - 25a667d6, Branch - main, Tag - v0.1.2", false, false), + actionRef, + ), + ), slack.InputBlock{ - Type: slack.MBTInput, - BlockID: blockEnv, - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Environment", - }, - Element: slack.SelectBlockElement{ - Type: slack.OptTypeStatic, - ActionID: actionEnv, - Placeholder: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Select target environment", - }, - Options: envs, - }, - }, - slack.InputBlock{ - Type: slack.MBTInput, - BlockID: blockType, - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Reference Type", - }, - Element: slack.SelectBlockElement{ - Type: slack.OptTypeStatic, - ActionID: actionType, - Placeholder: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Select your reference type", - }, - Options: []*slack.OptionBlockObject{ - { - Text: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Commit", - }, - Value: "commit", - }, - { - Text: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Branch", - }, - Value: "branch", - }, - { - Text: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Tag", - }, - Value: "tag", - }, - }, - }, - }, - slack.InputBlock{ - Type: slack.MBTInput, - BlockID: blockRef, - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Reference", - }, - Element: slack.PlainTextInputBlockElement{ - Type: slack.METPlainTextInput, - ActionID: actionRef, - Placeholder: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "E.g. Commit - 25a667d6, Branch - main, Tag - v0.1.2", - }, - }, - }, - slack.InputBlock{ - Type: slack.MBTInput, - BlockID: blockApprovers, - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Approvers", - }, + Type: slack.MBTInput, + BlockID: blockApprovers, + Label: slack.NewTextBlockObject(slack.PlainTextType, "Approvers", false, false), Optional: true, - Element: slack.SelectBlockElement{ - Type: slack.MultiOptTypeStatic, - ActionID: actionApprovers, - Placeholder: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Select approvers", - }, - Options: approvers, - }, + Element: slack.NewOptionsSelectBlockElement( + slack.MultiOptTypeStatic, + slack.NewTextBlockObject(slack.PlainTextType, "Select approvers", false, false), + actionApprovers, + approvers..., + ), }, }, }, diff --git a/internal/server/slack/rollback.go b/internal/server/slack/rollback.go index 4d9b1126..2657c146 100644 --- a/internal/server/slack/rollback.go +++ b/internal/server/slack/rollback.go @@ -106,7 +106,7 @@ func (s *Slack) handleRollbackCmd(c *gin.Context) { return } - as := s.getSucceedDeploymentAggregation(ctx, r, config) + as := s.getSuccessfulDeploymentAggregation(ctx, r, config) _, err = slack.New(cu.BotToken). OpenViewContext(ctx, cmd.TriggerID, buildRollbackView(cb.Hash, as, perms)) @@ -128,22 +128,18 @@ func buildRollbackView(callbackID string, as []*deploymentAggregation, perms []* for _, d := range a.deployments { created, _ := goment.New(d.CreatedAt) - options = append(options, &slack.OptionBlockObject{ - Text: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: fmt.Sprintf("#%d - %s deployed at %s", d.ID, d.GetShortRef(), created.FromNow()), - }, - Value: strconv.Itoa(d.ID), - }) + options = append(options, slack.NewOptionBlockObject( + strconv.Itoa(d.ID), + slack.NewTextBlockObject( + slack.PlainTextType, + fmt.Sprintf("#%d - %s deployed at %s", d.ID, d.GetShortRef(), created.FromNow()), + false, false), + nil)) } - groups = append(groups, &slack.OptionGroupBlockObject{ - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: string(a.envName), - }, - Options: options, - }) + groups = append(groups, slack.NewOptionGroupBlockElement( + slack.NewTextBlockObject(slack.PlainTextType, string(a.envName), false, false), + options...)) } approvers := []*slack.OptionBlockObject{} @@ -153,73 +149,49 @@ func buildRollbackView(callbackID string, as []*deploymentAggregation, perms []* continue } - approvers = append(approvers, &slack.OptionBlockObject{ - Text: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: u.Login, - }, - Value: u.ID, - }) + slack.NewOptionBlockObject(u.ID, slack.NewTextBlockObject(slack.PlainTextType, u.Login, false, false), nil) + approvers = append(approvers, slack.NewOptionBlockObject( + u.ID, + slack.NewTextBlockObject(slack.PlainTextType, u.Login, false, false), + nil)) } return slack.ModalViewRequest{ Type: slack.VTModal, CallbackID: callbackID, - Title: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Rollback", - }, - Submit: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Submit", - }, - Close: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Close", - }, + Title: slack.NewTextBlockObject(slack.PlainTextType, "Rollback", false, false), + Submit: slack.NewTextBlockObject(slack.PlainTextType, "Submit", false, false), + Close: slack.NewTextBlockObject(slack.PlainTextType, "Close", false, false), Blocks: slack.Blocks{ BlockSet: []slack.Block{ + slack.NewInputBlock( + blockDeployment, + slack.NewTextBlockObject(slack.PlainTextType, "Deployments", false, false), + slack.NewOptionsGroupSelectBlockElement( + slack.OptTypeStatic, + slack.NewTextBlockObject(slack.PlainTextType, "Select target deployment", false, false), + actionDeployment, + groups..., + ), + ), slack.InputBlock{ - Type: slack.MBTInput, - BlockID: blockDeployment, - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Deployments", - }, - Element: slack.SelectBlockElement{ - Type: slack.OptTypeStatic, - ActionID: actionDeployment, - Placeholder: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Select target deployment", - }, - OptionGroups: groups, - }, - }, - slack.InputBlock{ - Type: slack.MBTInput, - BlockID: blockApprovers, - Label: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Approvers", - }, + Type: slack.MBTInput, + BlockID: blockApprovers, Optional: true, - Element: slack.SelectBlockElement{ - Type: slack.MultiOptTypeStatic, - ActionID: actionApprovers, - Placeholder: &slack.TextBlockObject{ - Type: slack.PlainTextType, - Text: "Select approvers", - }, - Options: approvers, - }, + Label: slack.NewTextBlockObject(slack.PlainTextType, "Approvers", false, false), + Element: slack.NewOptionsSelectBlockElement( + slack.MultiOptTypeStatic, + slack.NewTextBlockObject(slack.PlainTextType, "Select approvers", false, false), + actionApprovers, + approvers..., + ), }, }, }, } } -func (s *Slack) getSucceedDeploymentAggregation(ctx context.Context, r *ent.Repo, cf *vo.Config) []*deploymentAggregation { +func (s *Slack) getSuccessfulDeploymentAggregation(ctx context.Context, r *ent.Repo, cf *vo.Config) []*deploymentAggregation { a := []*deploymentAggregation{} for _, env := range cf.Envs { From bd9721aa2bbb6a6c306440e80625485e58aa475e Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 11 Sep 2021 11:51:33 +0900 Subject: [PATCH 2/2] Add unit tests for interaction --- go.mod | 1 + go.sum | 5 + internal/server/slack/deploy_test.go | 86 +++++++- internal/server/slack/rollback_test.go | 196 ++++++++++++++++++ .../slack/testdata/deploy-interact.json | 4 +- .../slack/testdata/rollback-interact.json | 161 ++++++++++++++ 6 files changed, 446 insertions(+), 7 deletions(-) create mode 100644 internal/server/slack/rollback_test.go create mode 100644 internal/server/slack/testdata/rollback-interact.json diff --git a/go.mod b/go.mod index b42f5e8f..e35c54bd 100644 --- a/go.mod +++ b/go.mod @@ -33,5 +33,6 @@ require ( go.uber.org/zap v1.13.0 golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c + gopkg.in/h2non/gock.v1 v1.1.2 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) diff --git a/go.sum b/go.sum index 8880526c..5a118d55 100644 --- a/go.sum +++ b/go.sum @@ -199,6 +199,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -340,6 +342,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nleeper/goment v1.4.2 h1:r4c8KkCrsBJUnVi/IJ5HEqev5QY8aCWOXQtu+eYXtnI= github.com/nleeper/goment v1.4.2/go.mod h1:zDl5bAyDhqxwQKAvkSXMRLOdCowrdZz53ofRJc4VhTo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -758,6 +761,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= diff --git a/internal/server/slack/deploy_test.go b/internal/server/slack/deploy_test.go index 5a8bbbba..4b4cedfe 100644 --- a/internal/server/slack/deploy_test.go +++ b/internal/server/slack/deploy_test.go @@ -10,15 +10,93 @@ import ( "testing" "github.com/gin-gonic/gin" + "github.com/golang/mock/gomock" + "github.com/slack-go/slack" + "go.uber.org/zap" + "gopkg.in/h2non/gock.v1" + "github.com/gitploy-io/gitploy/ent" "github.com/gitploy-io/gitploy/ent/deployment" "github.com/gitploy-io/gitploy/internal/server/slack/mock" "github.com/gitploy-io/gitploy/vo" - "github.com/golang/mock/gomock" - "go.uber.org/zap" +) + +const ( + pathPostMessage string = "chat.postMessage" ) func TestSlack_interactDeploy(t *testing.T) { + t.Run("Post a message when the repository is locked.", func(t *testing.T) { + m := mock.NewMockInteractor(gomock.NewController(t)) + + // These values are in "./testdata/deploy-interact.json" + const ( + callbackID = "nafyVuEqzcchuVmV" + chatUserID = "U025KUBB2" + branch = "main" + env = "prod" + ) + + t.Log("Find the callback which was stored by the Slash command.") + m. + EXPECT(). + FindCallbackByHash(gomock.Any(), callbackID). + Return(&ent.Callback{ + Edges: ent.CallbackEdges{ + Repo: &ent.Repo{ + ID: "1", + Locked: true, + }, + }, + }, nil) + + t.Log("Find the chat-user who sent the payload.") + m. + EXPECT(). + FindChatUserByID(gomock.Any(), chatUserID). + Return(&ent.ChatUser{}, nil) + + t.Log("Get branch to validate the payload.") + m. + EXPECT(). + GetBranch(gomock.Any(), gomock.AssignableToTypeOf(&ent.User{}), gomock.AssignableToTypeOf(&ent.Repo{}), branch). + Return(&vo.Branch{ + Name: branch, + }, nil) + + t.Log("Post a message when the repository is locked.") + gock. + New(slack.APIURL). + Post(pathPostMessage). + Reply(200) + + s := &Slack{i: m, log: zap.L()} + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.POST("/interact", s.interactDeploy) + + // Build the Slack payload. + bytes, err := ioutil.ReadFile("./testdata/deploy-interact.json") + if err != nil { + t.Errorf("It has failed to open the JSON file: %s", err) + t.FailNow() + } + + form := url.Values{} + form.Add("payload", string(bytes)) + + req, _ := http.NewRequest("POST", "/interact", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("w.Code = %d, wanted %d. Body = %v", w.Code, http.StatusOK, w.Body) + } + }) + t.Run("Create a new deployment with payload.", func(t *testing.T) { m := mock.NewMockInteractor(gomock.NewController(t)) @@ -114,9 +192,7 @@ func TestSlack_interactDeploy(t *testing.T) { router.ServeHTTP(w, req) if w.Code != http.StatusOK { - t.Errorf("w.Code = %d, wanted %d", w.Code, http.StatusOK) - t.Logf("w.Body = %v", w.Body) - t.FailNow() + t.Fatalf("w.Code = %d, wanted %d. Body = %v", w.Code, http.StatusOK, w.Body) } }) } diff --git a/internal/server/slack/rollback_test.go b/internal/server/slack/rollback_test.go new file mode 100644 index 00000000..5d71f064 --- /dev/null +++ b/internal/server/slack/rollback_test.go @@ -0,0 +1,196 @@ +package slack + +import ( + "context" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/gin-gonic/gin" + "github.com/golang/mock/gomock" + "github.com/slack-go/slack" + "go.uber.org/zap" + "gopkg.in/h2non/gock.v1" + + "github.com/gitploy-io/gitploy/ent" + "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/internal/server/slack/mock" + "github.com/gitploy-io/gitploy/vo" +) + +func TestSlack_interactRollback(t *testing.T) { + t.Run("Post a message when the repository is locked.", func(t *testing.T) { + m := mock.NewMockInteractor(gomock.NewController(t)) + + // These values are in "./testdata/rollback-interact.json" + const ( + callbackID = "hZUZvJgWhxYvdekUGESXKjSusKWWIRKr" + chatUserID = "U025KUBB2" + deploymentID = 33 + ) + + t.Log("Find the callback which has the locked repository.") + m. + EXPECT(). + FindCallbackByHash(gomock.Any(), callbackID). + Return(&ent.Callback{ + Edges: ent.CallbackEdges{ + Repo: &ent.Repo{ + ID: "1", + Locked: true, + }, + }, + }, nil) + + t.Log("Find the chat-user who sent the payload.") + m. + EXPECT(). + FindChatUserByID(gomock.Any(), chatUserID). + Return(&ent.ChatUser{}, nil) + + t.Log("Find the deployment by ID.") + m. + EXPECT(). + FindDeploymentByID(gomock.Any(), deploymentID). + Return(&ent.Deployment{ + ID: deploymentID, + }, nil) + + t.Log("Post a message when the repository is locked.") + gock. + New(slack.APIURL). + Post(pathPostMessage). + Reply(200) + + s := &Slack{i: m, log: zap.L()} + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.POST("/interact", s.interactRollback) + + // Build the payload to interact. + bytes, err := ioutil.ReadFile("./testdata/rollback-interact.json") + if err != nil { + t.Errorf("It has failed to open the JSON file: %s", err) + t.FailNow() + } + + form := url.Values{} + form.Add("payload", string(bytes)) + + req, _ := http.NewRequest("POST", "/interact", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("w.Code = %d, wanted %d. Body = %v", w.Code, http.StatusOK, w.Body) + } + }) + + t.Run("Rollback with the returned deployment.", func(t *testing.T) { + m := mock.NewMockInteractor(gomock.NewController(t)) + + // These values are in "./testdata/rollback-interact.json" + const ( + callbackID = "hZUZvJgWhxYvdekUGESXKjSusKWWIRKr" + chatUserID = "U025KUBB2" + deploymentID = 33 + ) + + t.Log("Find the callback which was stored by the Slash command.") + m. + EXPECT(). + FindCallbackByHash(gomock.Any(), callbackID). + Return(&ent.Callback{ + Edges: ent.CallbackEdges{ + Repo: &ent.Repo{ID: "1"}, + }, + }, nil) + + t.Log("Find the chat-user who sent the payload.") + m. + EXPECT(). + FindChatUserByID(gomock.Any(), chatUserID). + Return(&ent.ChatUser{}, nil) + + t.Log("Find the deployment by ID.") + m. + EXPECT(). + FindDeploymentByID(gomock.Any(), deploymentID). + Return(&ent.Deployment{ + ID: deploymentID, + Type: deployment.TypeCommit, + Ref: "main", + Sha: "ee411aa", + Env: "prod", + }, nil) + + t.Log("Get the config file of the repository.") + m. + EXPECT(). + GetConfig(gomock.Any(), gomock.AssignableToTypeOf(&ent.User{}), gomock.AssignableToTypeOf(&ent.Repo{})). + Return(&vo.Config{ + Envs: []*vo.Env{ + {Name: "prod"}, + }, + }, nil) + + t.Log("Get the next number of deployment.") + m. + EXPECT(). + GetNextDeploymentNumberOfRepo(gomock.Any(), gomock.AssignableToTypeOf(&ent.Repo{})). + Return(4, nil) + + t.Log("Roll back with the returned deployment.") + m. + EXPECT(). + Rollback(gomock.Any(), gomock.AssignableToTypeOf(&ent.User{}), gomock.AssignableToTypeOf(&ent.Repo{}), &ent.Deployment{ + Number: 4, + Type: deployment.TypeCommit, + Ref: "main", + Sha: "ee411aa", + Env: "prod", + }, gomock.AssignableToTypeOf(&vo.Env{})). + DoAndReturn(func(ctx context.Context, u *ent.User, r *ent.Repo, d *ent.Deployment, e *vo.Env) (*ent.Deployment, error) { + d.ID = deploymentID + 1 + return d, nil + }) + + t.Log("Create a new event") + m. + EXPECT(). + CreateEvent(gomock.Any(), gomock.AssignableToTypeOf(&ent.Event{})). + Return(&ent.Event{}, nil) + + s := &Slack{i: m, log: zap.L()} + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.POST("/interact", s.interactRollback) + + // Build the payload to interact. + bytes, err := ioutil.ReadFile("./testdata/rollback-interact.json") + if err != nil { + t.Errorf("It has failed to open the JSON file: %s", err) + t.FailNow() + } + + form := url.Values{} + form.Add("payload", string(bytes)) + + req, _ := http.NewRequest("POST", "/interact", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("w.Code = %d, wanted %d. Body = %v", w.Code, http.StatusOK, w.Body) + } + }) +} diff --git a/internal/server/slack/testdata/deploy-interact.json b/internal/server/slack/testdata/deploy-interact.json index ac35f9ad..58f9a60c 100644 --- a/internal/server/slack/testdata/deploy-interact.json +++ b/internal/server/slack/testdata/deploy-interact.json @@ -6,8 +6,8 @@ }, "user": { "id": "U025KUBB2", - "username": "gitploy", - "name": "gitploy", + "username": "octocat", + "name": "octocat", "team_id": "T024K36ZE3" }, "trigger_id": "2343342985366.2150662237479.d8d4101b1404c64b832d43c5768fd2b0", diff --git a/internal/server/slack/testdata/rollback-interact.json b/internal/server/slack/testdata/rollback-interact.json new file mode 100644 index 00000000..25aeccf1 --- /dev/null +++ b/internal/server/slack/testdata/rollback-interact.json @@ -0,0 +1,161 @@ +{ + "type": "view_submission", + "team": { + "id": "T024EKG6ZE3", + "domain": "gitploy" + }, + "user": { + "id": "U025KUBB2", + "username": "octocat", + "name": "octocat", + "team_id": "T024EKG6ZE3" + }, + "api_app_id": "A024NKFBZT8", + "token": "KPuw2gHb31oG2GDX5mjbsw6O", + "trigger_id": "2479998291107.2150662237479.a6afd8db96b07e504c2a4dfb22d70e82", + "view": { + "id": "V02E0V16N13", + "team_id": "T024EKG6ZE3", + "type": "modal", + "blocks": [ + { + "type": "input", + "block_id": "block_deployment", + "label": { + "type": "plain_text", + "text": "Deployments", + "emoji": true + }, + "optional": false, + "dispatch_action": false, + "element": { + "type": "static_select", + "action_id": "aciton_deployment", + "placeholder": { + "type": "plain_text", + "text": "Select target deployment", + "emoji": true + }, + "option_groups": [ + { + "label": { + "type": "plain_text", + "text": "local", + "emoji": true + }, + "options": [ + { + "text": { + "type": "plain_text", + "text": "#33 - 36644dd deployed at 15 days ago", + "emoji": true + }, + "value": "33" + }, + { + "text": { + "type": "plain_text", + "text": "#32 - 36644dd deployed at 15 days ago", + "emoji": true + }, + "value": "32" + } + ] + } + ] + } + }, + { + "type": "input", + "block_id": "block_approvers", + "label": { + "type": "plain_text", + "text": "Approvers", + "emoji": true + }, + "optional": true, + "dispatch_action": false, + "element": { + "type": "multi_static_select", + "action_id": "action_approver_ids", + "placeholder": { + "type": "plain_text", + "text": "Select approvers", + "emoji": true + }, + "options": [ + { + "text": { + "type": "plain_text", + "text": "hanjunlee", + "emoji": true + }, + "value": "17633736" + } + ] + } + } + ], + "private_metadata": "", + "callback_id": "hZUZvJgWhxYvdekUGESXKjSusKWWIRKr", + "state": { + "values": { + "block_deployment": { + "aciton_deployment": { + "type": "static_select", + "selected_option": { + "text": { + "type": "plain_text", + "text": "#33 - 36644dd deployed at 15 days ago", + "emoji": true + }, + "value": "33" + } + } + }, + "block_approvers": { + "action_approver_ids": { + "type": "multi_static_select", + "selected_options": [ + { + "text": { + "type": "plain_text", + "text": "hanjunlee", + "emoji": true + }, + "value": "17633736" + } + ] + } + } + } + }, + "hash": "1631327008.N96kHN5Y", + "title": { + "type": "plain_text", + "text": "Rollback", + "emoji": true + }, + "clear_on_close": false, + "notify_on_close": false, + "close": { + "type": "plain_text", + "text": "Close", + "emoji": true + }, + "submit": { + "type": "plain_text", + "text": "Submit", + "emoji": true + }, + "previous_view_id": null, + "root_view_id": "V02E0V16N13", + "app_id": "A024NKFBZT8", + "external_id": "", + "app_installed_team_id": "T024EKG6ZE3", + "bot_id": "B02582YBE1F" + }, + "response_urls": [], + "is_enterprise_install": false, + "enterprise": null +} \ No newline at end of file