tests.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. from unittest import skipIf
  2. from unittest.mock import patch
  3. from django.conf import settings
  4. from django.shortcuts import reverse
  5. from django.utils import timezone
  6. from djstripe.enums import BillingScheme
  7. from freezegun import freeze_time
  8. from model_bakery import baker
  9. from rest_framework.test import APITestCase
  10. from glitchtip.test_utils import generators # pylint: disable=unused-import
  11. class SubscriptionAPITestCase(APITestCase):
  12. def setUp(self):
  13. self.user = baker.make("users.user")
  14. self.organization = baker.make("organizations_ext.Organization")
  15. self.organization.add_user(self.user)
  16. self.client.force_login(self.user)
  17. self.url = reverse("subscription-list")
  18. def test_list(self):
  19. customer = baker.make("djstripe.Customer", subscriber=self.organization)
  20. subscription = baker.make(
  21. "djstripe.Subscription", customer=customer, livemode=False
  22. )
  23. subscription2 = baker.make("djstripe.Subscription", livemode=False)
  24. subscription3 = baker.make(
  25. "djstripe.Subscription", customer=customer, livemode=True
  26. )
  27. res = self.client.get(self.url)
  28. self.assertContains(res, subscription.id)
  29. self.assertNotContains(res, subscription2.id)
  30. self.assertNotContains(res, subscription3.id)
  31. def test_detail(self):
  32. customer = baker.make("djstripe.Customer", subscriber=self.organization)
  33. subscription = baker.make(
  34. "djstripe.Subscription",
  35. status="active",
  36. customer=customer,
  37. livemode=False,
  38. created=timezone.make_aware(timezone.datetime(2020, 1, 2)),
  39. )
  40. # Should get most recent
  41. baker.make(
  42. "djstripe.Subscription",
  43. status="active",
  44. customer=customer,
  45. livemode=False,
  46. created=timezone.make_aware(timezone.datetime(2020, 1, 1)),
  47. )
  48. # should not get canceled subscriptions
  49. baker.make(
  50. "djstripe.Subscription",
  51. status="canceled",
  52. customer=customer,
  53. livemode=False,
  54. created=timezone.make_aware(timezone.datetime(2020, 1, 3)),
  55. )
  56. baker.make("djstripe.Subscription")
  57. url = reverse("subscription-detail", args=[self.organization.slug])
  58. res = self.client.get(url)
  59. self.assertContains(res, subscription.id)
  60. def test_events_count(self):
  61. """
  62. Event count should be accurate and work when there are multiple subscriptions for a given customer
  63. """
  64. customer = baker.make("djstripe.Customer", subscriber=self.organization)
  65. baker.make(
  66. "djstripe.Subscription",
  67. customer=customer,
  68. livemode=False,
  69. current_period_start=timezone.make_aware(timezone.datetime(2020, 1, 2)),
  70. current_period_end=timezone.make_aware(timezone.datetime(2020, 2, 2)),
  71. )
  72. baker.make(
  73. "djstripe.Subscription",
  74. customer=customer,
  75. livemode=False,
  76. status="Cancelled",
  77. current_period_start=timezone.make_aware(timezone.datetime(2019, 1, 2)),
  78. current_period_end=timezone.make_aware(timezone.datetime(2019, 2, 2)),
  79. )
  80. url = (
  81. reverse("subscription-detail", args=[self.organization.slug])
  82. + "events_count/"
  83. )
  84. with freeze_time(timezone.datetime(2020, 3, 1)):
  85. baker.make("events.Event", issue__project__organization=self.organization)
  86. with freeze_time(timezone.datetime(2020, 1, 5)):
  87. baker.make("events.Event")
  88. baker.make("events.Event", issue__project__organization=self.organization)
  89. baker.make(
  90. "performance.TransactionEvent",
  91. group__project__organization=self.organization,
  92. )
  93. baker.make(
  94. "releases.ReleaseFile",
  95. file__blob__size=1000000,
  96. release__organization=self.organization,
  97. _quantity=2,
  98. )
  99. res = self.client.get(url)
  100. self.assertEqual(
  101. res.data,
  102. {
  103. "eventCount": 1,
  104. "fileSizeMB": 2,
  105. "transactionEventCount": 1,
  106. "uptimeCheckEventCount": 0,
  107. },
  108. )
  109. def test_events_count_without_customer(self):
  110. """
  111. Due to async nature of Stripe integration, a customer may not exist
  112. """
  113. baker.make("djstripe.Subscription", livemode=False)
  114. url = (
  115. reverse("subscription-detail", args=[self.organization.slug])
  116. + "events_count/"
  117. )
  118. res = self.client.get(url)
  119. self.assertEqual(sum(res.data.values()), 0)
  120. @skipIf(
  121. settings.STRIPE_TEST_PUBLIC_KEY == "fake", "requires real Stripe test API key"
  122. )
  123. def test_create_free(self):
  124. """
  125. Users should not be able to create a free subscription if they have another non-canceled subscription
  126. """
  127. price = baker.make(
  128. "djstripe.Price",
  129. unit_amount=0,
  130. id="price_1KO6e1J4NuO0bv3IEXhpWpzt",
  131. billing_scheme=BillingScheme.per_unit,
  132. )
  133. baker.make("djstripe.Product", id="prod_L4F8CtH20Oad6S", default_price=price)
  134. data = {"price": price.id, "organization": self.organization.id}
  135. res = self.client.post(self.url, data)
  136. self.assertEqual(res.data["price"], price.id)
  137. # Second attempt should fail
  138. res = self.client.post(self.url, data)
  139. self.assertEqual(res.status_code, 409)
  140. def test_create_invalid_org(self):
  141. """Only owners may create subscriptions"""
  142. user = baker.make("users.user") # Non owner member
  143. plan = baker.make("djstripe.Plan", amount=0)
  144. self.organization.add_user(user)
  145. self.client.force_login(user)
  146. data = {"plan": plan.id, "organization": self.organization.id}
  147. res = self.client.post(self.url, data)
  148. self.assertEqual(res.status_code, 400)
  149. class ProductAPITestCase(APITestCase):
  150. def test_product_list(self):
  151. price = baker.make(
  152. "djstripe.Price",
  153. unit_amount=0,
  154. billing_scheme=BillingScheme.per_unit,
  155. active=True,
  156. product__active=True,
  157. product__livemode=False,
  158. product__metadata={"events": 10, "is_public": "true"},
  159. )
  160. inactive_price = baker.make(
  161. "djstripe.Price",
  162. unit_amount=0,
  163. billing_scheme=BillingScheme.per_unit,
  164. active=False,
  165. product__active=False,
  166. product__livemode=False,
  167. product__metadata={"events": 10, "is_public": "true"},
  168. )
  169. hidden_price = baker.make(
  170. "djstripe.Price",
  171. unit_amount=0,
  172. billing_scheme=BillingScheme.per_unit,
  173. active=True,
  174. product__active=True,
  175. product__livemode=False,
  176. product__metadata={"events": 10, "is_public": "false"},
  177. )
  178. user = baker.make("users.user")
  179. self.client.force_login(user)
  180. res = self.client.get(reverse("product-list"))
  181. self.assertContains(res, price.id)
  182. self.assertNotContains(res, inactive_price.id)
  183. self.assertNotContains(res, hidden_price.id)
  184. # Price ID must be from a real price actually set up on Stripe Test account
  185. class StripeAPITestCase(APITestCase):
  186. @skipIf(
  187. settings.STRIPE_TEST_PUBLIC_KEY == "fake", "requires real Stripe test API key"
  188. )
  189. def test_create_checkout(self):
  190. url = reverse("create-stripe-subscription-checkout")
  191. price = baker.make(
  192. "djstripe.Price",
  193. id="price_1MZhMWJ4NuO0bv3IGMoDoFFI",
  194. )
  195. user = baker.make("users.user")
  196. organization = baker.make("organizations_ext.Organization")
  197. organization.add_user(user)
  198. self.client.force_login(user)
  199. data = {"price": price.id, "organization": organization.id}
  200. res = self.client.post(url, data)
  201. self.assertEqual(res.status_code, 200)
  202. @skipIf(
  203. settings.STRIPE_TEST_PUBLIC_KEY == "fake", "requires real Stripe test API key"
  204. )
  205. def test_manage_billing(self):
  206. url = reverse("create-billing-portal")
  207. user = baker.make("users.user")
  208. organization = baker.make("organizations_ext.Organization")
  209. organization.add_user(user)
  210. self.client.force_login(user)
  211. data = {"organization": organization.id}
  212. res = self.client.post(url, data)
  213. self.assertEqual(res.status_code, 200)
  214. class SubscriptionIntegrationAPITestCase(APITestCase):
  215. def setUp(self):
  216. self.user = baker.make("users.user")
  217. self.organization = baker.make("organizations_ext.Organization")
  218. self.organization.add_user(self.user)
  219. # Make these in this manner to avoid syncing data to stripe actual
  220. self.price = baker.make(
  221. "djstripe.Price",
  222. active=True,
  223. unit_amount=0,
  224. billing_scheme=BillingScheme.per_unit,
  225. )
  226. self.customer = baker.make(
  227. "djstripe.Customer", subscriber=self.organization, livemode=False
  228. )
  229. self.client.force_login(self.user)
  230. self.list_url = reverse("subscription-list")
  231. self.detail_url = reverse("subscription-detail", args=[self.organization.slug])