diff --git a/server/command.go b/server/command.go index 22f3247a6..b8e7e5726 100644 --- a/server/command.go +++ b/server/command.go @@ -148,6 +148,9 @@ func addSubCommands(jira *model.AutocompleteData, optInstance bool) { jira.AddCommand(createTransitionCommand(optInstance)) jira.AddCommand(createAssignCommand(optInstance)) jira.AddCommand(createUnassignCommand(optInstance)) + jira.AddCommand(createConnectCommand()) + jira.AddCommand(createDisconnectCommand()) + jira.AddCommand(createSettingsCommand(optInstance)) // Generic commands jira.AddCommand(createIssueCommand(optInstance)) diff --git a/server/command_test.go b/server/command_test.go index 673a70f88..99d0e88c1 100644 --- a/server/command_test.go +++ b/server/command_test.go @@ -227,6 +227,102 @@ func TestPlugin_ExecuteCommand_Settings(t *testing.T) { } } +func TestPlugin_ExecuteCommand_Instance_Settings(t *testing.T) { + p := &Plugin{} + tc := TestConfiguration{} + p.updateConfig(func(conf *config) { + conf.Secret = tc.Secret + conf.mattermostSiteURL = mattermostSiteURL + }) + api := &plugintest.API{} + api.On("LogError", mock.AnythingOfTypeArgument("string")).Return(nil) + + tests := map[string]struct { + commandArgs *model.CommandArgs + numInstances int + expectedMsg string + }{ + "no storage": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings", UserId: mockUserIDUnknown}, + numInstances: 2, + expectedMsg: "Failed to load your connection to Jira. Error: TESTING user \"3\" not found.", + }, + "user not found": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings", UserId: mockUserIDUnknown}, + numInstances: 0, + expectedMsg: "Failed to load your connection to Jira. Error: TESTING user \"3\" not found.", + }, + "no params, with notifications": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings", UserId: mockUserIDWithNotifications}, + numInstances: 1, + expectedMsg: "Current settings:\n\tNotifications: on", + }, + "no params, without notifications": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings", UserId: mockUserIDWithoutNotifications}, + numInstances: 1, + expectedMsg: "Current settings:\n\tNotifications: off", + }, + "unknown setting": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings" + " test", UserId: mockUserIDWithoutNotifications}, + numInstances: 1, + expectedMsg: "Unknown setting.", + }, + "set notifications without value": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings" + " notifications", UserId: mockUserIDWithoutNotifications}, + numInstances: 1, + expectedMsg: "`/jira settings notifications [value]`\n* Invalid value. Accepted values are: `on` or `off`.", + }, + "set notification with unknown value": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings notifications test", UserId: mockUserIDWithoutNotifications}, + numInstances: 1, + expectedMsg: "`/jira settings notifications [value]`\n* Invalid value. Accepted values are: `on` or `off`.", + }, + "enable notifications": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings notifications on", UserId: mockUserIDWithoutNotifications}, + numInstances: 1, + expectedMsg: "Settings updated. Notifications on.", + }, + "disable notifications": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings notifications off", UserId: mockUserIDWithNotifications}, + numInstances: 1, + expectedMsg: "Settings updated. Notifications off.", + }, + "multiple instances are present: Notifications off": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings notifications off --instance https://jiraurl1.com", UserId: mockUserIDWithNotifications}, + numInstances: 2, + expectedMsg: "Settings updated. Notifications off.", + }, + "multiple instances are present: Notifications on": { + commandArgs: &model.CommandArgs{Command: "/jira instance settings notifications on --instance https://jiraurl2.com", UserId: mockUserIDWithNotifications}, + numInstances: 2, + expectedMsg: "Settings updated. Notifications on.", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + isSendEphemeralPostCalled := false + + currentTestAPI := api + currentTestAPI.On("SendEphemeralPost", mock.AnythingOfType("string"), mock.AnythingOfType("*model.Post")).Run(func(args mock.Arguments) { + isSendEphemeralPostCalled = true + + post := args.Get(1).(*model.Post) + assert.Equal(t, tt.expectedMsg, post.Message) + }).Once().Return(&model.Post{}) + + p.SetAPI(currentTestAPI) + p.client = pluginapi.NewClient(p.API, p.Driver) + p.instanceStore = p.getMockInstanceStoreKV(tt.numInstances) + p.userStore = getMockUserStoreKV() + + _, err := p.ExecuteCommand(&plugin.Context{}, tt.commandArgs) + require.Nil(t, err) + + assert.Equal(t, true, isSendEphemeralPostCalled) + }) + } +} + func TestPlugin_ExecuteCommand_Installation(t *testing.T) { api := &plugintest.API{} api.On("LogError", mock.AnythingOfTypeArgument("string")).Return(nil) diff --git a/server/http_test.go b/server/http_test.go index 28cab3b6b..c7ade75e5 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -174,7 +174,7 @@ func TestSubscribe(t *testing.T) { }, }, "Initial Subscription happy": { - subscription: `{"instance_id": "jiraurl1", "name": "some name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "some name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusOK, apiCalls: checkHasSubscriptions([]ChannelSubscription{ { @@ -188,37 +188,37 @@ func TestSubscribe(t *testing.T) { }, nil, t), }, "Initial Subscription, GetProject mocked error": { - subscription: fmt.Sprintf(`{"instance_id": "jiraurl1", "name": "some name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["%s"], "issue_types": ["10001"]}}`, nonExistantProjectKey), + subscription: fmt.Sprintf(`{"instance_id": "https://jiraurl1.com", "name": "some name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["%s"], "issue_types": ["10001"]}}`, nonExistantProjectKey), expectedStatusCode: http.StatusInternalServerError, apiCalls: hasSubscriptions([]ChannelSubscription{}, t), }, "Initial Subscription, empty name provided": { - subscription: `{"instance_id": "jiraurl1", "name": "", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: hasSubscriptions([]ChannelSubscription{}, t), }, "Initial Subscription, long name provided": { - subscription: `{"instance_id": "jiraurl1", "name": "` + TestDataLongSubscriptionName + `", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "` + TestDataLongSubscriptionName + `", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: hasSubscriptions([]ChannelSubscription{}, t), }, "Initial Subscription, no project provided": { - subscription: `{"instance_id": "jiraurl1", "name": "somename", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": [], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "somename", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": [], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: hasSubscriptions([]ChannelSubscription{}, t), }, "Initial Subscription, no events provided": { - subscription: `{"instance_id": "jiraurl1", "name": "somename", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": [], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "somename", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": [], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: hasSubscriptions([]ChannelSubscription{}, t), }, "Initial Subscription, no issue types provided": { - subscription: `{"instance_id": "jiraurl1", "name": "somename", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": []}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "somename", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": []}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: hasSubscriptions([]ChannelSubscription{}, t), }, "Adding to existing with other channel": { - subscription: `{"instance_id": "jiraurl1", "name": "some name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "some name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusOK, apiCalls: checkHasSubscriptions([]ChannelSubscription{ { @@ -252,7 +252,7 @@ func TestSubscribe(t *testing.T) { }), t), }, "Adding to existing in same channel": { - subscription: `{"instance_id": "jiraurl1", "name": "subscription name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "subscription name", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusOK, apiCalls: checkHasSubscriptions([]ChannelSubscription{ { @@ -286,7 +286,7 @@ func TestSubscribe(t *testing.T) { }), t), }, "Adding to existing with same name in same channel": { - subscription: `{"instance_id": "jiraurl1", "name": "SubscriptionName", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "SubscriptionName", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "filters": {"events": ["jira:issue_created"], "projects": ["myproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{ { @@ -503,7 +503,7 @@ func TestEditSubscription(t *testing.T) { }, }, "Editing subscription": { - subscription: `{"instance_id": "jiraurl1", "name": "some name", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "some name", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusOK, apiCalls: checkHasSubscriptions([]ChannelSubscription{ { @@ -530,7 +530,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, no name provided": { - subscription: `{"instance_id": "jiraurl1", "name": "", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( @@ -547,7 +547,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, name too long": { - subscription: `{"instance_id": "jiraurl1", "name": "` + TestDataLongSubscriptionName + `", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "` + TestDataLongSubscriptionName + `", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( @@ -564,7 +564,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, no project provided": { - subscription: `{"instance_id": "jiraurl1", "name": "somename", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": [], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "somename", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": [], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( @@ -581,7 +581,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, no events provided": { - subscription: `{"instance_id": "jiraurl1", "name": "somename", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": [], "projects": ["otherproject"], "issue_types": ["10001"]}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "somename", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": [], "projects": ["otherproject"], "issue_types": ["10001"]}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( @@ -598,7 +598,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, no issue types provided": { - subscription: `{"instance_id": "jiraurl1", "name": "somename", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": []}}`, + subscription: `{"instance_id": "https://jiraurl1.com", "name": "somename", "id": "aaaaaaaaaaaaaaaaaaaaaaaaab", "channel_id": "aaaaaaaaaaaaaaaaaaaaaaaaac", "filters": {"events": ["jira:issue_created"], "projects": ["otherproject"], "issue_types": []}}`, expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( @@ -615,7 +615,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, GetProject mocked error. Existing sub has nonexistent project.": { - subscription: fmt.Sprintf(`{"instance_id": "jiraurl1", "id": "subaaaaaaaaaabbbbbbbbbbccc", "name": "subscription name", "channel_id": "channelaaaaaaaaaabbbbbbbbb", "filters": {"events": ["jira:issue_created"], "projects": ["%s"], "issue_types": ["10001"]}}`, nonExistantProjectKey), + subscription: fmt.Sprintf(`{"instance_id": "https://jiraurl1.com", "id": "subaaaaaaaaaabbbbbbbbbbccc", "name": "subscription name", "channel_id": "channelaaaaaaaaaabbbbbbbbb", "filters": {"events": ["jira:issue_created"], "projects": ["%s"], "issue_types": ["10001"]}}`, nonExistantProjectKey), expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( @@ -632,7 +632,7 @@ func TestEditSubscription(t *testing.T) { }), t), }, "Editing subscription, GetProject mocked error. Existing sub has existing project.": { - subscription: fmt.Sprintf(`{"instance_id": "jiraurl1", "id": "subaaaaaaaaaabbbbbbbbbbccc", "name": "subscription name", "channel_id": "channelaaaaaaaaaabbbbbbbbb", "filters": {"events": ["jira:issue_created"], "projects": ["%s"], "issue_types": ["10001"]}}`, nonExistantProjectKey), + subscription: fmt.Sprintf(`{"instance_id": "https://jiraurl1.com", "id": "subaaaaaaaaaabbbbbbbbbbccc", "name": "subscription name", "channel_id": "channelaaaaaaaaaabbbbbbbbb", "filters": {"events": ["jira:issue_created"], "projects": ["%s"], "issue_types": ["10001"]}}`, nonExistantProjectKey), expectedStatusCode: http.StatusInternalServerError, apiCalls: checkHasSubscriptions([]ChannelSubscription{}, withExistingChannelSubscriptions( diff --git a/server/issue_parser_test.go b/server/issue_parser_test.go index fc6811a09..03198d5ef 100644 --- a/server/issue_parser_test.go +++ b/server/issue_parser_test.go @@ -33,7 +33,7 @@ func TestAsSlackAttachment(t *testing.T) { }, }, expectedAttachment: &model.SlackAttachment{ - Text: "[MM-57208: (Open)](jiraurl2/browse/MM-57208)", + Text: "[MM-57208: (Open)](https://jiraurl2.com/browse/MM-57208)", Color: "#95b7d0", }, }, @@ -48,7 +48,7 @@ func TestAsSlackAttachment(t *testing.T) { }, }, expectedAttachment: &model.SlackAttachment{ - Text: "[MM-57208: A Summary (Open)](jiraurl2/browse/MM-57208)", + Text: "[MM-57208: A Summary (Open)](https://jiraurl2.com/browse/MM-57208)", Color: "#95b7d0", }, }, @@ -76,7 +76,7 @@ func TestAsSlackAttachment(t *testing.T) { }, }, expectedAttachment: &model.SlackAttachment{ - Text: "[MM-57208: A Summary (Open)](jiraurl2/browse/MM-57208)\n\nA Description\n", + Text: "[MM-57208: A Summary (Open)](https://jiraurl2.com/browse/MM-57208)\n\nA Description\n", Color: "#95b7d0", Fields: []*model.SlackAttachmentField{ { @@ -110,7 +110,7 @@ func TestAsSlackAttachment(t *testing.T) { }, showActions: true, expectedAttachment: &model.SlackAttachment{ - Text: "[MM-57208: (Open)](jiraurl2/browse/MM-57208)", + Text: "[MM-57208: (Open)](https://jiraurl2.com/browse/MM-57208)", Color: "#95b7d0", Actions: []*model.PostAction{ { diff --git a/server/kv_mock_test.go b/server/kv_mock_test.go index 37393fec8..b5b90e016 100644 --- a/server/kv_mock_test.go +++ b/server/kv_mock_test.go @@ -20,9 +20,9 @@ type testInstance struct { var _ Instance = (*testInstance)(nil) const ( - mockInstance1URL = "jiraurl1" - mockInstance2URL = "jiraurl2" - mockInstance3URL = "jiraurl3" + mockInstance1URL = "https://jiraurl1.com" + mockInstance2URL = "https://jiraurl2.com" + mockInstance3URL = "https://jiraurl3.com" ) var testInstance1 = &testInstance{ diff --git a/server/subscribe_test.go b/server/subscribe_test.go index 9ebb12bc7..6ebd8974e 100644 --- a/server/subscribe_test.go +++ b/server/subscribe_test.go @@ -238,7 +238,7 @@ func TestListChannelSubscriptions(t *testing.T) { }, }), RunAssertions: func(t *testing.T, actual string) { - expected := "The following channels have subscribed to Jira notifications. To modify a subscription, navigate to the channel and type `/jira subscribe edit`\n\n#### Team 1 Display Name\n* **~channel-1-name** (1):\n\t* (1) jiraurl1\n\t\t* PROJ - Sub Name X" + expected := "The following channels have subscribed to Jira notifications. To modify a subscription, navigate to the channel and type `/jira subscribe edit`\n\n#### Team 1 Display Name\n* **~channel-1-name** (1):\n\t* (1) https://jiraurl1.com\n\t\t* PROJ - Sub Name X" assert.Equal(t, expected, actual) }, }, @@ -262,7 +262,7 @@ func TestListChannelSubscriptions(t *testing.T) { }, }), RunAssertions: func(t *testing.T, actual string) { - expected := "The following channels have subscribed to Jira notifications. To modify a subscription, navigate to the channel and type `/jira subscribe edit`\n\n#### Group and Direct Messages\n* **channel-2-name-DM** (1):\n\t* (1) jiraurl1\n\t\t* PROJ - Sub Name X" + expected := "The following channels have subscribed to Jira notifications. To modify a subscription, navigate to the channel and type `/jira subscribe edit`\n\n#### Group and Direct Messages\n* **channel-2-name-DM** (1):\n\t* (1) https://jiraurl1.com\n\t\t* PROJ - Sub Name X" assert.Equal(t, expected, actual) }, },