RecurlyClientTests.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. const sinon = require('sinon')
  2. const sinonChai = require('sinon-chai')
  3. const chai = require('chai')
  4. const chaiAsPromised = require('chai-as-promised')
  5. chai.use(sinonChai)
  6. chai.use(chaiAsPromised)
  7. const { expect } = chai
  8. const recurly = require('recurly')
  9. const modulePath = '../../../../app/src/Features/Subscription/RecurlyClient'
  10. const SandboxedModule = require('sandboxed-module')
  11. describe('RecurlyClient', function () {
  12. beforeEach(function () {
  13. this.settings = {
  14. apis: {
  15. recurly: {
  16. apiKey: 'nonsense',
  17. privateKey: 'private_nonsense',
  18. },
  19. },
  20. }
  21. this.user = { _id: '123456', email: 'joe@example.com', first_name: 'Joe' }
  22. this.subscription = {
  23. id: 'subscription-123',
  24. uuid: 'subscription-uuid-123',
  25. }
  26. this.subscriptionChange = { id: 'subscription-change-123' }
  27. this.recurlyAccount = new recurly.Account()
  28. Object.assign(this.recurlyAccount, { code: this.user._id })
  29. this.recurlySubscription = new recurly.Subscription()
  30. Object.assign(this.recurlySubscription, this.subscription)
  31. this.recurlySubscriptionChange = new recurly.SubscriptionChange()
  32. Object.assign(this.recurlySubscriptionChange, this.subscriptionChange)
  33. this.UserGetter = {
  34. promises: {
  35. getUser: sinon.stub().callsFake(userId => {
  36. if (userId === this.user._id) {
  37. return this.user
  38. }
  39. }),
  40. },
  41. }
  42. let client
  43. this.client = client = {
  44. getAccount: sinon.stub(),
  45. }
  46. this.recurly = {
  47. errors: recurly.errors,
  48. Client: function () {
  49. return client
  50. },
  51. }
  52. return (this.RecurlyClient = SandboxedModule.require(modulePath, {
  53. globals: {
  54. console: console,
  55. },
  56. requires: {
  57. '@overleaf/settings': this.settings,
  58. recurly: this.recurly,
  59. '@overleaf/logger': {
  60. err: sinon.stub(),
  61. error: sinon.stub(),
  62. warn: sinon.stub(),
  63. log: sinon.stub(),
  64. debug: sinon.stub(),
  65. },
  66. '../User/UserGetter': this.UserGetter,
  67. },
  68. }))
  69. })
  70. describe('initalizing recurly client with undefined API key parameter', function () {
  71. it('should create a client without error', function () {
  72. let testClient
  73. expect(() => {
  74. testClient = new recurly.Client(undefined)
  75. }).to.not.throw()
  76. expect(testClient).to.be.instanceOf(recurly.Client)
  77. })
  78. })
  79. describe('getAccountForUserId', function () {
  80. it('should return an Account if one exists', async function () {
  81. this.client.getAccount = sinon.stub().resolves(this.recurlyAccount)
  82. await expect(
  83. this.RecurlyClient.promises.getAccountForUserId(this.user._id)
  84. )
  85. .to.eventually.be.an.instanceOf(recurly.Account)
  86. .that.has.property('code', this.user._id)
  87. })
  88. it('should return nothing if no account found', async function () {
  89. this.client.getAccount = sinon
  90. .stub()
  91. .throws(new recurly.errors.NotFoundError())
  92. expect(
  93. this.RecurlyClient.promises.getAccountForUserId('nonsense')
  94. ).to.eventually.equal(undefined)
  95. })
  96. it('should re-throw caught errors', async function () {
  97. this.client.getAccount = sinon.stub().throws()
  98. await expect(
  99. this.RecurlyClient.promises.getAccountForUserId(this.user._id)
  100. ).to.eventually.be.rejectedWith(Error)
  101. })
  102. })
  103. describe('createAccountForUserId', function () {
  104. it('should return the Account as created by recurly', async function () {
  105. this.client.createAccount = sinon.stub().resolves(this.recurlyAccount)
  106. await expect(
  107. this.RecurlyClient.promises.createAccountForUserId(this.user._id)
  108. )
  109. .to.eventually.be.an.instanceOf(recurly.Account)
  110. .that.has.property('code', this.user._id)
  111. })
  112. it('should throw any API errors', async function () {
  113. this.client.createAccount = sinon.stub().throws()
  114. await expect(
  115. this.RecurlyClient.promises.createAccountForUserId(this.user._id)
  116. ).to.eventually.be.rejectedWith(Error)
  117. })
  118. })
  119. describe('getSubscription', function () {
  120. it('should return the subscription found by recurly', async function () {
  121. this.client.getSubscription = sinon
  122. .stub()
  123. .resolves(this.recurlySubscription)
  124. await expect(
  125. this.RecurlyClient.promises.getSubscription(this.subscription.id)
  126. )
  127. .to.eventually.be.an.instanceOf(recurly.Subscription)
  128. .that.has.property('id', this.subscription.id)
  129. })
  130. it('should throw any API errors', async function () {
  131. this.client.getSubscription = sinon.stub().throws()
  132. await expect(
  133. this.RecurlyClient.promises.getSubscription(this.user._id)
  134. ).to.eventually.be.rejectedWith(Error)
  135. })
  136. })
  137. describe('changeSubscription', function () {
  138. beforeEach(function () {
  139. this.client.createSubscriptionChange = sinon
  140. .stub()
  141. .resolves(this.recurlySubscriptionChange)
  142. })
  143. it('should attempt to create a subscription change', async function () {
  144. this.RecurlyClient.promises.changeSubscription(this.subscription.id, {})
  145. expect(this.client.createSubscriptionChange).to.be.calledWith(
  146. this.subscription.id
  147. )
  148. })
  149. it('should return the subscription change event', async function () {
  150. await expect(
  151. this.RecurlyClient.promises.changeSubscription(
  152. this.subscriptionChange.id,
  153. {}
  154. )
  155. )
  156. .to.eventually.be.an.instanceOf(recurly.SubscriptionChange)
  157. .that.has.property('id', this.subscriptionChange.id)
  158. })
  159. it('should throw any API errors', async function () {
  160. this.client.createSubscriptionChange = sinon.stub().throws()
  161. await expect(
  162. this.RecurlyClient.promises.changeSubscription(this.subscription.id, {})
  163. ).to.eventually.be.rejectedWith(Error)
  164. })
  165. describe('changeSubscriptionByUuid', function () {
  166. it('should attempt to create a subscription change', async function () {
  167. this.RecurlyClient.promises.changeSubscriptionByUuid(
  168. this.subscription.uuid,
  169. {}
  170. )
  171. expect(this.client.createSubscriptionChange).to.be.calledWith(
  172. 'uuid-' + this.subscription.uuid
  173. )
  174. })
  175. it('should return the subscription change event', async function () {
  176. await expect(
  177. this.RecurlyClient.promises.changeSubscriptionByUuid(
  178. this.subscriptionChange.id,
  179. {}
  180. )
  181. )
  182. .to.eventually.be.an.instanceOf(recurly.SubscriptionChange)
  183. .that.has.property('id', this.subscriptionChange.id)
  184. })
  185. it('should throw any API errors', async function () {
  186. this.client.createSubscriptionChange = sinon.stub().throws()
  187. await expect(
  188. this.RecurlyClient.promises.changeSubscriptionByUuid(
  189. this.subscription.id,
  190. {}
  191. )
  192. ).to.eventually.be.rejectedWith(Error)
  193. })
  194. })
  195. })
  196. describe('removeSubscriptionChange', function () {
  197. beforeEach(function () {
  198. this.client.removeSubscriptionChange = sinon.stub().resolves()
  199. })
  200. it('should attempt to remove a pending subscription change', async function () {
  201. this.RecurlyClient.promises.removeSubscriptionChange(
  202. this.subscription.id,
  203. {}
  204. )
  205. expect(this.client.removeSubscriptionChange).to.be.calledWith(
  206. this.subscription.id
  207. )
  208. })
  209. it('should throw any API errors', async function () {
  210. this.client.removeSubscriptionChange = sinon.stub().throws()
  211. await expect(
  212. this.RecurlyClient.promises.removeSubscriptionChange(
  213. this.subscription.id,
  214. {}
  215. )
  216. ).to.eventually.be.rejectedWith(Error)
  217. })
  218. describe('removeSubscriptionChangeByUuid', function () {
  219. it('should attempt to remove a pending subscription change', async function () {
  220. this.RecurlyClient.promises.removeSubscriptionChangeByUuid(
  221. this.subscription.uuid,
  222. {}
  223. )
  224. expect(this.client.removeSubscriptionChange).to.be.calledWith(
  225. 'uuid-' + this.subscription.uuid
  226. )
  227. })
  228. it('should throw any API errors', async function () {
  229. this.client.removeSubscriptionChange = sinon.stub().throws()
  230. await expect(
  231. this.RecurlyClient.promises.removeSubscriptionChangeByUuid(
  232. this.subscription.id,
  233. {}
  234. )
  235. ).to.eventually.be.rejectedWith(Error)
  236. })
  237. })
  238. })
  239. describe('reactivateSubscriptionByUuid', function () {
  240. it('should attempt to reactivate the subscription', async function () {
  241. this.client.reactivateSubscription = sinon
  242. .stub()
  243. .resolves(this.recurlySubscription)
  244. await expect(
  245. this.RecurlyClient.promises.reactivateSubscriptionByUuid(
  246. this.subscription.uuid
  247. )
  248. ).to.eventually.be.an.instanceOf(recurly.Subscription)
  249. expect(this.client.reactivateSubscription).to.be.calledWith(
  250. 'uuid-' + this.subscription.uuid
  251. )
  252. })
  253. })
  254. describe('cancelSubscriptionByUuid', function () {
  255. it('should attempt to cancel the subscription', async function () {
  256. this.client.cancelSubscription = sinon
  257. .stub()
  258. .resolves(this.recurlySubscription)
  259. await expect(
  260. this.RecurlyClient.promises.cancelSubscriptionByUuid(
  261. this.subscription.uuid
  262. )
  263. ).to.eventually.be.an.instanceOf(recurly.Subscription)
  264. expect(this.client.cancelSubscription).to.be.calledWith(
  265. 'uuid-' + this.subscription.uuid
  266. )
  267. })
  268. })
  269. })