|
|
@@ -2,6 +2,10 @@ const SandboxedModule = require('sandboxed-module')
|
|
|
const sinon = require('sinon')
|
|
|
const chai = require('chai')
|
|
|
const { expect } = chai
|
|
|
+const {
|
|
|
+ RecurlySubscription,
|
|
|
+ RecurlySubscriptionChangeRequest,
|
|
|
+} = require('../../../../app/src/Features/Subscription/RecurlyEntities')
|
|
|
|
|
|
const MODULE_PATH =
|
|
|
'../../../../app/src/Features/Subscription/SubscriptionHandler'
|
|
|
@@ -23,20 +27,16 @@ const mockRecurlySubscriptions = {
|
|
|
}
|
|
|
|
|
|
const mockRecurlyClientSubscriptions = {
|
|
|
- 'subscription-123-active': {
|
|
|
- id: 'subscription-123-recurly-id',
|
|
|
- uuid: 'subscription-123-active',
|
|
|
- plan: {
|
|
|
- name: 'Gold',
|
|
|
- code: 'gold',
|
|
|
- },
|
|
|
- currentPeriodEndsAt: new Date(),
|
|
|
- state: 'active',
|
|
|
- unitAmount: 10,
|
|
|
- account: {
|
|
|
- code: 'user-123',
|
|
|
- },
|
|
|
- },
|
|
|
+ 'subscription-123-active': new RecurlySubscription({
|
|
|
+ id: 'subscription-123-active',
|
|
|
+ userId: 'user-id',
|
|
|
+ planCode: 'collaborator',
|
|
|
+ planName: 'Collaborator',
|
|
|
+ planPrice: 10,
|
|
|
+ subtotal: 10,
|
|
|
+ currency: 'USD',
|
|
|
+ total: 10,
|
|
|
+ }),
|
|
|
}
|
|
|
|
|
|
const mockSubscriptionChanges = {
|
|
|
@@ -53,11 +53,16 @@ describe('SubscriptionHandler', function () {
|
|
|
{
|
|
|
planCode: 'collaborator',
|
|
|
name: 'Collaborator',
|
|
|
+ price_in_cents: 1000,
|
|
|
features: {
|
|
|
collaborators: -1,
|
|
|
versioning: true,
|
|
|
},
|
|
|
},
|
|
|
+ {
|
|
|
+ planCode: 'professional',
|
|
|
+ price_in_cents: 1500,
|
|
|
+ },
|
|
|
],
|
|
|
defaultPlanCode: {
|
|
|
collaborators: 0,
|
|
|
@@ -94,7 +99,7 @@ describe('SubscriptionHandler', function () {
|
|
|
.stub()
|
|
|
.resolves(this.activeRecurlyClientSubscription),
|
|
|
cancelSubscriptionByUuid: sinon.stub().resolves(),
|
|
|
- changeSubscriptionByUuid: sinon
|
|
|
+ applySubscriptionChangeRequest: sinon
|
|
|
.stub()
|
|
|
.resolves(this.activeRecurlySubscriptionChange),
|
|
|
getSubscription: sinon
|
|
|
@@ -122,14 +127,6 @@ describe('SubscriptionHandler', function () {
|
|
|
sendDeferredEmail: sinon.stub(),
|
|
|
}
|
|
|
|
|
|
- this.PlansLocator = {
|
|
|
- findLocalPlanInSettings: sinon.stub().returns({ planCode: 'plan' }),
|
|
|
- }
|
|
|
-
|
|
|
- this.SubscriptionHelper = {
|
|
|
- shouldPlanChangeAtTermEnd: sinon.stub(),
|
|
|
- }
|
|
|
-
|
|
|
this.UserUpdater = {
|
|
|
promises: {
|
|
|
updateUser: sinon.stub().resolves(),
|
|
|
@@ -148,8 +145,6 @@ describe('SubscriptionHandler', function () {
|
|
|
'./LimitationsManager': this.LimitationsManager,
|
|
|
'../Email/EmailHandler': this.EmailHandler,
|
|
|
'../Analytics/AnalyticsManager': this.AnalyticsManager,
|
|
|
- './PlansLocator': this.PlansLocator,
|
|
|
- './SubscriptionHelper': this.SubscriptionHelper,
|
|
|
'../User/UserUpdater': this.UserUpdater,
|
|
|
},
|
|
|
})
|
|
|
@@ -245,106 +240,81 @@ describe('SubscriptionHandler', function () {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- function shouldUpdateSubscription() {
|
|
|
- it('should update the subscription', function () {
|
|
|
- expect(
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid
|
|
|
- ).to.have.been.calledWith(this.subscription.recurlySubscription_id)
|
|
|
- const updateOptions =
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid.args[0][1]
|
|
|
- updateOptions.planCode.should.equal(this.plan_code)
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- function shouldSyncSubscription() {
|
|
|
- it('should sync the new subscription to the user', function () {
|
|
|
- expect(this.SubscriptionUpdater.promises.syncSubscription).to.have.been
|
|
|
- .called
|
|
|
-
|
|
|
- this.SubscriptionUpdater.promises.syncSubscription.args[0][0].should.deep.equal(
|
|
|
- this.activeRecurlySubscription
|
|
|
- )
|
|
|
- this.SubscriptionUpdater.promises.syncSubscription.args[0][1].should.deep.equal(
|
|
|
- this.user._id
|
|
|
- )
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- function testUserWithASubscription(shouldPlanChangeAtTermEnd, timeframe) {
|
|
|
- describe(
|
|
|
- 'when change should happen with timeframe ' + timeframe,
|
|
|
- function () {
|
|
|
- beforeEach(async function () {
|
|
|
- this.user.id = this.activeRecurlySubscription.account.account_code
|
|
|
- this.User.findById = (userId, projection) => ({
|
|
|
- exec: () => {
|
|
|
- userId.should.equal(this.user.id)
|
|
|
- return Promise.resolve(this.user)
|
|
|
- },
|
|
|
- })
|
|
|
- this.plan_code = 'collaborator'
|
|
|
- this.SubscriptionHelper.shouldPlanChangeAtTermEnd.returns(
|
|
|
- shouldPlanChangeAtTermEnd
|
|
|
- )
|
|
|
- this.LimitationsManager.promises.userHasV2Subscription.resolves({
|
|
|
- hasSubscription: true,
|
|
|
- subscription: this.subscription,
|
|
|
- })
|
|
|
- await this.SubscriptionHandler.promises.updateSubscription(
|
|
|
- this.user,
|
|
|
- this.plan_code,
|
|
|
- null
|
|
|
- )
|
|
|
+ describe('updateSubscription', function () {
|
|
|
+ describe('with a user with a subscription', function () {
|
|
|
+ beforeEach(async function () {
|
|
|
+ this.user.id = this.activeRecurlySubscription.account.account_code
|
|
|
+ this.User.findById = (userId, projection) => ({
|
|
|
+ exec: () => {
|
|
|
+ userId.should.equal(this.user.id)
|
|
|
+ return Promise.resolve(this.user)
|
|
|
+ },
|
|
|
})
|
|
|
-
|
|
|
- shouldUpdateSubscription()
|
|
|
- shouldSyncSubscription()
|
|
|
-
|
|
|
- it('should update with timeframe ' + timeframe, function () {
|
|
|
- const updateOptions =
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid.args[0][1]
|
|
|
- updateOptions.timeframe.should.equal(timeframe)
|
|
|
+ this.plan_code = 'professional'
|
|
|
+ this.LimitationsManager.promises.userHasV2Subscription.resolves({
|
|
|
+ hasSubscription: true,
|
|
|
+ subscription: this.subscription,
|
|
|
})
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
+ await this.SubscriptionHandler.promises.updateSubscription(
|
|
|
+ this.user,
|
|
|
+ this.plan_code,
|
|
|
+ null
|
|
|
+ )
|
|
|
+ })
|
|
|
|
|
|
- describe('updateSubscription', function () {
|
|
|
- describe('with a user with a subscription', function () {
|
|
|
- testUserWithASubscription(false, 'now')
|
|
|
- testUserWithASubscription(true, 'term_end')
|
|
|
-
|
|
|
- describe('when plan(s) could not be located in settings', function () {
|
|
|
- beforeEach(async function () {
|
|
|
- this.user.id = this.activeRecurlySubscription.account.account_code
|
|
|
- this.User.findById = (userId, projection) => ({
|
|
|
- exec: () => {
|
|
|
- userId.should.equal(this.user.id)
|
|
|
- return Promise.resolve(this.user)
|
|
|
- },
|
|
|
+ it('should update the subscription', function () {
|
|
|
+ expect(
|
|
|
+ this.RecurlyClient.promises.applySubscriptionChangeRequest
|
|
|
+ ).to.have.been.calledWith(
|
|
|
+ new RecurlySubscriptionChangeRequest({
|
|
|
+ subscriptionId: this.subscription.recurlySubscription_id,
|
|
|
+ timeframe: 'now',
|
|
|
+ planCode: this.plan_code,
|
|
|
})
|
|
|
+ )
|
|
|
+ })
|
|
|
|
|
|
- this.plan_code = 'collaborator'
|
|
|
- this.PlansLocator.findLocalPlanInSettings.returns(null)
|
|
|
- this.LimitationsManager.promises.userHasV2Subscription.resolves({
|
|
|
- hasSubscription: true,
|
|
|
- subscription: this.subscription,
|
|
|
- })
|
|
|
+ it('should sync the new subscription to the user', function () {
|
|
|
+ expect(this.SubscriptionUpdater.promises.syncSubscription).to.have.been
|
|
|
+ .called
|
|
|
+
|
|
|
+ this.SubscriptionUpdater.promises.syncSubscription.args[0][0].should.deep.equal(
|
|
|
+ this.activeRecurlySubscription
|
|
|
+ )
|
|
|
+ this.SubscriptionUpdater.promises.syncSubscription.args[0][1].should.deep.equal(
|
|
|
+ this.user._id
|
|
|
+ )
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('when plan(s) could not be located in settings', function () {
|
|
|
+ beforeEach(async function () {
|
|
|
+ this.user.id = this.activeRecurlySubscription.account.account_code
|
|
|
+ this.User.findById = (userId, projection) => ({
|
|
|
+ exec: () => {
|
|
|
+ userId.should.equal(this.user.id)
|
|
|
+ return Promise.resolve(this.user)
|
|
|
+ },
|
|
|
})
|
|
|
|
|
|
- it('should be rejected and should not update the subscription', function () {
|
|
|
- expect(
|
|
|
- this.SubscriptionHandler.promises.updateSubscription(
|
|
|
- this.user,
|
|
|
- this.plan_code,
|
|
|
- null
|
|
|
- )
|
|
|
- ).to.be.rejected
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid.called.should.equal(
|
|
|
- false
|
|
|
- )
|
|
|
+ this.LimitationsManager.promises.userHasV2Subscription.resolves({
|
|
|
+ hasSubscription: true,
|
|
|
+ subscription: this.subscription,
|
|
|
})
|
|
|
})
|
|
|
+
|
|
|
+ it('should be rejected and should not update the subscription', function () {
|
|
|
+ expect(
|
|
|
+ this.SubscriptionHandler.promises.updateSubscription(
|
|
|
+ this.user,
|
|
|
+ 'unknown-plan',
|
|
|
+ null
|
|
|
+ )
|
|
|
+ ).to.be.rejected
|
|
|
+ this.RecurlyClient.promises.applySubscriptionChangeRequest.called.should.equal(
|
|
|
+ false
|
|
|
+ )
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
describe('with a user without a subscription', function () {
|
|
|
@@ -358,7 +328,7 @@ describe('SubscriptionHandler', function () {
|
|
|
})
|
|
|
|
|
|
it('should redirect to the subscription dashboard', function () {
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid.called.should.equal(
|
|
|
+ this.RecurlyClient.promises.applySubscriptionChangeRequest.called.should.equal(
|
|
|
false
|
|
|
)
|
|
|
this.SubscriptionUpdater.promises.syncSubscription.called.should.equal(
|
|
|
@@ -407,11 +377,14 @@ describe('SubscriptionHandler', function () {
|
|
|
|
|
|
it('should update the subscription', function () {
|
|
|
expect(
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid
|
|
|
- ).to.be.calledWith(this.subscription.recurlySubscription_id)
|
|
|
- const updateOptions =
|
|
|
- this.RecurlyClient.promises.changeSubscriptionByUuid.args[0][1]
|
|
|
- updateOptions.planCode.should.equal(this.plan_code)
|
|
|
+ this.RecurlyClient.promises.applySubscriptionChangeRequest
|
|
|
+ ).to.be.calledWith(
|
|
|
+ new RecurlySubscriptionChangeRequest({
|
|
|
+ subscriptionId: this.subscription.recurlySubscription_id,
|
|
|
+ timeframe: 'now',
|
|
|
+ planCode: this.plan_code,
|
|
|
+ })
|
|
|
+ )
|
|
|
})
|
|
|
})
|
|
|
})
|