tests.py 9.5 KB

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