From 353c681d51bca1a2401efc4c62c00f14c7fed7eb Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Thu, 30 Apr 2026 14:45:02 +0200 Subject: [PATCH] [web] Disable AI Assist add-on purchase for plans-2026-phase-1 users (#33178) Users in the plans-2026-phase-1=enabled split test can no longer purchase the AI Assist add-on via crafted HTTP requests. The preview and purchase endpoints return 404/redirect for these users. Co-authored-by: Claude Sonnet 4.6 GitOrigin-RevId: 2c75eb622cf44dc91019a692290ac646b51fd72c --- .../Subscription/SubscriptionController.mjs | 22 ++++++++++++++++ .../SubscriptionController.test.mjs | 26 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.mjs b/services/web/app/src/Features/Subscription/SubscriptionController.mjs index 52e5bd6d06..7c07bc8437 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.mjs +++ b/services/web/app/src/Features/Subscription/SubscriptionController.mjs @@ -532,6 +532,18 @@ async function previewAddonPurchase(req, res) { return HttpErrorHandler.notFound(req, res, `Unknown add-on: ${addOnCode}`) } + const { variant: plans2026Phase1Variant } = + await SplitTestHandler.promises.getAssignment( + req, + res, + 'plans-2026-phase-1' + ) + if (plans2026Phase1Variant === 'enabled') { + return res.redirect( + '/user/subscription?redirect-reason=ai-assist-unavailable' + ) + } + const canUseAi = await PermissionsManager.promises.checkUserPermissions( user, ['use-ai'] @@ -656,6 +668,16 @@ async function purchaseAddon(req, res, next) { return res.sendStatus(404) } + const { variant: plans2026Phase1Variant } = + await SplitTestHandler.promises.getAssignment( + req, + res, + 'plans-2026-phase-1' + ) + if (plans2026Phase1Variant === 'enabled') { + return res.sendStatus(404) + } + const { isPaused } = await checkSubscriptionPauseStatus(user) if (isPaused) { return HttpErrorHandler.badRequest( diff --git a/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs b/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs index c370ac9131..50bc8695da 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs +++ b/services/web/test/unit/src/Subscription/SubscriptionController.test.mjs @@ -932,6 +932,19 @@ describe('SubscriptionController', function () { 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( @@ -1428,6 +1441,19 @@ describe('SubscriptionController', function () { ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(null) }) + 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() + + 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 manual or custom subscription', function () { it('should redirect with ai-assist-unavailable when subscription has customAccount = true', async function (ctx) { const customSubscription = {