Prechádzať zdrojové kódy

Merge pull request #12473 from overleaf/ab-server-pro-split-test-overrides

[web] Add split test overrides through settings for non-SaaS env

GitOrigin-RevId: 82cb6a573a992e730107f6287e7804cfe0f04aa5
June Kelly 3 rokov pred
rodič
commit
e780a09a15

+ 16 - 3
services/web/app/src/Features/SplitTests/SplitTestHandler.js

@@ -11,6 +11,7 @@ const UserAnalyticsIdCache = require('../Analytics/UserAnalyticsIdCache')
 const { getAnalyticsIdFromMongoUser } = require('../Analytics/AnalyticsHelper')
 const Features = require('../../infrastructure/Features')
 const SplitTestUtils = require('./SplitTestUtils')
+const Settings = require('@overleaf/settings')
 
 const DEFAULT_VARIANT = 'default'
 const ALPHA_PHASE = 'alpha'
@@ -49,7 +50,7 @@ const DEFAULT_ASSIGNMENT = {
  */
 async function getAssignment(req, res, splitTestName, { sync = false } = {}) {
   if (!Features.hasFeature('saas')) {
-    return DEFAULT_ASSIGNMENT
+    return _getNonSaasAssignment(splitTestName)
   }
 
   const query = req.query || {}
@@ -107,7 +108,7 @@ async function getAssignmentForUser(
   { sync = false } = {}
 ) {
   if (!Features.hasFeature('saas')) {
-    return DEFAULT_ASSIGNMENT
+    return _getNonSaasAssignment(splitTestName)
   }
 
   const analyticsId = await UserAnalyticsIdCache.get(userId)
@@ -131,7 +132,7 @@ async function getAssignmentForMongoUser(
   { sync = false } = {}
 ) {
   if (!Features.hasFeature('saas')) {
-    return DEFAULT_ASSIGNMENT
+    return _getNonSaasAssignment(splitTestName)
   }
 
   return _getAssignment(splitTestName, {
@@ -403,6 +404,18 @@ async function _loadSplitTestInfoInLocals(locals, splitTestName) {
   }
 }
 
+function _getNonSaasAssignment(splitTestName) {
+  if (Settings.splitTestOverrides?.[splitTestName]) {
+    return {
+      variant: Settings.splitTestOverrides?.[splitTestName],
+      analytics: {
+        segmentation: {},
+      },
+    }
+  }
+  return DEFAULT_ASSIGNMENT
+}
+
 module.exports = {
   getPercentile,
   getAssignment: callbackify(getAssignment),

+ 86 - 3
services/web/test/unit/src/SplitTests/SplitTestHandlerTests.js

@@ -2,7 +2,9 @@ const Path = require('path')
 const SandboxedModule = require('sandboxed-module')
 const sinon = require('sinon')
 const { ObjectId } = require('mongodb')
-const { expect } = require('chai')
+const { assert, expect } = require('chai')
+const MockRequest = require('../helpers/MockRequest')
+const MockResponse = require('../helpers/MockResponse')
 
 const MODULE_PATH = Path.join(
   __dirname,
@@ -39,6 +41,17 @@ describe('SplitTestHandler', function () {
     for (const splitTest of this.splitTests) {
       this.SplitTestCache.get.withArgs(splitTest.name).resolves(splitTest)
     }
+    this.Settings = {
+      moduleImportSequence: [],
+      overleaf: {},
+    }
+    this.AnalyticsManager = {
+      getIdsFromSession: sinon.stub(),
+    }
+    this.LocalsHelper = {
+      setSplitTestVariant: sinon.stub(),
+      setSplitTestInfo: sinon.stub(),
+    }
 
     this.SplitTestHandler = SandboxedModule.require(MODULE_PATH, {
       requires: {
@@ -46,10 +59,14 @@ describe('SplitTestHandler', function () {
         './SplitTestCache': this.SplitTestCache,
         '../../models/SplitTest': { SplitTest: this.SplitTest },
         '../User/UserUpdater': {},
-        '../Analytics/AnalyticsManager': {},
-        './LocalsHelper': {},
+        '../Analytics/AnalyticsManager': this.AnalyticsManager,
+        './LocalsHelper': this.LocalsHelper,
+        '@overleaf/settings': this.Settings,
       },
     })
+
+    this.req = new MockRequest()
+    this.res = new MockResponse()
   })
 
   describe('with an existing user', function () {
@@ -174,6 +191,72 @@ describe('SplitTestHandler', function () {
       })
     })
   })
+
+  describe('with settings overrides', function () {
+    beforeEach(function () {
+      this.Settings.splitTestOverrides = {
+        'my-test-name': 'foo-1',
+      }
+    })
+
+    it('should not use the override when in SaaS mode', async function () {
+      this.AnalyticsManager.getIdsFromSession.returns({
+        userId: 'abc123abc123',
+      })
+      this.SplitTestCache.get.returns({
+        name: 'my-test-name',
+        versions: [
+          {
+            versionNumber: 0,
+            active: true,
+            variants: [
+              {
+                name: '100-percent-variant',
+                rolloutPercent: 100,
+                rolloutStripes: [{ start: 0, end: 100 }],
+              },
+            ],
+          },
+        ],
+      })
+
+      const assignment = await this.SplitTestHandler.promises.getAssignment(
+        this.req,
+        this.res,
+        'my-test-name'
+      )
+
+      assert.equal('100-percent-variant', assignment.variant)
+    })
+
+    it('should use the override when not in SaaS mode', async function () {
+      this.Settings.splitTestOverrides = {
+        'my-test-name': 'foo-1',
+      }
+      this.Settings.overleaf = undefined
+
+      const assignment = await this.SplitTestHandler.promises.getAssignment(
+        this.req,
+        this.res,
+        'my-test-name'
+      )
+
+      assert.equal('foo-1', assignment.variant)
+    })
+
+    it('should use default when not in SaaS mode and no override is provided', async function () {
+      this.Settings.splitTestOverrides = {}
+      this.Settings.overleaf = undefined
+
+      const assignment = await this.SplitTestHandler.promises.getAssignment(
+        this.req,
+        this.res,
+        'my-test-name'
+      )
+
+      assert.equal('default', assignment.variant)
+    })
+  })
 })
 
 function makeSplitTest(