mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 14:13:43 +01:00
[web] Cleanup plans-2026-phase-1 - phase 4: rest of backend (#34771)
* Remove `plans-2026-phase-1` from subscription module * Remove code related to `plans-2026-phase-1` * Remove `SplitTestUserGetter.getProjection` * Update tests * Remove last mentions of `plans-2026-phase-1` * In test, wait for `_updateCustomerIoSubscriptionProperties` to be done * Remove `purchaseAddon` test * Remove AI-addon tests * Fix `_quotaTierToAllowance` throwing on zero-grant tiers `!quota` treated `0` as invalid, so `basic` (grant 0) threw. Check `typeof quota !== 'number'` alone. Unmasked once branch removed the split-test branch that skipped this path. * Extract `waitForCustomerIoSync` helper in FeaturesUpdater tests * https://github.com/overleaf/internal/pull/34771#discussion_r3552882875 GitOrigin-RevId: 0fe0e41b7a4105a3c4739214e2fffb77e91dbc42
This commit is contained in:
@@ -7,6 +7,16 @@ const { ObjectId } = mongodb
|
||||
|
||||
const MODULE_PATH = '../../../../app/src/Features/Subscription/FeaturesUpdater'
|
||||
|
||||
// helper awaiting _updateCustomerIoSubscriptionProperties called by refreshFeatures,
|
||||
// which is otherwise fire-and-forget.
|
||||
async function waitForCustomerIoSync(ctx) {
|
||||
await vi.waitFor(() => {
|
||||
expect(ctx.Modules.promises.hooks.fire).to.have.been.calledWith(
|
||||
'setUserProperties'
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
describe('FeaturesUpdater', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.renewalDate = new Date('2099-04-01T00:00:00Z')
|
||||
@@ -426,6 +436,7 @@ describe('FeaturesUpdater', function () {
|
||||
it('should return features and featuresChanged', async function (ctx) {
|
||||
const { features, featuresChanged } =
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
expect(features).to.exist
|
||||
expect(featuresChanged).to.exist
|
||||
})
|
||||
@@ -433,6 +444,7 @@ describe('FeaturesUpdater', function () {
|
||||
describe('normally', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should update the user with the merged features', function (ctx) {
|
||||
@@ -504,6 +516,7 @@ describe('FeaturesUpdater', function () {
|
||||
},
|
||||
])
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should sync trial_end_date to customer.io', function (ctx) {
|
||||
@@ -542,6 +555,7 @@ describe('FeaturesUpdater', function () {
|
||||
},
|
||||
])
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should report stripe as the payment_provider', function (ctx) {
|
||||
@@ -568,6 +582,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should sync commons=true to customer.io', function (ctx) {
|
||||
@@ -609,6 +624,7 @@ describe('FeaturesUpdater', function () {
|
||||
},
|
||||
])
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should set commons, individual_subscription, and ai-assist-add-on together', function (ctx) {
|
||||
@@ -636,6 +652,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should sync false subscription flags and no payment_provider', function (ctx) {
|
||||
@@ -682,6 +699,7 @@ describe('FeaturesUpdater', function () {
|
||||
])
|
||||
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should sync expiry_date and blank next_renewal_date in customer.io', function (ctx) {
|
||||
@@ -727,6 +745,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should sync groupSize to customer.io', function (ctx) {
|
||||
@@ -785,6 +804,7 @@ describe('FeaturesUpdater', function () {
|
||||
.resolves([{ _id: policyId, userCannotUseAIFeatures: true }]),
|
||||
})
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should set group_ai_enabled to false', function (ctx) {
|
||||
@@ -824,6 +844,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should derive payment_provider from the group subscription', function (ctx) {
|
||||
@@ -850,6 +871,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
expect(ctx.Modules.promises.hooks.fire).to.have.been.calledWith(
|
||||
'setUserProperties',
|
||||
ctx.user._id,
|
||||
@@ -867,6 +889,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
expect(ctx.Modules.promises.hooks.fire).to.have.been.calledWith(
|
||||
'setUserProperties',
|
||||
ctx.user._id,
|
||||
@@ -884,6 +907,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
expect(ctx.Modules.promises.hooks.fire).to.have.been.calledWith(
|
||||
'setUserProperties',
|
||||
ctx.user._id,
|
||||
@@ -901,6 +925,7 @@ describe('FeaturesUpdater', function () {
|
||||
}
|
||||
)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
expect(ctx.Modules.promises.hooks.fire).to.have.been.calledWith(
|
||||
'setUserProperties',
|
||||
ctx.user._id,
|
||||
@@ -915,6 +940,7 @@ describe('FeaturesUpdater', function () {
|
||||
.withArgs(ctx.user._id)
|
||||
.resolves(null)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should send mixed feature set user property', function (ctx) {
|
||||
@@ -934,6 +960,7 @@ describe('FeaturesUpdater', function () {
|
||||
.withArgs(ctx.user._id)
|
||||
.resolves(ctx.subscriptions.noDropbox)
|
||||
await ctx.FeaturesUpdater.promises.refreshFeatures(ctx.user._id, 'test')
|
||||
await waitForCustomerIoSync(ctx)
|
||||
})
|
||||
|
||||
it('should fire module hook to unlink dropbox', function (ctx) {
|
||||
|
||||
@@ -913,152 +913,6 @@ describe('SubscriptionController', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('purchaseAddon', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.SessionManager.getSessionUser.returns(ctx.user) // Make sure getSessionUser returns the user
|
||||
ctx.next = sinon.stub()
|
||||
ctx.req.params = { addOnCode: AI_ADD_ON_CODE } // Mock add-on code
|
||||
})
|
||||
|
||||
it('should return 200 on successful purchase of AI add-on', async function (ctx) {
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
ctx.res.sendStatus = sinon.spy()
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.SubscriptionHandler.promises.purchaseAddon).to.have.been.called
|
||||
expect(
|
||||
ctx.SubscriptionHandler.promises.purchaseAddon
|
||||
).to.have.been.calledWith(ctx.user._id, AI_ADD_ON_CODE, 1)
|
||||
expect(
|
||||
ctx.FeaturesUpdater.promises.refreshFeatures
|
||||
).to.have.been.calledWith(ctx.user._id, 'add-on-purchase')
|
||||
expect(ctx.res.sendStatus).to.have.been.calledWith(200)
|
||||
expect(ctx.logger.debug).to.have.been.calledWith(
|
||||
{ userId: ctx.user._id, addOnCode: AI_ADD_ON_CODE },
|
||||
'purchasing add-ons'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return 404 if the add-on code is not AI_ADD_ON_CODE', async function (ctx) {
|
||||
ctx.req.params = { addOnCode: 'some-other-addon' }
|
||||
ctx.res.sendStatus = sinon.spy()
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.SubscriptionHandler.promises.purchaseAddon).to.not.have.been
|
||||
.called
|
||||
expect(ctx.FeaturesUpdater.promises.refreshFeatures).to.not.have.been
|
||||
.called
|
||||
expect(ctx.res.sendStatus).to.have.been.calledWith(404)
|
||||
})
|
||||
|
||||
it('should return 404 when plans-2026-phase-1 split test is enabled', async function (ctx) {
|
||||
ctx.SplitTestV2Hander.promises.getAssignment.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
ctx.res.sendStatus = sinon.spy()
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.SubscriptionHandler.promises.purchaseAddon).to.not.have.been
|
||||
.called
|
||||
expect(ctx.res.sendStatus).to.have.been.calledWith(404)
|
||||
})
|
||||
|
||||
it('should handle DuplicateAddOnError and send badRequest while sending 200', async function (ctx) {
|
||||
ctx.req.params.addOnCode = AI_ADD_ON_CODE
|
||||
ctx.SubscriptionHandler.promises.purchaseAddon.rejects(
|
||||
new SubscriptionErrors.DuplicateAddOnError()
|
||||
)
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.HttpErrorHandler.badRequest).to.have.been.calledWith(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'Your subscription already includes this add-on',
|
||||
{ addon: AI_ADD_ON_CODE }
|
||||
)
|
||||
expect(
|
||||
ctx.FeaturesUpdater.promises.refreshFeatures
|
||||
).to.have.been.calledWith(ctx.user._id, 'add-on-purchase')
|
||||
expect(ctx.res.sendStatus).toHaveBeenCalledWith(200)
|
||||
})
|
||||
|
||||
it('should handle PaymentActionRequiredError and return 402 with details', async function (ctx) {
|
||||
ctx.req.params.addOnCode = AI_ADD_ON_CODE
|
||||
const paymentError = new SubscriptionErrors.PaymentActionRequiredError({
|
||||
clientSecret: 'secret123',
|
||||
publicKey: 'pubkey456',
|
||||
})
|
||||
ctx.SubscriptionHandler.promises.purchaseAddon.rejects(paymentError)
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.res.status).toHaveBeenCalledWith(402)
|
||||
expect(ctx.res.json).toHaveBeenCalledWith({
|
||||
message: 'Payment action required',
|
||||
clientSecret: 'secret123',
|
||||
publicKey: 'pubkey456',
|
||||
})
|
||||
|
||||
expect(ctx.FeaturesUpdater.promises.refreshFeatures).to.not.have.been
|
||||
.called
|
||||
})
|
||||
|
||||
it('should refresh features', async function (ctx) {
|
||||
ctx.req.params.addOnCode = 'assistant'
|
||||
ctx.SubscriptionHandler.promises.purchaseAddon = sinon.stub().resolves()
|
||||
ctx.FeaturesUpdater.promises.refreshFeatures = sinon.stub().resolves()
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res)
|
||||
|
||||
expect(
|
||||
ctx.FeaturesUpdater.promises.refreshFeatures.calledWith(
|
||||
ctx.user._id,
|
||||
'add-on-purchase'
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('should respond with a bad request if the subscription already includes the addOn', async function (ctx) {
|
||||
ctx.req.params.addOnCode = 'assistant'
|
||||
ctx.SubscriptionHandler.promises.purchaseAddon = sinon
|
||||
.stub()
|
||||
.rejects(new SubscriptionErrors.DuplicateAddOnError())
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res)
|
||||
|
||||
expect(
|
||||
ctx.HttpErrorHandler.badRequest.calledWith(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'Your subscription already includes this add-on',
|
||||
{ addon: 'assistant' }
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('should handle MultiplePendingChangesError and return 422 with JSON response', async function (ctx) {
|
||||
ctx.req.params.addOnCode = AI_ADD_ON_CODE
|
||||
ctx.SubscriptionHandler.promises.purchaseAddon.rejects(
|
||||
new SubscriptionErrors.MultiplePendingChangesError()
|
||||
)
|
||||
|
||||
await ctx.SubscriptionController.purchaseAddon(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.res.status).toHaveBeenCalledWith(422)
|
||||
expect(ctx.res.json).toHaveBeenCalledWith({
|
||||
code: 'multiple_pending_changes',
|
||||
message:
|
||||
'Cannot complete purchase while there are multiple pending subscription changes. Please contact support.',
|
||||
})
|
||||
expect(ctx.FeaturesUpdater.promises.refreshFeatures).to.not.have.been
|
||||
.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('removeAddon', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.SessionManager.getSessionUser.returns(ctx.user)
|
||||
@@ -1812,21 +1666,12 @@ describe('SubscriptionController', function () {
|
||||
describe('previewAddonPurchase', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.req = new MockRequest(vi)
|
||||
ctx.req.params = { addOnCode: 'assistant' }
|
||||
ctx.req.query = { purchaseReferrer: 'fake-referrer' }
|
||||
ctx.res = new MockResponse(vi)
|
||||
|
||||
ctx.Modules.promises.hooks.fire
|
||||
.withArgs('getPaymentMethod')
|
||||
.resolves(['fake-method'])
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(null)
|
||||
ctx.res.redirect = sinon.stub()
|
||||
})
|
||||
|
||||
it('should redirect with ai-assist-unavailable when plans-2026-phase-1 is enabled', async function (ctx) {
|
||||
ctx.SplitTestV2Hander.promises.getAssignment.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
ctx.res.redirect = sinon.stub()
|
||||
it('should redirect with ai-assist-unavailable for the AI add-on', async function (ctx) {
|
||||
ctx.req.params = { addOnCode: AI_ADD_ON_CODE }
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
@@ -1835,177 +1680,15 @@ describe('SubscriptionController', function () {
|
||||
)
|
||||
})
|
||||
|
||||
describe('when user has manual or custom subscription', function () {
|
||||
it('should redirect with ai-assist-unavailable when subscription has customAccount = true', async function (ctx) {
|
||||
const customSubscription = {
|
||||
_id: 'sub-123',
|
||||
customAccount: true,
|
||||
collectionMethod: 'automatic',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
customSubscription
|
||||
)
|
||||
it('should 404 for an unknown add-on', async function (ctx) {
|
||||
ctx.req.params = { addOnCode: 'unknown-addon' }
|
||||
|
||||
ctx.res.redirect = sinon.stub()
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.res.redirect).to.have.been.calledWith(
|
||||
'/user/subscription?redirect-reason=ai-assist-unavailable'
|
||||
)
|
||||
})
|
||||
|
||||
it('should redirect with ai-assist-unavailable when subscription has collectionMethod = manual', async function (ctx) {
|
||||
const manualSubscription = {
|
||||
_id: 'sub-123',
|
||||
customAccount: false,
|
||||
collectionMethod: 'manual',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
manualSubscription
|
||||
)
|
||||
|
||||
ctx.res.redirect = sinon.stub()
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.res.redirect).to.have.been.calledWith(
|
||||
'/user/subscription?redirect-reason=ai-assist-unavailable'
|
||||
)
|
||||
})
|
||||
|
||||
it('should redirect with ai-assist-unavailable when subscription has both customAccount and manual collection', async function (ctx) {
|
||||
const customManualSubscription = {
|
||||
_id: 'sub-123',
|
||||
customAccount: true,
|
||||
collectionMethod: 'manual',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
customManualSubscription
|
||||
)
|
||||
|
||||
ctx.res.redirect = sinon.stub()
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.res.redirect).to.have.been.calledWith(
|
||||
'/user/subscription?redirect-reason=ai-assist-unavailable'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when user has normal subscription', function () {
|
||||
it('should proceed with preview when subscription is not manual or custom', async function (ctx) {
|
||||
const normalSubscription = {
|
||||
_id: 'sub-123',
|
||||
customAccount: false,
|
||||
collectionMethod: 'automatic',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
normalSubscription
|
||||
)
|
||||
ctx.PlansLocator.findLocalPlanInSettings.withArgs('assistant').returns({
|
||||
planCode: 'assistant',
|
||||
name: 'AI Assist',
|
||||
annual: false,
|
||||
})
|
||||
|
||||
ctx.res.render = sinon.stub()
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.res.render).to.have.been.calledWith(
|
||||
'subscriptions/preview-change',
|
||||
sinon.match({
|
||||
changePreview: sinon.match({
|
||||
change: {
|
||||
type: 'add-on-purchase',
|
||||
addOn: { code: 'assistant', name: 'AI Assist' },
|
||||
},
|
||||
}),
|
||||
purchaseReferrer: 'fake-referrer',
|
||||
redirectedPaymentErrorCode: undefined,
|
||||
})
|
||||
)
|
||||
expect(
|
||||
ctx.SubscriptionHandler.promises.previewAddonPurchase
|
||||
).to.have.been.calledWith(ctx.user._id, 'assistant')
|
||||
expect(
|
||||
ctx.PlansLocator.findLocalPlanInSettings
|
||||
).to.have.been.calledWith('assistant')
|
||||
})
|
||||
|
||||
it('should pass redirectedPaymentErrorCode to the view when errorCode query param is present', async function (ctx) {
|
||||
const normalSubscription = {
|
||||
_id: 'sub-123',
|
||||
customAccount: false,
|
||||
collectionMethod: 'automatic',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
normalSubscription
|
||||
)
|
||||
ctx.req.query.errorCode = 'payment_failed'
|
||||
|
||||
ctx.res.render = sinon.stub()
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.res.render).to.have.been.calledWith(
|
||||
'subscriptions/preview-change',
|
||||
sinon.match({
|
||||
changePreview: sinon.match.object,
|
||||
purchaseReferrer: 'fake-referrer',
|
||||
redirectedPaymentErrorCode: 'payment_failed',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('returns 404 when the add-on code is not in the local plan registry', async function (ctx) {
|
||||
const normalSubscription = {
|
||||
_id: 'sub-123',
|
||||
customAccount: false,
|
||||
collectionMethod: 'automatic',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
normalSubscription
|
||||
)
|
||||
ctx.PlansLocator.findLocalPlanInSettings
|
||||
.withArgs('assistant')
|
||||
.returns(null)
|
||||
|
||||
ctx.res.render = sinon.stub()
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.HttpErrorHandler.notFound).to.have.been.calledWith(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'Unknown add-on: assistant'
|
||||
)
|
||||
expect(ctx.res.render).not.to.have.been.called
|
||||
})
|
||||
|
||||
it('should proceed with preview when customAccount is undefined and collectionMethod is automatic', async function (ctx) {
|
||||
const normalSubscription = {
|
||||
_id: 'sub-123',
|
||||
// customAccount: undefined (not set)
|
||||
collectionMethod: 'automatic',
|
||||
}
|
||||
ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
|
||||
normalSubscription
|
||||
)
|
||||
|
||||
ctx.res.render = sinon.stub()
|
||||
|
||||
await ctx.SubscriptionController.previewAddonPurchase(ctx.req, ctx.res)
|
||||
|
||||
expect(ctx.res.render).to.have.been.calledWith(
|
||||
'subscriptions/preview-change'
|
||||
)
|
||||
expect(
|
||||
ctx.SubscriptionHandler.promises.previewAddonPurchase
|
||||
).to.have.been.calledWith(ctx.user._id, 'assistant')
|
||||
})
|
||||
expect(ctx.HttpErrorHandler.notFound).to.have.been.calledWith(
|
||||
ctx.req,
|
||||
ctx.res
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user