test_organization_discover_query.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. from __future__ import absolute_import
  2. from datetime import datetime, timedelta
  3. from sentry.testutils import APITestCase
  4. from django.core.urlresolvers import reverse
  5. from sentry.testutils import SnubaTestCase
  6. class OrganizationDiscoverQueryTest(APITestCase, SnubaTestCase):
  7. def setUp(self):
  8. super(OrganizationDiscoverQueryTest, self).setUp()
  9. self.now = datetime.now()
  10. one_second_ago = self.now - timedelta(seconds=1)
  11. self.login_as(user=self.user, superuser=False)
  12. self.org = self.create_organization(owner=self.user, name='foo')
  13. self.project = self.create_project(
  14. name='bar',
  15. organization=self.org,
  16. )
  17. self.other_project = self.create_project(name='other')
  18. self.group = self.create_group(project=self.project, short_id=20)
  19. self.event = self.create_event(
  20. group=self.group,
  21. platform="python",
  22. datetime=one_second_ago,
  23. tags={'environment': 'production'},
  24. data={
  25. 'message': 'message!',
  26. 'exception': {
  27. 'values': [
  28. {
  29. 'type': 'ValidationError',
  30. 'value': 'Bad request',
  31. 'mechanism': {
  32. 'type': '1',
  33. 'value': '1',
  34. },
  35. 'stacktrace': {
  36. 'frames': [
  37. {
  38. 'function': '?',
  39. 'filename': 'http://localhost:1337/error.js',
  40. 'lineno': 29,
  41. 'colno': 3,
  42. 'in_app': True
  43. },
  44. ]
  45. },
  46. }
  47. ]
  48. }
  49. },
  50. )
  51. def test(self):
  52. with self.feature('organizations:discover'):
  53. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  54. response = self.client.post(url, {
  55. 'projects': [self.project.id],
  56. 'fields': ['message', 'platform'],
  57. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  58. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  59. 'orderby': '-timestamp',
  60. 'range': None,
  61. })
  62. assert response.status_code == 200, response.content
  63. assert len(response.data['data']) == 1
  64. assert response.data['data'][0]['message'] == 'message!'
  65. assert response.data['data'][0]['platform'] == 'python'
  66. def test_relative_dates(self):
  67. with self.feature('organizations:discover'):
  68. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  69. response = self.client.post(url, {
  70. 'projects': [self.project.id],
  71. 'fields': ['message', 'platform'],
  72. 'range': '1d',
  73. 'orderby': '-timestamp',
  74. 'start': None,
  75. 'end': None,
  76. })
  77. assert response.status_code == 200, response.content
  78. assert len(response.data['data']) == 1
  79. assert response.data['data'][0]['message'] == 'message!'
  80. assert response.data['data'][0]['platform'] == 'python'
  81. def test_invalid_date_request(self):
  82. with self.feature('organizations:discover'):
  83. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  84. response = self.client.post(url, {
  85. 'projects': [self.project.id],
  86. 'fields': ['message', 'platform'],
  87. 'range': '1d',
  88. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  89. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  90. 'orderby': '-timestamp',
  91. })
  92. assert response.status_code == 400, response.content
  93. with self.feature('organizations:discover'):
  94. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  95. response = self.client.post(url, {
  96. 'projects': [self.project.id],
  97. 'fields': ['message', 'platform'],
  98. 'statsPeriodStart': '7d',
  99. 'statsPeriodEnd': '1d',
  100. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  101. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  102. 'orderby': '-timestamp',
  103. })
  104. assert response.status_code == 400, response.content
  105. def test_invalid_range_value(self):
  106. with self.feature('organizations:discover'):
  107. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  108. response = self.client.post(url, {
  109. 'projects': [self.project.id],
  110. 'fields': ['message', 'platform'],
  111. 'range': '1x',
  112. 'orderby': '-timestamp',
  113. 'start': None,
  114. 'end': None,
  115. })
  116. assert response.status_code == 400, response.content
  117. def test_invalid_aggregation_function(self):
  118. with self.feature('organizations:discover'):
  119. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  120. response = self.client.post(url, {
  121. 'projects': [self.project.id],
  122. 'fields': ['message', 'platform'],
  123. 'aggregations': [['test', 'test', 'test']],
  124. 'range': '14d',
  125. 'orderby': '-timestamp',
  126. 'start': None,
  127. 'end': None,
  128. })
  129. assert response.status_code == 400, response.content
  130. def test_boolean_condition(self):
  131. with self.feature('organizations:discover'):
  132. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  133. response = self.client.post(url, {
  134. 'projects': [self.project.id],
  135. 'fields': ['message', 'platform', 'stack.in_app'],
  136. 'conditions': [['stack.in_app', '=', True]],
  137. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  138. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  139. 'orderby': '-timestamp',
  140. 'range': None,
  141. })
  142. assert response.status_code == 200, response.content
  143. assert len(response.data['data']) == 1
  144. assert response.data['data'][0]['message'] == 'message!'
  145. assert response.data['data'][0]['platform'] == 'python'
  146. def test_array_join(self):
  147. with self.feature('organizations:discover'):
  148. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  149. response = self.client.post(url, {
  150. 'projects': [self.project.id],
  151. 'fields': ['message', 'error.type'],
  152. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  153. 'end': (datetime.now() + timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  154. 'orderby': '-timestamp',
  155. 'range': None,
  156. })
  157. assert response.status_code == 200, response.content
  158. assert len(response.data['data']) == 1
  159. assert response.data['data'][0]['error.type'] == 'ValidationError'
  160. def test_array_condition_equals(self):
  161. with self.feature('organizations:discover'):
  162. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  163. response = self.client.post(url, {
  164. 'projects': [self.project.id],
  165. 'conditions': [['error.type', '=', 'ValidationError']],
  166. 'fields': ['message'],
  167. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  168. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  169. 'orderby': '-timestamp',
  170. 'range': None,
  171. })
  172. assert response.status_code == 200, response.content
  173. assert len(response.data['data']) == 1
  174. def test_array_condition_not_equals(self):
  175. with self.feature('organizations:discover'):
  176. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  177. response = self.client.post(url, {
  178. 'projects': [self.project.id],
  179. 'conditions': [['error.type', '!=', 'ValidationError']],
  180. 'fields': ['message'],
  181. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  182. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  183. 'orderby': '-timestamp',
  184. 'range': None,
  185. })
  186. assert response.status_code == 200, response.content
  187. assert len(response.data['data']) == 0
  188. def test_select_project_name(self):
  189. with self.feature('organizations:discover'):
  190. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  191. response = self.client.post(url, {
  192. 'projects': [self.project.id],
  193. 'fields': ['project.name'],
  194. 'range': '14d',
  195. 'orderby': '-timestamp',
  196. 'start': None,
  197. 'end': None,
  198. })
  199. assert response.status_code == 200, response.content
  200. assert len(response.data['data']) == 1
  201. assert(response.data['data'][0]['project.name']) == 'bar'
  202. def test_groupby_project_name(self):
  203. with self.feature('organizations:discover'):
  204. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  205. response = self.client.post(url, {
  206. 'projects': [self.project.id],
  207. 'aggregations': [['count()', '', 'count']],
  208. 'fields': ['project.name'],
  209. 'range': '14d',
  210. 'orderby': '-count',
  211. 'start': None,
  212. 'end': None,
  213. })
  214. assert response.status_code == 200, response.content
  215. assert len(response.data['data']) == 1
  216. assert(response.data['data'][0]['project.name']) == 'bar'
  217. assert(response.data['data'][0]['count']) == 1
  218. def test_zerofilled_dates_when_rollup_relative(self):
  219. with self.feature('organizations:discover'):
  220. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  221. response = self.client.post(url, {
  222. 'projects': [self.project.id],
  223. 'aggregations': [['count()', '', 'count']],
  224. 'fields': ['project.name'],
  225. 'groupby': ['time'],
  226. 'orderby': 'time',
  227. 'range': '5d',
  228. 'rollup': 86400,
  229. 'start': None,
  230. 'end': None,
  231. })
  232. assert response.status_code == 200, response.content
  233. assert len(response.data['data']) == 6
  234. assert(response.data['data'][5]['time']) > response.data['data'][4]['time']
  235. assert(response.data['data'][5]['project.name']) == 'bar'
  236. assert(response.data['data'][5]['count']) == 1
  237. def test_zerofilled_dates_when_rollup_absolute(self):
  238. with self.feature('organizations:discover'):
  239. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  240. response = self.client.post(url, {
  241. 'projects': [self.project.id],
  242. 'aggregations': [['count()', '', 'count']],
  243. 'fields': ['project.name'],
  244. 'groupby': ['time'],
  245. 'orderby': '-time',
  246. 'start': (self.now - timedelta(seconds=300)).strftime('%Y-%m-%dT%H:%M:%S'),
  247. 'end': self.now.strftime('%Y-%m-%dT%H:%M:%S'),
  248. 'rollup': 60,
  249. 'range': None,
  250. })
  251. assert response.status_code == 200, response.content
  252. assert len(response.data['data']) == 6
  253. assert(response.data['data'][0]['time']) > response.data['data'][2]['time']
  254. assert(response.data['data'][0]['project.name']) == 'bar'
  255. assert(response.data['data'][0]['count']) == 1
  256. def test_uniq_project_name(self):
  257. with self.feature('organizations:discover'):
  258. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  259. response = self.client.post(url, {
  260. 'projects': [self.project.id],
  261. 'aggregations': [['uniq', 'project.name', 'uniq_project_name']],
  262. 'range': '14d',
  263. 'orderby': '-uniq_project_name',
  264. 'start': None,
  265. 'end': None,
  266. })
  267. assert response.status_code == 200, response.content
  268. assert len(response.data['data']) == 1
  269. assert(response.data['data'][0]['uniq_project_name']) == 1
  270. def test_meta_types(self):
  271. with self.feature('organizations:discover'):
  272. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  273. response = self.client.post(url, {
  274. 'projects': [self.project.id],
  275. 'fields': ['project.id', 'project.name'],
  276. 'aggregations': [['count()', '', 'count']],
  277. 'range': '14d',
  278. 'orderby': '-count',
  279. 'start': None,
  280. 'end': None,
  281. })
  282. assert response.status_code == 200, response.content
  283. assert response.data['meta'] == [
  284. {'name': 'project.id', 'type': 'integer'},
  285. {'name': 'project.name', 'type': 'string'},
  286. {'name': 'count', 'type': 'integer'}
  287. ]
  288. def test_no_feature_access(self):
  289. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  290. response = self.client.post(url, {
  291. 'projects': [self.project.id],
  292. 'fields': ['message', 'platform'],
  293. 'range': '14d',
  294. 'orderby': '-timestamp',
  295. 'start': None,
  296. 'end': None,
  297. })
  298. assert response.status_code == 404, response.content
  299. def test_invalid_project(self):
  300. with self.feature('organizations:discover'):
  301. url = reverse('sentry-api-0-organization-discover-query', args=[self.org.slug])
  302. response = self.client.post(url, {
  303. 'projects': [self.other_project.id],
  304. 'fields': ['message', 'platform'],
  305. 'range': '14d',
  306. 'orderby': '-timestamp',
  307. 'start': None,
  308. 'end': None,
  309. })
  310. assert response.status_code == 403, response.content
  311. def test_superuser(self):
  312. self.new_org = self.create_organization(name='foo_new')
  313. self.new_project = self.create_project(
  314. name='bar_new',
  315. organization=self.new_org,
  316. )
  317. self.login_as(user=self.user, superuser=True)
  318. with self.feature('organizations:discover'):
  319. url = reverse('sentry-api-0-organization-discover-query', args=[self.new_org.slug])
  320. response = self.client.post(url, {
  321. 'projects': [self.new_project.id],
  322. 'fields': ['message', 'platform'],
  323. 'start': (datetime.now() - timedelta(seconds=10)).strftime('%Y-%m-%dT%H:%M:%S'),
  324. 'end': (datetime.now()).strftime('%Y-%m-%dT%H:%M:%S'),
  325. 'orderby': '-timestamp',
  326. 'range': None,
  327. })
  328. assert response.status_code == 200, response.content