package jira
import (
"encoding/json"
"fmt"
"github.com/google/go-cmp/cmp"
"io"
"io/ioutil"
"net/http"
"reflect"
"strings"
"testing"
"time"
"github.com/trivago/tgo/tcontainer"
)
func TestIssueService_Get_Success(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{}}`)
})
issue, _, err := testClient.Issue.Get("10002", nil)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_Get_WithQuerySuccess(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002?expand=foo")
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{}}`)
})
opt := &GetQueryOptions{
Expand: "foo",
}
issue, _, err := testClient.Issue.Get("10002", opt)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_Create(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue")
w.WriteHeader(http.StatusCreated)
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{}}`)
})
i := &Issue{
Fields: &IssueFields{
Description: "example bug report",
},
}
issue, _, err := testClient.Issue.Create(i)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_CreateThenGet(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue")
w.WriteHeader(http.StatusCreated)
io.Copy(w, r.Body)
})
i := &Issue{
Fields: &IssueFields{
Description: "example bug report",
Created: Time(time.Now()),
},
}
issue, _, err := testClient.Issue.Create(i)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
bytes, err := json.Marshal(issue)
if err != nil {
t.Errorf("Error marshaling issue: %s", err)
}
_, err = w.Write(bytes)
if err != nil {
t.Errorf("Error writing response: %s", err)
}
})
issue2, _, err := testClient.Issue.Get("10002", nil)
if issue2 == nil {
t.Error("Expected issue. Issue is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_Update(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/PROJ-9001", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
testRequestURL(t, r, "/rest/api/2/issue/PROJ-9001")
w.WriteHeader(http.StatusNoContent)
})
i := &Issue{
Key: "PROJ-9001",
Fields: &IssueFields{
Description: "example bug report",
},
}
issue, _, err := testClient.Issue.Update(i)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_UpdateIssue(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/PROJ-9001", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
testRequestURL(t, r, "/rest/api/2/issue/PROJ-9001")
w.WriteHeader(http.StatusNoContent)
})
jID := "PROJ-9001"
i := make(map[string]interface{})
fields := make(map[string]interface{})
i["fields"] = fields
resp, err := testClient.Issue.UpdateIssue(jID, i)
if resp == nil {
t.Error("Expected resp. resp is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_AddComment(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/comment", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue/10000/comment")
w.WriteHeader(http.StatusCreated)
fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}`)
})
c := &Comment{
Body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
Visibility: CommentVisibility{
Type: "role",
Value: "Administrators",
},
}
comment, _, err := testClient.Issue.AddComment("10000", c)
if comment == nil {
t.Error("Expected Comment. Comment is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_UpdateComment(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/comment/10001", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
testRequestURL(t, r, "/rest/api/2/issue/10000/comment/10001")
w.WriteHeader(http.StatusCreated)
fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10001","id":"10001","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}`)
})
c := &Comment{
ID: "10001",
Body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
Visibility: CommentVisibility{
Type: "role",
Value: "Administrators",
},
}
comment, _, err := testClient.Issue.UpdateComment("10000", c)
if comment == nil {
t.Error("Expected Comment. Comment is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_DeleteComment(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/comment/10001", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testRequestURL(t, r, "/rest/api/2/issue/10000/comment/10001")
w.WriteHeader(http.StatusNoContent)
fmt.Fprint(w, `{}`)
})
err := testClient.Issue.DeleteComment("10000", "10001")
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_AddWorklogRecord(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/worklog", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue/10000/worklog")
w.WriteHeader(http.StatusCreated)
fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2018-02-14T22:14:46.003+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2018-02-14T22:14:46.003+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}`)
})
r := &WorklogRecord{
TimeSpent: "1h",
}
record, _, err := testClient.Issue.AddWorklogRecord("10000", r)
if record == nil {
t.Error("Expected Record. Record is nil")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_AddLink(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issueLink", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issueLink")
w.WriteHeader(http.StatusOK)
})
il := &IssueLink{
Type: IssueLinkType{
Name: "Duplicate",
},
InwardIssue: &Issue{
Key: "HSP-1",
},
OutwardIssue: &Issue{
Key: "MKY-1",
},
Comment: &Comment{
Body: "Linked related issue!",
Visibility: CommentVisibility{
Type: "group",
Value: "jira-software-users",
},
},
}
resp, err := testClient.Issue.AddLink(il)
if resp == nil {
t.Error("Expected response. Response is nil")
}
if resp.StatusCode != 200 {
t.Errorf("Expected Status code 200. Given %d", resp.StatusCode)
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_Get_Fields(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"labels":["test"],"watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"epic": {"id": 19415,"key": "EPIC-77","self": "https://example.atlassian.net/rest/agile/1.0/epic/19415","name": "Epic Name","summary": "Do it","color": {"key": "color_11"},"done": false},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{}}`)
})
issue, _, err := testClient.Issue.Get("10002", nil)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if !reflect.DeepEqual(issue.Fields.Labels, []string{"test"}) {
t.Error("Expected labels for the returned issue")
}
if len(issue.Fields.Comments.Comments) != 1 {
t.Errorf("Expected one comment, %v found", len(issue.Fields.Comments.Comments))
}
if issue.Fields.Epic == nil {
t.Error("Epic expected but not found")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_Get_RenderedFields(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"labels":["test"],"watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"epic": {"id": 19415,"key": "EPIC-77","self": "https://example.atlassian.net/rest/agile/1.0/epic/19415","name": "Epic Name","summary": "Do it","color": {"key": "color_11"},"done": false},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{},"renderedFields":{"resolutiondate":"In 1 week","updated":"2 hours ago","comment":{"comments":[{"body":"This is HTML"}]}}}`)
})
issue, _, err := testClient.Issue.Get("10002", nil)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if issue.RenderedFields.Updated != "2 hours ago" {
t.Error("Expected updated to equla '2 hours ago' for rendered field")
}
if len(issue.RenderedFields.Comments.Comments) != 1 {
t.Errorf("Expected one comment, %v found", len(issue.RenderedFields.Comments.Comments))
}
comment := issue.RenderedFields.Comments.Comments[0]
if comment.Body != "This is HTML" {
t.Errorf("Wrong comment body returned in RenderedField. Got %s", comment.Body)
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_DownloadAttachment(t *testing.T) {
var testAttachment = "Here is an attachment"
setup()
defer teardown()
testMux.HandleFunc("/secure/attachment/", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/secure/attachment/10000/")
w.WriteHeader(http.StatusOK)
w.Write([]byte(testAttachment))
})
resp, err := testClient.Issue.DownloadAttachment("10000")
if resp == nil {
t.Error("Expected response. Response is nil")
}
defer resp.Body.Close()
attachment, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Error("Expected attachment text", err)
}
if string(attachment) != testAttachment {
t.Errorf("Expecting an attachment: %s", string(attachment))
}
if resp.StatusCode != 200 {
t.Errorf("Expected Status code 200. Given %d", resp.StatusCode)
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_DownloadAttachment_BadStatus(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/secure/attachment/", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/secure/attachment/10000/")
w.WriteHeader(http.StatusForbidden)
})
resp, err := testClient.Issue.DownloadAttachment("10000")
if resp == nil {
t.Error("Expected response. Response is nil")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Errorf("Expected Status code %d. Given %d", http.StatusForbidden, resp.StatusCode)
}
if err == nil {
t.Errorf("Error expected")
}
}
func TestIssueService_PostAttachment(t *testing.T) {
var testAttachment = "Here is an attachment"
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/attachments", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue/10000/attachments")
status := http.StatusOK
file, _, err := r.FormFile("file")
if err != nil {
status = http.StatusNotAcceptable
}
if file == nil {
status = http.StatusNoContent
} else {
// Read the file into memory
data, err := ioutil.ReadAll(file)
if err != nil {
status = http.StatusInternalServerError
}
if string(data) != testAttachment {
status = http.StatusNotAcceptable
}
w.WriteHeader(status)
fmt.Fprint(w, `[{"self":"http://jira/jira/rest/api/2/attachment/228924","id":"228924","filename":"example.jpg","author":{"self":"http://jira/jira/rest/api/2/user?username=test","name":"test","emailAddress":"test@test.com","avatarUrls":{"16x16":"http://jira/jira/secure/useravatar?size=small&avatarId=10082","48x48":"http://jira/jira/secure/useravatar?avatarId=10082"},"displayName":"Tester","active":true},"created":"2016-05-24T00:25:17.000-0700","size":32280,"mimeType":"image/jpeg","content":"http://jira/jira/secure/attachment/228924/example.jpg","thumbnail":"http://jira/jira/secure/thumbnail/228924/_thumb_228924.png"}]`)
file.Close()
}
})
reader := strings.NewReader(testAttachment)
issue, resp, err := testClient.Issue.PostAttachment("10000", reader, "attachment")
if issue == nil {
t.Error("Expected response. Response is nil")
}
if resp.StatusCode != 200 {
t.Errorf("Expected Status code 200. Given %d", resp.StatusCode)
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_PostAttachment_NoResponse(t *testing.T) {
var testAttachment = "Here is an attachment"
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/attachments", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue/10000/attachments")
w.WriteHeader(http.StatusOK)
})
reader := strings.NewReader(testAttachment)
_, _, err := testClient.Issue.PostAttachment("10000", reader, "attachment")
if err == nil {
t.Errorf("Error expected: %s", err)
}
}
func TestIssueService_PostAttachment_NoFilename(t *testing.T) {
var testAttachment = "Here is an attachment"
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/attachments", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue/10000/attachments")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `[{"self":"http://jira/jira/rest/api/2/attachment/228924","id":"228924","filename":"example.jpg","author":{"self":"http://jira/jira/rest/api/2/user?username=test","name":"test","emailAddress":"test@test.com","avatarUrls":{"16x16":"http://jira/jira/secure/useravatar?size=small&avatarId=10082","48x48":"http://jira/jira/secure/useravatar?avatarId=10082"},"displayName":"Tester","active":true},"created":"2016-05-24T00:25:17.000-0700","size":32280,"mimeType":"image/jpeg","content":"http://jira/jira/secure/attachment/228924/example.jpg","thumbnail":"http://jira/jira/secure/thumbnail/228924/_thumb_228924.png"}]`)
})
reader := strings.NewReader(testAttachment)
_, _, err := testClient.Issue.PostAttachment("10000", reader, "")
if err != nil {
t.Errorf("Error expected: %s", err)
}
}
func TestIssueService_PostAttachment_NoAttachment(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10000/attachments", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/api/2/issue/10000/attachments")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `[{"self":"http://jira/jira/rest/api/2/attachment/228924","id":"228924","filename":"example.jpg","author":{"self":"http://jira/jira/rest/api/2/user?username=test","name":"test","emailAddress":"test@test.com","avatarUrls":{"16x16":"http://jira/jira/secure/useravatar?size=small&avatarId=10082","48x48":"http://jira/jira/secure/useravatar?avatarId=10082"},"displayName":"Tester","active":true},"created":"2016-05-24T00:25:17.000-0700","size":32280,"mimeType":"image/jpeg","content":"http://jira/jira/secure/attachment/228924/example.jpg","thumbnail":"http://jira/jira/secure/thumbnail/228924/_thumb_228924.png"}]`)
})
_, _, err := testClient.Issue.PostAttachment("10000", nil, "attachment")
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_DeleteAttachment(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/attachment/10054", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testRequestURL(t, r, "/rest/api/2/attachment/10054")
w.WriteHeader(http.StatusNoContent)
fmt.Fprint(w, `{}`)
})
resp, err := testClient.Issue.DeleteAttachment("10054")
if resp.StatusCode != 204 {
t.Error("Expected attachment not deleted.")
if resp.StatusCode == 403 {
t.Error("User not permitted to delete attachment")
}
if resp.StatusCode == 404 {
t.Error("Attachment not found")
}
} else {
t.Log("Attachment deleted")
}
if err != nil {
t.Errorf("Error given: %s", err)
} else {
t.Log("No error")
}
}
func TestIssueService_Search(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/search?jql=something&startAt=1&maxResults=40&expand=foo")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"expand": "schema,names","startAt": 1,"maxResults": 40,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`)
})
opt := &SearchOptions{StartAt: 1, MaxResults: 40, Expand: "foo"}
_, resp, err := testClient.Issue.Search("something", opt)
if resp == nil {
t.Errorf("Response given: %+v", resp)
}
if err != nil {
t.Errorf("Error given: %s", err)
}
if resp.StartAt != 1 {
t.Errorf("StartAt should populate with 1, %v given", resp.StartAt)
}
if resp.MaxResults != 40 {
t.Errorf("StartAt should populate with 40, %v given", resp.MaxResults)
}
if resp.Total != 6 {
t.Errorf("StartAt should populate with 6, %v given", resp.Total)
}
}
func TestIssueService_Search_WithoutPaging(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/search?jql=something")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"expand": "schema,names","startAt": 0,"maxResults": 50,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`)
})
_, resp, err := testClient.Issue.Search("something", nil)
if resp == nil {
t.Errorf("Response given: %+v", resp)
}
if err != nil {
t.Errorf("Error given: %s", err)
}
if resp.StartAt != 0 {
t.Errorf("StartAt should populate with 0, %v given", resp.StartAt)
}
if resp.MaxResults != 50 {
t.Errorf("StartAt should populate with 50, %v given", resp.MaxResults)
}
if resp.Total != 6 {
t.Errorf("StartAt should populate with 6, %v given", resp.Total)
}
}
func TestIssueService_SearchPages(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
if r.URL.String() == "/rest/api/2/search?jql=something&startAt=1&maxResults=2&expand=foo&validateQuery=warn" {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"expand": "schema,names","startAt": 1,"maxResults": 2,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`)
return
} else if r.URL.String() == "/rest/api/2/search?jql=something&startAt=3&maxResults=2&expand=foo&validateQuery=warn" {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"expand": "schema,names","startAt": 3,"maxResults": 2,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`)
return
} else if r.URL.String() == "/rest/api/2/search?jql=something&startAt=5&maxResults=2&expand=foo&validateQuery=warn" {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"expand": "schema,names","startAt": 5,"maxResults": 2,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}}]}`)
return
}
t.Errorf("Unexpected URL: %v", r.URL)
})
opt := &SearchOptions{StartAt: 1, MaxResults: 2, Expand: "foo", ValidateQuery: "warn"}
issues := make([]Issue, 0)
err := testClient.Issue.SearchPages("something", opt, func(issue Issue) error {
issues = append(issues, issue)
return nil
})
if err != nil {
t.Errorf("Error given: %s", err)
}
if len(issues) != 5 {
t.Errorf("Expected 5 issues, %v given", len(issues))
}
}
func TestIssueService_GetCustomFields(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"customfield_123":"test","watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{}}`)
})
issue, _, err := testClient.Issue.GetCustomFields("10002")
if err != nil {
t.Errorf("Error given: %s", err)
}
if issue == nil {
t.Error("Expected Customfields")
}
cf := issue["customfield_123"]
if cf != "test" {
t.Error("Expected \"test\" for custom field")
}
}
func TestIssueService_GetComplexCustomFields(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","fields":{"customfield_123":{"self":"http://www.example.com/jira/rest/api/2/customFieldOption/123","value":"test","id":"123"},"watcher":{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]},"attachment":[{"self":"http://www.example.com/jira/rest/api/2.0/attachments/10000","filename":"picture.jpg","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred","24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred","32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.461+0000","size":23123,"mimeType":"image/jpeg","content":"http://www.example.com/jira/attachments/10000","thumbnail":"http://www.example.com/jira/secure/thumbnail/10000"}],"sub-tasks":[{"id":"10000","type":{"id":"10000","name":"","inward":"Parent","outward":"Sub-task"},"outwardIssue":{"id":"10003","key":"EX-2","self":"http://www.example.com/jira/rest/api/2/issue/EX-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"description":"example bug report","project":{"self":"http://www.example.com/jira/rest/api/2/project/EX","id":"10000","key":"EX","name":"Example","avatarUrls":{"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000","24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000","16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000","32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"},"projectCategory":{"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000","id":"10000","name":"FIRST","description":"First Project Category"}},"comment":{"comments":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000","id":"10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.","updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","visibility":{"type":"role","value":"Administrators"}}]},"issuelinks":[{"id":"10001","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"outwardIssue":{"id":"10004L","key":"PRJ-2","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}},{"id":"10002","type":{"id":"10000","name":"Dependent","inward":"depends on","outward":"is depended by"},"inwardIssue":{"id":"10004","key":"PRJ-3","self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3","fields":{"status":{"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png","name":"Open"}}}}],"worklog":{"worklogs":[{"self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"comment":"I did some work here.","updated":"2016-03-16T04:22:37.471+0000","visibility":{"type":"group","value":"jira-developers"},"started":"2016-03-16T04:22:37.471+0000","timeSpent":"3h 20m","timeSpentSeconds":12000,"id":"100028","issueId":"10002"}]},"updated":"2016-04-06T02:36:53.594-0700","duedate":"2018-01-19","timetracking":{"originalEstimate":"10m","remainingEstimate":"3m","timeSpent":"6m","originalEstimateSeconds":600,"remainingEstimateSeconds":200,"timeSpentSeconds":400}},"names":{"watcher":"watcher","attachment":"attachment","sub-tasks":"sub-tasks","description":"description","project":"project","comment":"comment","issuelinks":"issuelinks","worklog":"worklog","updated":"updated","timetracking":"timetracking"},"schema":{}}`)
})
issue, _, err := testClient.Issue.GetCustomFields("10002")
if err != nil {
t.Errorf("Error given: %s", err)
}
if issue == nil {
t.Error("Expected Customfields")
}
cf := issue["customfield_123"]
if cf != "test" {
t.Error("Expected \"test\" for custom field")
}
}
func TestIssueService_GetTransitions(t *testing.T) {
setup()
defer teardown()
testAPIEndpoint := "/rest/api/2/issue/123/transitions"
raw, err := ioutil.ReadFile("./mocks/transitions.json")
if err != nil {
t.Error(err.Error())
}
testMux.HandleFunc(testAPIEndpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, testAPIEndpoint)
fmt.Fprint(w, string(raw))
})
transitions, _, err := testClient.Issue.GetTransitions("123")
if err != nil {
t.Errorf("Got error: %v", err)
}
if transitions == nil {
t.Error("Expected transition list. Got nil.")
}
if len(transitions) != 2 {
t.Errorf("Expected 2 transitions. Got %d", len(transitions))
}
if transitions[0].Fields["summary"].Required != false {
t.Errorf("First transition summary field should not be required")
}
}
func TestIssueService_DoTransition(t *testing.T) {
setup()
defer teardown()
testAPIEndpoint := "/rest/api/2/issue/123/transitions"
transitionID := "22"
testMux.HandleFunc(testAPIEndpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, testAPIEndpoint)
decoder := json.NewDecoder(r.Body)
var payload CreateTransitionPayload
err := decoder.Decode(&payload)
if err != nil {
t.Errorf("Got error: %v", err)
}
if payload.Transition.ID != transitionID {
t.Errorf("Expected %s to be in payload, got %s instead", transitionID, payload.Transition.ID)
}
})
_, err := testClient.Issue.DoTransition("123", transitionID)
if err != nil {
t.Errorf("Got error: %v", err)
}
}
func TestIssueService_DoTransitionWithPayload(t *testing.T) {
setup()
defer teardown()
testAPIEndpoint := "/rest/api/2/issue/123/transitions"
transitionID := "22"
customPayload := map[string]interface{}{
"update": map[string]interface{}{
"comment": []map[string]interface{}{
{
"add": map[string]string{
"body": "Hello World",
},
},
},
},
"transition": TransitionPayload{
ID: transitionID,
},
}
testMux.HandleFunc(testAPIEndpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, testAPIEndpoint)
decoder := json.NewDecoder(r.Body)
payload := map[string]interface{}{}
err := decoder.Decode(&payload)
if err != nil {
t.Errorf("Got error: %v", err)
}
contains := func(key string) bool {
_, ok := payload[key]
return ok
}
if !contains("update") || !contains("transition") {
t.Fatalf("Excpected update, transition to be in payload, got %s instead", payload)
}
transition, ok := payload["transition"].(map[string]interface{})
if !ok {
t.Fatalf("Excpected transition to be in payload, got %s instead", payload["transition"])
}
if transition["id"].(string) != transitionID {
t.Errorf("Expected %s to be in payload, got %s instead", transitionID, transition["id"])
}
})
_, err := testClient.Issue.DoTransitionWithPayload("123", customPayload)
if err != nil {
t.Errorf("Got error: %v", err)
}
}
func TestIssueFields_TestMarshalJSON_PopulateUnknownsSuccess(t *testing.T) {
data := `{
"customfield_123":"test",
"description":"example bug report",
"project":{
"self":"http://www.example.com/jira/rest/api/2/project/EX",
"id":"10000",
"key":"EX",
"name":"Example",
"avatarUrls":{
"48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000",
"24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000",
"16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000",
"32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"
},
"projectCategory":{
"self":"http://www.example.com/jira/rest/api/2/projectCategory/10000",
"id":"10000",
"name":"FIRST",
"description":"First Project Category"
}
},
"issuelinks":[
{
"id":"10001",
"type":{
"id":"10000",
"name":"Dependent",
"inward":"depends on",
"outward":"is depended by"
},
"outwardIssue":{
"id":"10004L",
"key":"PRJ-2",
"self":"http://www.example.com/jira/rest/api/2/issue/PRJ-2",
"fields":{
"status":{
"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png",
"name":"Open"
}
}
}
},
{
"id":"10002",
"type":{
"id":"10000",
"name":"Dependent",
"inward":"depends on",
"outward":"is depended by"
},
"inwardIssue":{
"id":"10004",
"key":"PRJ-3",
"self":"http://www.example.com/jira/rest/api/2/issue/PRJ-3",
"fields":{
"status":{
"iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png",
"name":"Open"
}
}
}
}
]
}`
i := new(IssueFields)
err := json.Unmarshal([]byte(data), i)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
if len(i.Unknowns) != 1 {
t.Errorf("Expected 1 unknown field to be present, received %d", len(i.Unknowns))
}
if i.Description != "example bug report" {
t.Errorf("Expected description to be \"%s\", received \"%s\"", "example bug report", i.Description)
}
}
func TestIssueFields_MarshalJSON_OmitsEmptyFields(t *testing.T) {
i := &IssueFields{
Description: "blahblah",
Type: IssueType{
Name: "Story",
},
Labels: []string{"aws-docker"},
}
rawdata, err := json.Marshal(i)
if err != nil {
t.Errorf("Expected nil err, received %s", err)
}
// convert json to map and see if unset keys are there
issuef := tcontainer.NewMarshalMap()
err = json.Unmarshal(rawdata, &issuef)
if err != nil {
t.Errorf("Expected nil err, received %s", err)
}
_, err = issuef.Int("issuetype/avatarId")
if err == nil {
t.Error("Expected non nil error, received nil")
}
// verify that the field that should be there, is.
name, err := issuef.String("issuetype/name")
if err != nil {
t.Errorf("Expected nil err, received %s", err)
}
if name != "Story" {
t.Errorf("Expected Story, received %s", name)
}
}
func TestIssueFields_MarshalJSON_Success(t *testing.T) {
i := &IssueFields{
Description: "example bug report",
Unknowns: tcontainer.MarshalMap{
"customfield_123": "test",
},
Project: Project{
Self: "http://www.example.com/jira/rest/api/2/project/EX",
ID: "10000",
Key: "EX",
},
AffectsVersions: []*AffectsVersion{
&AffectsVersion{
ID: "10705",
Name: "2.1.0-rc3",
Self: "http://www.example.com/jira/rest/api/2/version/10705",
ReleaseDate: "2018-09-30",
},
},
}
bytes, err := json.Marshal(i)
if err != nil {
t.Errorf("Expected nil err, received %s", err)
}
received := new(IssueFields)
// the order of json might be different. so unmarshal it again and compare objects
err = json.Unmarshal(bytes, received)
if err != nil {
t.Errorf("Expected nil err, received %s", err)
}
if !reflect.DeepEqual(i, received) {
t.Errorf("Received object different from expected. Expected %+v, received %+v", i, received)
}
}
func TestInitIssueWithMetaAndFields_Success(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["summary"] = map[string]interface{}{
"name": "Summary",
"schema": map[string]interface{}{
"type": "string",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedSummary := "Issue Summary"
fieldConfig := map[string]string{
"Summary": "Issue Summary",
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
gotSummary, found := issue.Fields.Unknowns["summary"]
if !found {
t.Errorf("Expected summary to be set in issue. Not set.")
}
if gotSummary != expectedSummary {
t.Errorf("Expected %s received %s", expectedSummary, gotSummary)
}
}
func TestInitIssueWithMetaAndFields_ArrayValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["component"] = map[string]interface{}{
"name": "Component/s",
"schema": map[string]interface{}{
"type": "array",
"items": "component",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedComponent := "Jira automation"
fieldConfig := map[string]string{
"Component/s": expectedComponent,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
c, isArray := issue.Fields.Unknowns["component"].([]Component)
if isArray == false {
t.Error("Expected array, non array object received")
}
if len(c) != 1 {
t.Errorf("Expected received array to be of length 1. Got %d", len(c))
}
gotComponent := c[0].Name
if err != nil {
t.Errorf("Expected err to be nil, received %s", err)
}
if gotComponent != expectedComponent {
t.Errorf("Expected %s received %s", expectedComponent, gotComponent)
}
}
func TestInitIssueWithMetaAndFields_DateValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["created"] = map[string]interface{}{
"name": "Created",
"schema": map[string]interface{}{
"type": "date",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedCreated := "19 oct 2012"
fieldConfig := map[string]string{
"Created": expectedCreated,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
gotCreated, err := issue.Fields.Unknowns.String("created")
if err != nil {
t.Errorf("Expected err to be nil, received %s", err)
}
if gotCreated != expectedCreated {
t.Errorf("Expected %s received %s", expectedCreated, gotCreated)
}
}
func TestInitIssueWithMetaAndFields_UserValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["assignee"] = map[string]interface{}{
"name": "Assignee",
"schema": map[string]interface{}{
"type": "user",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedAssignee := "jdoe"
fieldConfig := map[string]string{
"Assignee": expectedAssignee,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
a, _ := issue.Fields.Unknowns.Value("assignee")
gotAssignee := a.(User).Name
if gotAssignee != expectedAssignee {
t.Errorf("Expected %s received %s", expectedAssignee, gotAssignee)
}
}
func TestInitIssueWithMetaAndFields_ProjectValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["project"] = map[string]interface{}{
"name": "Project",
"schema": map[string]interface{}{
"type": "project",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
setProject := "somewhere"
fieldConfig := map[string]string{
"Project": setProject,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
a, _ := issue.Fields.Unknowns.Value("project")
gotProject := a.(Project).Name
if gotProject != metaProject.Name {
t.Errorf("Expected %s received %s", metaProject.Name, gotProject)
}
}
func TestInitIssueWithMetaAndFields_PriorityValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["priority"] = map[string]interface{}{
"name": "Priority",
"schema": map[string]interface{}{
"type": "priority",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedPriority := "Normal"
fieldConfig := map[string]string{
"Priority": expectedPriority,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
a, _ := issue.Fields.Unknowns.Value("priority")
gotPriority := a.(Priority).Name
if gotPriority != expectedPriority {
t.Errorf("Expected %s received %s", expectedPriority, gotPriority)
}
}
func TestInitIssueWithMetaAndFields_SelectList(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["someitem"] = map[string]interface{}{
"name": "A Select Item",
"schema": map[string]interface{}{
"type": "option",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedVal := "Value"
fieldConfig := map[string]string{
"A Select Item": expectedVal,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
a, _ := issue.Fields.Unknowns.Value("someitem")
gotVal := a.(Option).Value
if gotVal != expectedVal {
t.Errorf("Expected %s received %s", expectedVal, gotVal)
}
}
func TestInitIssueWithMetaAndFields_IssuetypeValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["issuetype"] = map[string]interface{}{
"name": "Issue type",
"schema": map[string]interface{}{
"type": "issuetype",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
expectedIssuetype := "Bug"
fieldConfig := map[string]string{
"Issue type": expectedIssuetype,
}
issue, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err != nil {
t.Errorf("Expected nil error, received %s", err)
}
a, _ := issue.Fields.Unknowns.Value("issuetype")
gotIssuetype := a.(IssueType).Name
if gotIssuetype != expectedIssuetype {
t.Errorf("Expected %s received %s", expectedIssuetype, gotIssuetype)
}
}
func TestInitIssueWithmetaAndFields_FailureWithUnknownValueType(t *testing.T) {
metaProject := MetaProject{
Name: "Engineering - Dept",
Id: "ENG",
}
fields := tcontainer.NewMarshalMap()
fields["issuetype"] = map[string]interface{}{
"name": "Issue type",
"schema": map[string]interface{}{
"type": "randomType",
},
}
metaIssueType := MetaIssueType{
Fields: fields,
}
fieldConfig := map[string]string{
"Issue tyoe": "sometype",
}
_, err := InitIssueWithMetaAndFields(&metaProject, &metaIssueType, fieldConfig)
if err == nil {
t.Error("Expected non nil error, received nil")
}
}
func TestIssueService_Delete(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testRequestURL(t, r, "/rest/api/2/issue/10002")
w.WriteHeader(http.StatusNoContent)
fmt.Fprint(w, `{}`)
})
resp, err := testClient.Issue.Delete("10002")
if resp.StatusCode != 204 {
t.Error("Expected issue not deleted.")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func getTime(original time.Time) *Time {
jiraTime := Time(original)
return &jiraTime
}
func TestIssueService_GetWorklogs(t *testing.T) {
setup()
defer teardown()
tt := []struct {
name string
response string
issueId string
uri string
worklog *Worklog
err error
option *AddWorklogQueryOptions
}{
{
name: "simple worklog",
response: `{"startAt": 1,"maxResults": 40,"total": 1,"worklogs": [{"id": "3","self": "http://kelpie9:8081/rest/api/2/issue/10002/worklog/3","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","comment":"","started":"2016-03-16T04:22:37.356+0000","timeSpent": "1h","timeSpentSeconds": 3600,"issueId":"10002"}]}`,
issueId: "10002",
uri: "/rest/api/2/issue/%s/worklog",
worklog: &Worklog{
StartAt: 1,
MaxResults: 40,
Total: 1,
Worklogs: []WorklogRecord{
{
Self: "http://kelpie9:8081/rest/api/2/issue/10002/worklog/3",
Author: &User{
Self: "http://www.example.com/jira/rest/api/2/user?username=fred",
Name: "fred",
DisplayName: "Fred F. User",
},
UpdateAuthor: &User{
Self: "http://www.example.com/jira/rest/api/2/user?username=fred",
Name: "fred",
DisplayName: "Fred F. User",
},
Created: getTime(time.Date(2016, time.March, 16, 4, 22, 37, 356000000, time.UTC)),
Started: getTime(time.Date(2016, time.March, 16, 4, 22, 37, 356000000, time.UTC)),
Updated: getTime(time.Date(2016, time.March, 16, 4, 22, 37, 356000000, time.UTC)),
TimeSpent: "1h",
TimeSpentSeconds: 3600,
ID: "3",
IssueID: "10002",
},
},
},
},
{
name: "expanded worklog",
response: `{"startAt":1,"maxResults":40,"total":1,"worklogs":[{"id":"3","self":"http://kelpie9:8081/rest/api/2/issue/10002/worklog/3","author":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"updateAuthor":{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false},"created":"2016-03-16T04:22:37.356+0000","updated":"2016-03-16T04:22:37.356+0000","comment":"","started":"2016-03-16T04:22:37.356+0000","timeSpent":"1h","timeSpentSeconds":3600,"issueId":"10002","properties":[{"key":"foo","value":{"bar":"baz"}}]}]}`,
issueId: "10002",
uri: "/rest/api/2/issue/%s/worklog?expand=properties",
worklog: &Worklog{
StartAt: 1,
MaxResults: 40,
Total: 1,
Worklogs: []WorklogRecord{
{
Self: "http://kelpie9:8081/rest/api/2/issue/10002/worklog/3",
Author: &User{
Self: "http://www.example.com/jira/rest/api/2/user?username=fred",
Name: "fred",
DisplayName: "Fred F. User",
},
UpdateAuthor: &User{
Self: "http://www.example.com/jira/rest/api/2/user?username=fred",
Name: "fred",
DisplayName: "Fred F. User",
},
Created: getTime(time.Date(2016, time.March, 16, 4, 22, 37, 356000000, time.UTC)),
Started: getTime(time.Date(2016, time.March, 16, 4, 22, 37, 356000000, time.UTC)),
Updated: getTime(time.Date(2016, time.March, 16, 4, 22, 37, 356000000, time.UTC)),
TimeSpent: "1h",
TimeSpentSeconds: 3600,
ID: "3",
IssueID: "10002",
Properties: []EntityProperty{
{
Key: "foo",
Value: map[string]interface{}{
"bar": "baz",
},
},
},
},
},
},
option: &AddWorklogQueryOptions{Expand: "properties"},
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
uri := fmt.Sprintf(tc.uri, tc.issueId)
testMux.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, uri)
_, _ = fmt.Fprint(w, tc.response)
})
var worklog *Worklog
var err error
if tc.option != nil {
worklog, _, err = testClient.Issue.GetWorklogs(tc.issueId, WithQueryOptions(tc.option))
} else {
worklog, _, err = testClient.Issue.GetWorklogs(tc.issueId)
}
if err != nil && !cmp.Equal(err, tc.err) {
t.Errorf("unexpected error: %v", err)
}
if !cmp.Equal(worklog, tc.worklog) {
t.Errorf("unexpected worklog structure: %s", cmp.Diff(worklog, tc.worklog))
}
})
}
}
func TestIssueService_GetWatchers(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002/watchers", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002/watchers")
fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers","isWatching":false,"watchCount":1,"watchers":[{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","name":"fred","displayName":"Fred F. User","active":false}]}`)
})
testMux.HandleFunc("/rest/api/2/user", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/user?username=fred")
fmt.Fprint(w, `{"self":"http://www.example.com/jira/rest/api/2/user?username=fred","key":"fred",
"name":"fred","emailAddress":"fred@example.com","avatarUrls":{"48x48":"http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred",
"24x24":"http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred","16x16":"http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred",
"32x32":"http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"},"displayName":"Fred F. User","active":true,"timeZone":"Australia/Sydney","groups":{"size":3,"items":[
{"name":"jira-user","self":"http://www.example.com/jira/rest/api/2/group?groupname=jira-user"},{"name":"jira-admin",
"self":"http://www.example.com/jira/rest/api/2/group?groupname=jira-admin"},{"name":"important","self":"http://www.example.com/jira/rest/api/2/group?groupname=important"
}]},"applicationRoles":{"size":1,"items":[]},"expand":"groups,applicationRoles"}`)
})
watchers, _, err := testClient.Issue.GetWatchers("10002")
if err != nil {
t.Errorf("Error given: %s", err)
return
}
if watchers == nil {
t.Error("Expected watchers. Watchers is nil")
return
}
if len(*watchers) != 1 {
t.Errorf("Expected 1 watcher, got: %d", len(*watchers))
return
}
if (*watchers)[0].Name != "fred" {
t.Error("Expected watcher name fred")
}
}
func TestIssueService_UpdateAssignee(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002/assignee", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
testRequestURL(t, r, "/rest/api/2/issue/10002/assignee")
w.WriteHeader(http.StatusNoContent)
})
resp, err := testClient.Issue.UpdateAssignee("10002", &User{
Name: "test-username",
})
if resp.StatusCode != 204 {
t.Error("Expected issue not re-assigned.")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}
func TestIssueService_Get_Fields_Changelog(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"expand":"changelog","id":"10002","self":"http://www.example.com/jira/rest/api/2/issue/10002","key":"EX-1","changelog":{"startAt": 0,"maxResults": 1, "total": 1, "histories": [{"id": "10002", "author": {"self": "http://www.example.com/jira/rest/api/2/user?username=fred", "name": "fred", "key": "fred", "emailAddress": "fred@example.com", "avatarUrls": {"48x48": "http://www.example.com/secure/useravatar?ownerId=fred&avatarId=33072", "24x24": "http://www.example.com/secure/useravatar?size=small&ownerId=fred&avatarId=33072", "16x16": "http://www.example.com/secure/useravatar?size=xsmall&ownerId=fred&avatarId=33072", "32x32": "http://www.example.com/secure/useravatar?size=medium&ownerId=fred&avatarId=33072"},"displayName":"Fred","active": true,"timeZone":"Australia/Sydney"},"created":"2018-06-20T16:50:35.000+0300","items":[{"field":"Rank","fieldtype":"custom","from":"","fromString":"","to":"","toString":"Ranked higher"}]}]}}`)
})
issue, _, _ := testClient.Issue.Get("10002", &GetQueryOptions{Expand: "changelog"})
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if len(issue.Changelog.Histories) != 1 {
t.Errorf("Expected one history item, %v found", len(issue.Changelog.Histories))
}
if issue.Changelog.Histories[0].Created != "2018-06-20T16:50:35.000+0300" {
t.Errorf("Expected created time of history item 2018-06-20T16:50:35.000+0300, %v got", issue.Changelog.Histories[0].Created)
}
tm, _ := time.Parse("2006-01-02T15:04:05.999-0700", "2018-06-20T16:50:35.000+0300")
if ct, _ := issue.Changelog.Histories[0].CreatedTime(); !tm.Equal(ct) {
t.Errorf("Expected CreatedTime func return %v time, %v got", tm, ct)
}
}
func TestIssueService_Get_Fields_AffectsVersions(t *testing.T) {
setup()
defer teardown()
testMux.HandleFunc("/rest/api/2/issue/10002", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, "/rest/api/2/issue/10002")
fmt.Fprint(w, `{"fields":{"versions":[{"self":"http://www.example.com/jira/rest/api/2/version/10705","id":"10705","description":"test description","name":"2.1.0-rc3","archived":false,"released":false,"releaseDate":"2018-09-30"}]}}`)
})
issue, _, err := testClient.Issue.Get("10002", nil)
if issue == nil {
t.Error("Expected issue. Issue is nil")
}
if !reflect.DeepEqual(issue.Fields.AffectsVersions, []*AffectsVersion{
{
ID: "10705",
Name: "2.1.0-rc3",
Self: "http://www.example.com/jira/rest/api/2/version/10705",
ReleaseDate: "2018-09-30",
Released: false,
Archived: false,
Description: "test description",
},
}) {
t.Error("Expected AffectsVersions for the returned issue")
}
if err != nil {
t.Errorf("Error given: %s", err)
}
}