test_plugin.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. from __future__ import absolute_import
  2. import responses
  3. from exam import fixture
  4. from django.contrib.auth.models import AnonymousUser
  5. from django.core.urlresolvers import reverse
  6. from django.test import RequestFactory
  7. from sentry.testutils import TestCase
  8. from sentry.utils import json
  9. from sentry_plugins.jira.plugin import JiraPlugin
  10. create_meta_response = {
  11. "expand": "projects",
  12. "projects": [
  13. {
  14. "expand": "issuetypes",
  15. "self": "https://getsentry.atlassian.net/rest/api/2/project/10000",
  16. "id": "10000",
  17. "key": "SEN",
  18. "name": "Sentry",
  19. "avatarUrls": {
  20. "48x48": "https://getsentry.atlassian.net/secure/projectavatar?avatarId=10324",
  21. "24x24": "https://getsentry.atlassian.net/secure/projectavatar?size=small&avatarId=10324",
  22. "16x16": "https://getsentry.atlassian.net/secure/projectavatar?size=xsmall&avatarId=10324",
  23. "32x32": "https://getsentry.atlassian.net/secure/projectavatar?size=medium&avatarId=10324",
  24. },
  25. "issuetypes": [
  26. {
  27. "self": "https://getsentry.atlassian.net/rest/api/2/issuetype/10002",
  28. "id": "10002",
  29. "description": "A task that needs to be done.",
  30. "iconUrl": "https://getsentry.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10318&avatarType=issuetype",
  31. "name": "Task",
  32. "subtask": False,
  33. "expand": "fields",
  34. "fields": {
  35. "summary": {
  36. "required": True,
  37. "schema": {"type": "string", "system": "summary"},
  38. "name": "Summary",
  39. "hasDefaultValue": False,
  40. "operations": ["set"],
  41. },
  42. "issuetype": {
  43. "required": True,
  44. "schema": {"type": "issuetype", "system": "issuetype"},
  45. "name": "Issue Type",
  46. "hasDefaultValue": False,
  47. "operations": [],
  48. "allowedValues": [
  49. {
  50. "self": "https://getsentry.atlassian.net/rest/api/2/issuetype/10002",
  51. "id": "10002",
  52. "description": "A task that needs to be done.",
  53. "iconUrl": "https://getsentry.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10318&avatarType=issuetype",
  54. "name": "Task",
  55. "subtask": False,
  56. "avatarId": 10318,
  57. }
  58. ],
  59. },
  60. "components": {
  61. "required": False,
  62. "schema": {
  63. "type": "array",
  64. "items": "component",
  65. "system": "components",
  66. },
  67. "name": "Component/s",
  68. "hasDefaultValue": False,
  69. "operations": ["add", "set", "remove"],
  70. "allowedValues": [],
  71. },
  72. "description": {
  73. "required": False,
  74. "schema": {"type": "string", "system": "description"},
  75. "name": "Description",
  76. "hasDefaultValue": False,
  77. "operations": ["set"],
  78. },
  79. "project": {
  80. "required": True,
  81. "schema": {"type": "project", "system": "project"},
  82. "name": "Project",
  83. "hasDefaultValue": False,
  84. "operations": ["set"],
  85. "allowedValues": [
  86. {
  87. "self": "https://getsentry.atlassian.net/rest/api/2/project/10000",
  88. "id": "10000",
  89. "key": "SEN",
  90. "name": "Sentry",
  91. "avatarUrls": {
  92. "48x48": "https://getsentry.atlassian.net/secure/projectavatar?avatarId=10324",
  93. "24x24": "https://getsentry.atlassian.net/secure/projectavatar?size=small&avatarId=10324",
  94. "16x16": "https://getsentry.atlassian.net/secure/projectavatar?size=xsmall&avatarId=10324",
  95. "32x32": "https://getsentry.atlassian.net/secure/projectavatar?size=medium&avatarId=10324",
  96. },
  97. }
  98. ],
  99. },
  100. "reporter": {
  101. "required": True,
  102. "schema": {"type": "user", "system": "reporter"},
  103. "name": "Reporter",
  104. "autoCompleteUrl": "https://getsentry.atlassian.net/rest/api/latest/user/search?username=",
  105. "hasDefaultValue": False,
  106. "operations": ["set"],
  107. },
  108. "fixVersions": {
  109. "required": False,
  110. "schema": {
  111. "type": "array",
  112. "items": "version",
  113. "system": "fixVersions",
  114. },
  115. "name": "Fix Version/s",
  116. "hasDefaultValue": False,
  117. "operations": ["set", "add", "remove"],
  118. "allowedValues": [],
  119. },
  120. "priority": {
  121. "required": False,
  122. "schema": {"type": "priority", "system": "priority"},
  123. "name": "Priority",
  124. "hasDefaultValue": True,
  125. "operations": ["set"],
  126. "allowedValues": [
  127. {
  128. "self": "https://getsentry.atlassian.net/rest/api/2/priority/1",
  129. "iconUrl": "https://getsentry.atlassian.net/images/icons/priorities/highest.svg",
  130. "name": "Highest",
  131. "id": "1",
  132. }
  133. ],
  134. },
  135. "customfield_10003": {
  136. "required": False,
  137. "schema": {
  138. "type": "array",
  139. "items": "string",
  140. "custom": "com.pyxis.greenhopper.jira:gh-sprint",
  141. "customId": 10003,
  142. },
  143. "name": "Sprint",
  144. "hasDefaultValue": False,
  145. "operations": ["set"],
  146. },
  147. "labels": {
  148. "required": False,
  149. "schema": {"type": "array", "items": "string", "system": "labels"},
  150. "name": "Labels",
  151. "autoCompleteUrl": "https://getsentry.atlassian.net/rest/api/1.0/labels/suggest?query=",
  152. "hasDefaultValue": False,
  153. "operations": ["add", "set", "remove"],
  154. },
  155. "attachment": {
  156. "required": False,
  157. "schema": {
  158. "type": "array",
  159. "items": "attachment",
  160. "system": "attachment",
  161. },
  162. "name": "Attachment",
  163. "hasDefaultValue": False,
  164. "operations": [],
  165. },
  166. "assignee": {
  167. "required": False,
  168. "schema": {"type": "user", "system": "assignee"},
  169. "name": "Assignee",
  170. "autoCompleteUrl": "https://getsentry.atlassian.net/rest/api/latest/user/assignable/search?issueKey=null&username=",
  171. "hasDefaultValue": False,
  172. "operations": ["set"],
  173. },
  174. },
  175. }
  176. ],
  177. }
  178. ],
  179. }
  180. issue_response = {
  181. "key": "SEN-19",
  182. "id": "10708",
  183. "fields": {"summary": "TypeError: 'set' object has no attribute '__getitem__'"},
  184. }
  185. class JiraPluginTest(TestCase):
  186. @fixture
  187. def plugin(self):
  188. return JiraPlugin()
  189. @fixture
  190. def request(self):
  191. return RequestFactory()
  192. def test_conf_key(self):
  193. assert self.plugin.conf_key == "jira"
  194. def test_get_issue_label(self):
  195. group = self.create_group(message="Hello world", culprit="foo.bar")
  196. assert self.plugin.get_issue_label(group, "SEN-1") == "SEN-1"
  197. def test_get_issue_url(self):
  198. self.plugin.set_option("instance_url", "https://getsentry.atlassian.net", self.project)
  199. group = self.create_group(message="Hello world", culprit="foo.bar")
  200. assert (
  201. self.plugin.get_issue_url(group, "SEN-1")
  202. == "https://getsentry.atlassian.net/browse/SEN-1"
  203. )
  204. def test_is_configured(self):
  205. assert self.plugin.is_configured(None, self.project) is False
  206. self.plugin.set_option("default_project", "SEN", self.project)
  207. assert self.plugin.is_configured(None, self.project) is True
  208. @responses.activate
  209. def test_create_issue(self):
  210. responses.add(
  211. responses.GET,
  212. "https://getsentry.atlassian.net/rest/api/2/issue/createmeta",
  213. json=create_meta_response,
  214. )
  215. responses.add(
  216. responses.POST,
  217. "https://getsentry.atlassian.net/rest/api/2/issue",
  218. json={"key": "SEN-1"},
  219. )
  220. self.plugin.set_option("instance_url", "https://getsentry.atlassian.net", self.project)
  221. group = self.create_group(message="Hello world", culprit="foo.bar")
  222. request = self.request.get("/")
  223. request.user = AnonymousUser()
  224. form_data = {
  225. "title": "Hello",
  226. "description": "Fix this.",
  227. "issuetype": "bug",
  228. "project": "SEN",
  229. }
  230. assert self.plugin.create_issue(request, group, form_data) == "SEN-1"
  231. @responses.activate
  232. def test_link_issue(self):
  233. responses.add(
  234. responses.GET,
  235. "https://getsentry.atlassian.net/rest/api/2/issue/SEN-19",
  236. json=issue_response,
  237. )
  238. self.plugin.set_option("instance_url", "https://getsentry.atlassian.net", self.project)
  239. group = self.create_group(message="Hello world", culprit="foo.bar")
  240. request = self.request.get("/")
  241. request.user = AnonymousUser()
  242. form_data = {"issue_id": "SEN-19"}
  243. assert (
  244. self.plugin.link_issue(request, group, form_data)["title"]
  245. == issue_response["fields"]["summary"]
  246. )
  247. def test_no_secrets(self):
  248. self.user = self.create_user("foo@example.com")
  249. self.org = self.create_organization(owner=self.user, name="Rowdy Tiger")
  250. self.team = self.create_team(organization=self.org, name="Mariachi Band")
  251. self.project = self.create_project(organization=self.org, teams=[self.team], name="Bengal")
  252. self.login_as(self.user)
  253. self.plugin.set_option("password", "abcdef", self.project)
  254. url = reverse(
  255. "sentry-api-0-project-plugin-details", args=[self.org.slug, self.project.slug, "jira"]
  256. )
  257. res = self.client.get(url)
  258. config = json.loads(res.content)["config"]
  259. password_config = [item for item in config if item["name"] == "password"][0]
  260. assert password_config.get("type") == "secret"
  261. assert password_config.get("value") is None
  262. assert password_config.get("hasSavedValue") is True
  263. assert password_config.get("prefix") == ""
  264. def test_get_formatted_user(self):
  265. assert self.plugin._get_formatted_user(
  266. {"displayName": "Foo Bar", "emailAddress": "foo@sentry.io", "name": "foobar"}
  267. ) == {"text": "Foo Bar - foo@sentry.io (foobar)", "id": "foobar"}
  268. # test weird addon users that don't have email addresses
  269. assert self.plugin._get_formatted_user(
  270. {
  271. "name": "robot",
  272. "avatarUrls": {
  273. "16x16": "https://avatar-cdn.atlassian.com/someid",
  274. "24x24": "https://avatar-cdn.atlassian.com/someotherid",
  275. },
  276. "self": "https://something.atlassian.net/rest/api/2/user?username=someaddon",
  277. }
  278. ) == {"id": "robot", "text": "robot (robot)"}