| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570 |
- /* eslint-disable
- camelcase,
- handle-callback-err,
- max-len,
- no-return-assign,
- no-unused-vars,
- standard/no-callback-literal,
- */
- // TODO: This file was created by bulk-decaffeinate.
- // Fix any style issues and re-enable lint.
- /*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
- const should = require('chai').should()
- const { expect } = require('chai')
- const sinon = require('sinon')
- const crypto = require('crypto')
- const querystring = require('querystring')
- const modulePath = '../../../../app/src/Features/Subscription/RecurlyWrapper'
- const SandboxedModule = require('sandboxed-module')
- const tk = require('timekeeper')
- const fixtures = {
- 'subscriptions/44f83d7cba354d5b84812419f923ea96':
- '<?xml version="1.0" encoding="UTF-8"?>' +
- '<subscription href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96">' +
- ' <account href="https://api.recurly.com/v2/accounts/104"/>' +
- ' <plan href="https://api.recurly.com/v2/plans/gold">' +
- ' <plan_code>gold</plan_code>' +
- ' <name>Gold plan</name>' +
- ' </plan>' +
- ' <uuid>44f83d7cba354d5b84812419f923ea96</uuid>' +
- ' <state>active</state>' +
- ' <unit_amount_in_cents type="integer">800</unit_amount_in_cents>' +
- ' <currency>EUR</currency>' +
- ' <quantity type="integer">1</quantity>' +
- ' <activated_at type="datetime">2011-05-27T07:00:00Z</activated_at>' +
- ' <canceled_at nil="nil"></canceled_at>' +
- ' <expires_at nil="nil"></expires_at>' +
- ' <current_period_started_at type="datetime">2011-06-27T07:00:00Z</current_period_started_at>' +
- ' <current_period_ends_at type="datetime">2011-07-27T07:00:00Z</current_period_ends_at>' +
- ' <trial_started_at nil="nil"></trial_started_at>' +
- ' <trial_ends_at nil="nil"></trial_ends_at>' +
- ' <subscription_add_ons type="array">' +
- ' <subscription_add_on>' +
- ' <add_on_code>ipaddresses</add_on_code>' +
- ' <quantity>10</quantity>' +
- ' <unit_amount_in_cents>150</unit_amount_in_cents>' +
- ' </subscription_add_on>' +
- ' </subscription_add_ons>' +
- ' <a name="cancel" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/cancel" method="put"/>' +
- ' <a name="terminate" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/terminate" method="put"/>' +
- ' <a name="postpone" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/postpone" method="put"/>' +
- '</subscription>',
- 'recurly_js/result/70db44b10f5f4b238669480c9903f6f5':
- '<?xml version="1.0" encoding="UTF-8"?>' +
- '<subscription href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96">' +
- ' <account href="https://api.recurly.com/v2/accounts/104"/>' +
- ' <plan href="https://api.recurly.com/v2/plans/gold">' +
- ' <plan_code>gold</plan_code>' +
- ' <name>Gold plan</name>' +
- ' </plan>' +
- ' <uuid>44f83d7cba354d5b84812419f923ea96</uuid>' +
- ' <state>active</state>' +
- ' <unit_amount_in_cents type="integer">800</unit_amount_in_cents>' +
- ' <currency>EUR</currency>' +
- ' <quantity type="integer">1</quantity>' +
- ' <activated_at type="datetime">2011-05-27T07:00:00Z</activated_at>' +
- ' <canceled_at nil="nil"></canceled_at>' +
- ' <expires_at nil="nil"></expires_at>' +
- ' <current_period_started_at type="datetime">2011-06-27T07:00:00Z</current_period_started_at>' +
- ' <current_period_ends_at type="datetime">2011-07-27T07:00:00Z</current_period_ends_at>' +
- ' <trial_started_at nil="nil"></trial_started_at>' +
- ' <trial_ends_at nil="nil"></trial_ends_at>' +
- ' <subscription_add_ons type="array">' +
- ' <subscription_add_on>' +
- ' <add_on_code>ipaddresses</add_on_code>' +
- ' <quantity>10</quantity>' +
- ' <unit_amount_in_cents>150</unit_amount_in_cents>' +
- ' </subscription_add_on>' +
- ' </subscription_add_ons>' +
- ' <a name="cancel" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/cancel" method="put"/>' +
- ' <a name="terminate" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/terminate" method="put"/>' +
- ' <a name="postpone" href="https://api.recurly.com/v2/subscriptions/44f83d7cba354d5b84812419f923ea96/postpone" method="put"/>' +
- '</subscription>',
- 'accounts/104':
- '<?xml version="1.0" encoding="UTF-8"?>' +
- '<account href="https://api.recurly.com/v2/accounts/104">' +
- ' <adjustments href="https://api.recurly.com/v2/accounts/1/adjustments"/>' +
- ' <billing_info href="https://api.recurly.com/v2/accounts/1/billing_info"/>' +
- ' <invoices href="https://api.recurly.com/v2/accounts/1/invoices"/>' +
- ' <redemption href="https://api.recurly.com/v2/accounts/1/redemption"/>' +
- ' <subscriptions href="https://api.recurly.com/v2/accounts/1/subscriptions"/>' +
- ' <transactions href="https://api.recurly.com/v2/accounts/1/transactions"/>' +
- ' <account_code>104</account_code>' +
- ' <state>active</state>' +
- ' <username nil="nil"></username>' +
- ' <email>verena@example.com</email>' +
- ' <first_name>Verena</first_name>' +
- ' <last_name>Example</last_name>' +
- ' <accept_language nil="nil"></accept_language>' +
- ' <hosted_login_token>a92468579e9c4231a6c0031c4716c01d</hosted_login_token>' +
- ' <created_at type="datetime">2011-10-25T12:00:00</created_at>' +
- '</account>'
- }
- const mockApiRequest = function(options, callback) {
- if (fixtures[options.url]) {
- return callback(null, { statusCode: 200 }, fixtures[options.url])
- } else {
- return callback(new Error('Not found'), { statusCode: 404 })
- }
- }
- describe('RecurlyWrapper', function() {
- before(function() {
- let RecurlyWrapper
- this.settings = {
- plans: [
- {
- planCode: 'collaborator',
- name: 'Collaborator',
- features: {
- collaborators: -1,
- versioning: true
- }
- }
- ],
- defaultPlanCode: {
- collaborators: 0,
- versioning: false
- },
- apis: {
- recurly: {
- apiKey: 'nonsense',
- privateKey: 'private_nonsense'
- }
- }
- }
- tk.freeze(Date.now()) // freeze the time for these tests
- return (this.RecurlyWrapper = RecurlyWrapper = SandboxedModule.require(
- modulePath,
- {
- globals: {
- console: console
- },
- requires: {
- 'settings-sharelatex': this.settings,
- 'logger-sharelatex': {
- err: sinon.stub(),
- error: sinon.stub(),
- warn: sinon.stub(),
- log: sinon.stub()
- },
- request: sinon.stub(),
- xml2js: require('xml2js')
- }
- }
- ))
- })
- after(function() {
- return tk.reset()
- })
- describe('getSubscription', function() {
- describe('with proper subscription id', function() {
- before(function() {
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- mockApiRequest
- )
- return this.RecurlyWrapper.getSubscription(
- '44f83d7cba354d5b84812419f923ea96',
- (error, recurlySubscription) => {
- return (this.recurlySubscription = recurlySubscription)
- }
- )
- })
- after(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('should look up the subscription at the normal API end point', function() {
- return this.apiRequest.args[0][0].url.should.equal(
- 'subscriptions/44f83d7cba354d5b84812419f923ea96'
- )
- })
- it('should return the subscription', function() {
- return this.recurlySubscription.uuid.should.equal(
- '44f83d7cba354d5b84812419f923ea96'
- )
- })
- })
- describe('with ReculyJS token', function() {
- before(function() {
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- mockApiRequest
- )
- return this.RecurlyWrapper.getSubscription(
- '70db44b10f5f4b238669480c9903f6f5',
- { recurlyJsResult: true },
- (error, recurlySubscription) => {
- return (this.recurlySubscription = recurlySubscription)
- }
- )
- })
- after(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('should return the subscription', function() {
- return this.recurlySubscription.uuid.should.equal(
- '44f83d7cba354d5b84812419f923ea96'
- )
- })
- it('should look up the subscription at the RecurlyJS API end point', function() {
- return this.apiRequest.args[0][0].url.should.equal(
- 'recurly_js/result/70db44b10f5f4b238669480c9903f6f5'
- )
- })
- })
- describe('with includeAccount', function() {
- beforeEach(function() {
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- mockApiRequest
- )
- return this.RecurlyWrapper.getSubscription(
- '44f83d7cba354d5b84812419f923ea96',
- { includeAccount: true },
- (error, recurlySubscription) => {
- return (this.recurlySubscription = recurlySubscription)
- }
- )
- })
- afterEach(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('should request the account from the API', function() {
- return this.apiRequest.args[1][0].url.should.equal('accounts/104')
- })
- it('should populate the account attribute', function() {
- return this.recurlySubscription.account.account_code.should.equal('104')
- })
- })
- })
- describe('updateSubscription', function() {
- beforeEach(function(done) {
- this.recurlySubscriptionId = 'subscription-id-123'
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- (options, callback) => {
- this.requestOptions = options
- return callback(
- null,
- {},
- fixtures['subscriptions/44f83d7cba354d5b84812419f923ea96']
- )
- }
- )
- return this.RecurlyWrapper.updateSubscription(
- this.recurlySubscriptionId,
- { plan_code: 'silver', timeframe: 'now' },
- (error, recurlySubscription) => {
- this.recurlySubscription = recurlySubscription
- return done()
- }
- )
- })
- afterEach(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('sends correct XML', function() {
- this.apiRequest.called.should.equal(true)
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
- <subscription>
- <plan_code>silver</plan_code>
- <timeframe>now</timeframe>
- </subscription>\
- `)
- this.requestOptions.url.should.equal(
- `subscriptions/${this.recurlySubscriptionId}`
- )
- return this.requestOptions.method.should.equal('put')
- })
- it('should return the updated subscription', function() {
- should.exist(this.recurlySubscription)
- return this.recurlySubscription.plan.plan_code.should.equal('gold')
- })
- })
- describe('cancelSubscription', function() {
- beforeEach(function(done) {
- this.recurlySubscriptionId = 'subscription-id-123'
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- (options, callback) => {
- options.url.should.equal(
- `subscriptions/${this.recurlySubscriptionId}/cancel`
- )
- options.method.should.equal('put')
- return callback()
- }
- )
- return this.RecurlyWrapper.cancelSubscription(
- this.recurlySubscriptionId,
- done
- )
- })
- afterEach(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('should send a cancel request to the API', function() {
- return this.apiRequest.called.should.equal(true)
- })
- describe('when the subscription is already cancelled', function() {
- beforeEach(function() {
- this.RecurlyWrapper.apiRequest.restore()
- this.recurlySubscriptionId = 'subscription-id-123'
- return (this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- (options, callback) => {
- return callback(
- new Error('woops'),
- {},
- "<error><description>A canceled subscription can't transition to canceled</description></error>"
- )
- }
- ))
- })
- it('should not produce an error', function(done) {
- return this.RecurlyWrapper.cancelSubscription(
- this.recurlySubscriptionId,
- err => {
- expect(err).to.equal(null)
- return done()
- }
- )
- })
- })
- })
- describe('reactivateSubscription', function() {
- beforeEach(function(done) {
- this.recurlySubscriptionId = 'subscription-id-123'
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- (options, callback) => {
- options.url.should.equal(
- `subscriptions/${this.recurlySubscriptionId}/reactivate`
- )
- options.method.should.equal('put')
- return callback()
- }
- )
- return this.RecurlyWrapper.reactivateSubscription(
- this.recurlySubscriptionId,
- done
- )
- })
- afterEach(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('should send a cancel request to the API', function() {
- return this.apiRequest.called.should.equal(true)
- })
- })
- describe('redeemCoupon', function() {
- beforeEach(function(done) {
- this.recurlyAccountId = 'account-id-123'
- this.coupon_code = '312321312'
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- (options, callback) => {
- options.url.should.equal(`coupons/${this.coupon_code}/redeem`)
- options.method.should.equal('post')
- return callback()
- }
- )
- return this.RecurlyWrapper.redeemCoupon(
- this.recurlyAccountId,
- this.coupon_code,
- done
- )
- })
- afterEach(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('sends correct XML', function() {
- this.apiRequest.called.should.equal(true)
- const { body } = this.apiRequest.lastCall.args[0]
- return expect(body).to.equal(`\
- <redemption>
- <account_code>account-id-123</account_code>
- <currency>USD</currency>
- </redemption>\
- `)
- })
- })
- describe('createFixedAmmountCoupon', function() {
- beforeEach(function(done) {
- this.couponCode = 'a-coupon-code'
- this.couponName = 'a-coupon-name'
- this.currencyCode = 'EUR'
- this.discount = 1337
- this.planCode = 'a-plan-code'
- this.apiRequest = sinon.stub(
- this.RecurlyWrapper,
- 'apiRequest',
- (options, callback) => {
- return callback()
- }
- )
- return this.RecurlyWrapper.createFixedAmmountCoupon(
- this.couponCode,
- this.couponName,
- this.currencyCode,
- this.discount,
- this.planCode,
- done
- )
- })
- afterEach(function() {
- return this.RecurlyWrapper.apiRequest.restore()
- })
- it('sends correct XML', function() {
- this.apiRequest.called.should.equal(true)
- const { body } = this.apiRequest.lastCall.args[0]
- return expect(body).to.equal(`\
- <coupon>
- <coupon_code>a-coupon-code</coupon_code>
- <name>a-coupon-name</name>
- <discount_type>dollars</discount_type>
- <discount_in_cents>
- <EUR>1337</EUR>
- </discount_in_cents>
- <plan_codes>
- <plan_code>a-plan-code</plan_code>
- </plan_codes>
- <applies_to_all_plans>false</applies_to_all_plans>
- </coupon>\
- `)
- })
- })
- describe('createSubscription', function() {
- beforeEach(function() {
- this.user = {
- _id: 'some_id',
- email: 'user@example.com'
- }
- this.subscriptionDetails = {
- currencyCode: 'EUR',
- plan_code: 'some_plan_code',
- coupon_code: '',
- isPaypal: true,
- address: {
- address1: 'addr_one',
- address2: 'addr_two',
- country: 'some_country',
- state: 'some_state',
- zip: 'some_zip'
- }
- }
- this.subscription = {}
- this.recurly_token_id = 'a-token-id'
- return (this.call = callback => {
- return this.RecurlyWrapper.createSubscription(
- this.user,
- this.subscriptionDetails,
- this.recurly_token_id,
- callback
- )
- })
- })
- describe('when paypal', function() {
- beforeEach(function() {
- this.subscriptionDetails.isPaypal = true
- this._createPaypalSubscription = sinon.stub(
- this.RecurlyWrapper,
- '_createPaypalSubscription'
- )
- return this._createPaypalSubscription.callsArgWith(
- 3,
- null,
- this.subscription
- )
- })
- afterEach(function() {
- return this._createPaypalSubscription.restore()
- })
- it('should not produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.equal(null)
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should produce a subscription object', function(done) {
- return this.call((err, sub) => {
- expect(sub).to.deep.equal(this.subscription)
- return done()
- })
- })
- it('should call _createPaypalSubscription', function(done) {
- return this.call((err, sub) => {
- this._createPaypalSubscription.callCount.should.equal(1)
- return done()
- })
- })
- describe('when _createPaypalSubscription produces an error', function() {
- beforeEach(function() {
- return this._createPaypalSubscription.callsArgWith(
- 3,
- new Error('woops')
- )
- })
- it('should produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- describe('when not paypal', function() {
- beforeEach(function() {
- this.subscriptionDetails.isPaypal = false
- this._createCreditCardSubscription = sinon.stub(
- this.RecurlyWrapper,
- '_createCreditCardSubscription'
- )
- return this._createCreditCardSubscription.callsArgWith(
- 3,
- null,
- this.subscription
- )
- })
- afterEach(function() {
- return this._createCreditCardSubscription.restore()
- })
- it('should not produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.equal(null)
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should produce a subscription object', function(done) {
- return this.call((err, sub) => {
- expect(sub).to.deep.equal(this.subscription)
- return done()
- })
- })
- it('should call _createCreditCardSubscription', function(done) {
- return this.call((err, sub) => {
- this._createCreditCardSubscription.callCount.should.equal(1)
- return done()
- })
- })
- describe('when _createCreditCardSubscription produces an error', function() {
- beforeEach(function() {
- return this._createCreditCardSubscription.callsArgWith(
- 3,
- new Error('woops')
- )
- })
- it('should produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- })
- describe('_createCreditCardSubscription', function() {
- beforeEach(function() {
- this.user = {
- _id: 'some_id',
- email: 'user@example.com',
- first_name: 'Foo',
- last_name: 'Johnson'
- }
- this.subscriptionDetails = {
- currencyCode: 'EUR',
- plan_code: 'some_plan_code',
- coupon_code: '',
- isPaypal: true,
- first_name: 'Prairie',
- address: {
- address1: 'addr_one',
- address2: 'addr_two',
- country: 'some_country',
- state: 'some_state',
- zip: 'some_zip'
- }
- }
- this.subscription = {}
- this.recurly_token_id = 'a-token-id'
- this.apiRequest = sinon.stub(this.RecurlyWrapper, 'apiRequest')
- this.response = { statusCode: 200 }
- this.body = '<xml>is_bad</xml>'
- this.apiRequest.callsArgWith(1, null, this.response, this.body)
- this._parseSubscriptionXml = sinon.stub(
- this.RecurlyWrapper,
- '_parseSubscriptionXml'
- )
- this._parseSubscriptionXml.callsArgWith(1, null, this.subscription)
- return (this.call = callback => {
- return this.RecurlyWrapper._createCreditCardSubscription(
- this.user,
- this.subscriptionDetails,
- this.recurly_token_id,
- callback
- )
- })
- })
- afterEach(function() {
- this.apiRequest.restore()
- return this._parseSubscriptionXml.restore()
- })
- it('sends correct XML', function(done) {
- return this.call((err, result) => {
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
- <subscription>
- <plan_code>some_plan_code</plan_code>
- <currency>EUR</currency>
- <coupon_code/>
- <account>
- <account_code>some_id</account_code>
- <email>user@example.com</email>
- <first_name>Prairie</first_name>
- <last_name>Johnson</last_name>
- <billing_info>
- <token_id>a-token-id</token_id>
- </billing_info>
- </account>
- </subscription>\
- `)
- return done()
- })
- })
- it('should not produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.not.be.instanceof(Error)
- expect(err).to.equal(null)
- return done()
- })
- })
- it('should produce a subscription', function(done) {
- return this.call((err, sub) => {
- expect(sub).to.equal(this.subscription)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, sub) => {
- this.apiRequest.callCount.should.equal(1)
- return done()
- })
- })
- it('should call _parseSubscriptionXml', function(done) {
- return this.call((err, sub) => {
- this._parseSubscriptionXml.callCount.should.equal(1)
- return done()
- })
- })
- describe('when api request produces an error', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, new Error('woops'))
- })
- it('should produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, sub) => {
- this.apiRequest.callCount.should.equal(1)
- return done()
- })
- })
- it('should not _parseSubscriptionXml', function(done) {
- return this.call((err, sub) => {
- this._parseSubscriptionXml.callCount.should.equal(0)
- return done()
- })
- })
- })
- describe('when parse xml produces an error', function() {
- beforeEach(function() {
- return this._parseSubscriptionXml.callsArgWith(1, new Error('woops'))
- })
- it('should produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- describe('_createPaypalSubscription', function() {
- beforeEach(function() {
- this.checkAccountExists = sinon.stub(
- this.RecurlyWrapper._paypal,
- 'checkAccountExists'
- )
- this.createAccount = sinon.stub(
- this.RecurlyWrapper._paypal,
- 'createAccount'
- )
- this.createBillingInfo = sinon.stub(
- this.RecurlyWrapper._paypal,
- 'createBillingInfo'
- )
- this.setAddress = sinon.stub(this.RecurlyWrapper._paypal, 'setAddress')
- this.createSubscription = sinon.stub(
- this.RecurlyWrapper._paypal,
- 'createSubscription'
- )
- this.user = {
- _id: 'some_id',
- email: 'user@example.com'
- }
- this.subscriptionDetails = {
- currencyCode: 'EUR',
- plan_code: 'some_plan_code',
- coupon_code: '',
- isPaypal: true,
- address: {
- address1: 'addr_one',
- address2: 'addr_two',
- country: 'some_country',
- state: 'some_state',
- zip: 'some_zip'
- }
- }
- this.subscription = {}
- this.recurly_token_id = 'a-token-id'
- // set up data callbacks
- const { user } = this
- const { subscriptionDetails } = this
- const { recurly_token_id } = this
- this.checkAccountExists.callsArgWith(1, null, {
- user,
- subscriptionDetails,
- recurly_token_id,
- userExists: false,
- account: { accountCode: 'xx' }
- })
- this.createAccount.callsArgWith(1, null, {
- user,
- subscriptionDetails,
- recurly_token_id,
- userExists: false,
- account: { accountCode: 'xx' }
- })
- this.createBillingInfo.callsArgWith(1, null, {
- user,
- subscriptionDetails,
- recurly_token_id,
- userExists: false,
- account: { accountCode: 'xx' },
- billingInfo: { token_id: 'abc' }
- })
- this.setAddress.callsArgWith(1, null, {
- user,
- subscriptionDetails,
- recurly_token_id,
- userExists: false,
- account: { accountCode: 'xx' },
- billingInfo: { token_id: 'abc' }
- })
- this.createSubscription.callsArgWith(1, null, {
- user,
- subscriptionDetails,
- recurly_token_id,
- userExists: false,
- account: { accountCode: 'xx' },
- billingInfo: { token_id: 'abc' },
- subscription: this.subscription
- })
- return (this.call = callback => {
- return this.RecurlyWrapper._createPaypalSubscription(
- this.user,
- this.subscriptionDetails,
- this.recurly_token_id,
- callback
- )
- })
- })
- afterEach(function() {
- this.checkAccountExists.restore()
- this.createAccount.restore()
- this.createBillingInfo.restore()
- this.setAddress.restore()
- return this.createSubscription.restore()
- })
- it('should not produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should produce a subscription object', function(done) {
- return this.call((err, sub) => {
- expect(sub).to.not.equal(null)
- expect(sub).to.equal(this.subscription)
- return done()
- })
- })
- it('should call each of the paypal stages', function(done) {
- return this.call((err, sub) => {
- this.checkAccountExists.callCount.should.equal(1)
- this.createAccount.callCount.should.equal(1)
- this.createBillingInfo.callCount.should.equal(1)
- this.setAddress.callCount.should.equal(1)
- this.createSubscription.callCount.should.equal(1)
- return done()
- })
- })
- describe('when one of the paypal stages produces an error', function() {
- beforeEach(function() {
- return this.createAccount.callsArgWith(1, new Error('woops'))
- })
- it('should produce an error', function(done) {
- return this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- it('should stop calling the paypal stages after the error', function(done) {
- return this.call((err, sub) => {
- this.checkAccountExists.callCount.should.equal(1)
- this.createAccount.callCount.should.equal(1)
- this.createBillingInfo.callCount.should.equal(0)
- this.setAddress.callCount.should.equal(0)
- this.createSubscription.callCount.should.equal(0)
- return done()
- })
- })
- })
- })
- describe('paypal actions', function() {
- beforeEach(function() {
- this.apiRequest = sinon.stub(this.RecurlyWrapper, 'apiRequest')
- this._parseAccountXml = sinon.spy(this.RecurlyWrapper, '_parseAccountXml')
- this._parseBillingInfoXml = sinon.spy(
- this.RecurlyWrapper,
- '_parseBillingInfoXml'
- )
- this._parseSubscriptionXml = sinon.spy(
- this.RecurlyWrapper,
- '_parseSubscriptionXml'
- )
- return (this.cache = {
- user: (this.user = {
- _id: 'some_id',
- email: 'foo@bar.com',
- first_name: 'Foo',
- last_name: 'Bar'
- }),
- recurly_token_id: (this.recurly_token_id = 'some_token'),
- subscriptionDetails: (this.subscriptionDetails = {
- currencyCode: 'EUR',
- plan_code: 'some_plan_code',
- coupon_code: '',
- isPaypal: true,
- address: {
- address1: 'addr_one',
- address2: 'addr_two',
- city: 'some_city',
- country: 'some_country',
- state: 'some_state',
- zip: 'some_zip'
- }
- })
- })
- })
- afterEach(function() {
- this.apiRequest.restore()
- this._parseAccountXml.restore()
- this._parseBillingInfoXml.restore()
- return this._parseSubscriptionXml.restore()
- })
- describe('_paypal.checkAccountExists', function() {
- beforeEach(function() {
- return (this.call = callback => {
- return this.RecurlyWrapper._paypal.checkAccountExists(
- this.cache,
- callback
- )
- })
- })
- describe('when the account exists', function() {
- beforeEach(function() {
- const resultXml =
- '<account><account_code>abc</account_code></account>'
- return this.apiRequest.callsArgWith(
- 1,
- null,
- { statusCode: 200 },
- resultXml
- )
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(1)
- return done()
- })
- })
- it('should call _parseAccountXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(1)
- return done()
- })
- })
- it('should add the account to the cumulative result', function(done) {
- return this.call((err, result) => {
- expect(result.account).to.not.equal(null)
- expect(result.account).to.not.equal(undefined)
- expect(result.account).to.deep.equal({
- account_code: 'abc'
- })
- return done()
- })
- })
- it('should set userExists to true', function(done) {
- return this.call((err, result) => {
- expect(result.userExists).to.equal(true)
- return done()
- })
- })
- })
- describe('when the account does not exist', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, null, { statusCode: 404 }, '')
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('GET')
- return done()
- })
- })
- it('should not call _parseAccountXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(0)
- return done()
- })
- })
- it('should not add the account to result', function(done) {
- return this.call((err, result) => {
- expect(result.account).to.equal(undefined)
- return done()
- })
- })
- it('should set userExists to false', function(done) {
- return this.call((err, result) => {
- expect(result.userExists).to.equal(false)
- return done()
- })
- })
- })
- describe('when apiRequest produces an error', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500
- })
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- describe('_paypal.createAccount', function() {
- beforeEach(function() {
- return (this.call = callback => {
- return this.RecurlyWrapper._paypal.createAccount(this.cache, callback)
- })
- })
- describe('when address is missing from subscriptionDetails', function() {
- beforeEach(function() {
- return (this.cache.subscriptionDetails.address = null)
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- describe('when account already exists', function() {
- beforeEach(function() {
- this.cache.userExists = true
- return (this.cache.account = { account_code: 'abc' })
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should produce cache object', function(done) {
- return this.call((err, result) => {
- expect(result).to.deep.equal(this.cache)
- expect(result.account).to.deep.equal({
- account_code: 'abc'
- })
- return done()
- })
- })
- it('should not call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(0)
- return done()
- })
- })
- it('should not call _parseAccountXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(0)
- return done()
- })
- })
- })
- describe('when account does not exist', function() {
- beforeEach(function() {
- this.cache.userExists = false
- const resultXml =
- '<account><account_code>abc</account_code></account>'
- return this.apiRequest.callsArgWith(
- 1,
- null,
- { statusCode: 200 },
- resultXml
- )
- })
- it('sends correct XML', function(done) {
- return this.call((err, result) => {
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
- <account>
- <account_code>some_id</account_code>
- <email>foo@bar.com</email>
- <first_name>Foo</first_name>
- <last_name>Bar</last_name>
- <address>
- <address1>addr_one</address1>
- <address2>addr_two</address2>
- <city>some_city</city>
- <state>some_state</state>
- <zip>some_zip</zip>
- <country>some_country</country>
- </address>
- </account>\
- `)
- return done()
- })
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('POST')
- return done()
- })
- })
- it('should call _parseAccountXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(1)
- return done()
- })
- })
- describe('when apiRequest produces an error', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500
- })
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- })
- describe('_paypal.createBillingInfo', function() {
- beforeEach(function() {
- this.cache.account = { account_code: 'abc' }
- return (this.call = callback => {
- return this.RecurlyWrapper._paypal.createBillingInfo(
- this.cache,
- callback
- )
- })
- })
- describe('when account_code is missing from cache', function() {
- beforeEach(function() {
- return (this.cache.account.account_code = null)
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- describe('when all goes well', function() {
- beforeEach(function() {
- const resultXml = '<billing_info><a>1</a></billing_info>'
- return this.apiRequest.callsArgWith(
- 1,
- null,
- { statusCode: 200 },
- resultXml
- )
- })
- it('sends correct XML', function(done) {
- return this.call((err, result) => {
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
- <billing_info>
- <token_id>some_token</token_id>
- </billing_info>\
- `)
- return done()
- })
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('POST')
- return done()
- })
- })
- it('should call _parseBillingInfoXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseBillingInfoXml.callCount.should.equal(1)
- return done()
- })
- })
- it('should set billingInfo on cache', function(done) {
- return this.call((err, result) => {
- expect(result.billingInfo).to.deep.equal({
- a: '1'
- })
- return done()
- })
- })
- })
- describe('when apiRequest produces an error', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500
- })
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- describe('_paypal.setAddress', function() {
- beforeEach(function() {
- this.cache.account = { account_code: 'abc' }
- this.cache.billingInfo = {}
- return (this.call = callback => {
- return this.RecurlyWrapper._paypal.setAddress(this.cache, callback)
- })
- })
- describe('when account_code is missing from cache', function() {
- beforeEach(function() {
- return (this.cache.account.account_code = null)
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- describe('when address is missing from subscriptionDetails', function() {
- beforeEach(function() {
- return (this.cache.subscriptionDetails.address = null)
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- describe('when all goes well', function() {
- beforeEach(function() {
- const resultXml = '<billing_info><city>London</city></billing_info>'
- return this.apiRequest.callsArgWith(
- 1,
- null,
- { statusCode: 200 },
- resultXml
- )
- })
- it('sends correct XML', function(done) {
- return this.call((err, result) => {
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
- <billing_info>
- <address1>addr_one</address1>
- <address2>addr_two</address2>
- <city>some_city</city>
- <state>some_state</state>
- <zip>some_zip</zip>
- <country>some_country</country>
- </billing_info>\
- `)
- return done()
- })
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('PUT')
- return done()
- })
- })
- it('should call _parseBillingInfoXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseBillingInfoXml.callCount.should.equal(1)
- return done()
- })
- })
- it('should set billingInfo on cache', function(done) {
- return this.call((err, result) => {
- expect(result.billingInfo).to.deep.equal({
- city: 'London'
- })
- return done()
- })
- })
- })
- describe('when apiRequest produces an error', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500
- })
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- describe('_paypal.createSubscription', function() {
- beforeEach(function() {
- this.cache.account = { account_code: 'abc' }
- this.cache.billingInfo = {}
- return (this.call = callback => {
- return this.RecurlyWrapper._paypal.createSubscription(
- this.cache,
- callback
- )
- })
- })
- describe('when all goes well', function() {
- beforeEach(function() {
- const resultXml = '<subscription><a>1</a></subscription>'
- return this.apiRequest.callsArgWith(
- 1,
- null,
- { statusCode: 200 },
- resultXml
- )
- })
- it('sends correct XML', function(done) {
- return this.call((err, result) => {
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
- <subscription>
- <plan_code>some_plan_code</plan_code>
- <currency>EUR</currency>
- <coupon_code/>
- <account>
- <account_code>some_id</account_code>
- </account>
- </subscription>\
- `)
- return done()
- })
- })
- it('should not produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- return done()
- })
- })
- it('should call apiRequest', function(done) {
- return this.call((err, result) => {
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('POST')
- return done()
- })
- })
- it('should call _parseSubscriptionXml', function(done) {
- return this.call((err, result) => {
- this.RecurlyWrapper._parseSubscriptionXml.callCount.should.equal(1)
- return done()
- })
- })
- it('should set subscription on cache', function(done) {
- return this.call((err, result) => {
- expect(result.subscription).to.deep.equal({
- a: '1'
- })
- return done()
- })
- })
- })
- describe('when apiRequest produces an error', function() {
- beforeEach(function() {
- return this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500
- })
- })
- it('should produce an error', function(done) {
- return this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- return done()
- })
- })
- })
- })
- })
- describe('listAccountActiveSubscriptions', function() {
- beforeEach(function() {
- this.user_id = 'mock-user-id'
- this.callback = sinon.stub()
- this.RecurlyWrapper.apiRequest = sinon
- .stub()
- .yields(
- null,
- (this.response = { mock: 'response' }),
- (this.body = '<mock body/>')
- )
- return (this.RecurlyWrapper._parseSubscriptionsXml = sinon
- .stub()
- .yields(null, (this.subscriptions = ['mock', 'subscriptions'])))
- })
- describe('with an account', function() {
- beforeEach(function() {
- return this.RecurlyWrapper.listAccountActiveSubscriptions(
- this.user_id,
- this.callback
- )
- })
- it('should send a request to Recurly', function() {
- return this.RecurlyWrapper.apiRequest
- .calledWith({
- url: `accounts/${this.user_id}/subscriptions`,
- qs: {
- state: 'active'
- },
- expect404: true
- })
- .should.equal(true)
- })
- it('should return the subscriptions', function() {
- return this.callback
- .calledWith(null, this.subscriptions)
- .should.equal(true)
- })
- })
- describe('without an account', function() {
- beforeEach(function() {
- this.response.statusCode = 404
- return this.RecurlyWrapper.listAccountActiveSubscriptions(
- this.user_id,
- this.callback
- )
- })
- it('should return an empty array of subscriptions', function() {
- return this.callback.calledWith(null, []).should.equal(true)
- })
- })
- })
- })
|