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
This commit is contained in:
June Kelly 2023-04-11 14:19:04 +01:00 committed by Copybot
parent 17109393c5
commit e780a09a15
2 changed files with 102 additions and 6 deletions

View file

@ -11,6 +11,7 @@ const UserAnalyticsIdCache = require('../Analytics/UserAnalyticsIdCache')
const { getAnalyticsIdFromMongoUser } = require('../Analytics/AnalyticsHelper') const { getAnalyticsIdFromMongoUser } = require('../Analytics/AnalyticsHelper')
const Features = require('../../infrastructure/Features') const Features = require('../../infrastructure/Features')
const SplitTestUtils = require('./SplitTestUtils') const SplitTestUtils = require('./SplitTestUtils')
const Settings = require('@overleaf/settings')
const DEFAULT_VARIANT = 'default' const DEFAULT_VARIANT = 'default'
const ALPHA_PHASE = 'alpha' const ALPHA_PHASE = 'alpha'
@ -49,7 +50,7 @@ const DEFAULT_ASSIGNMENT = {
*/ */
async function getAssignment(req, res, splitTestName, { sync = false } = {}) { async function getAssignment(req, res, splitTestName, { sync = false } = {}) {
if (!Features.hasFeature('saas')) { if (!Features.hasFeature('saas')) {
return DEFAULT_ASSIGNMENT return _getNonSaasAssignment(splitTestName)
} }
const query = req.query || {} const query = req.query || {}
@ -107,7 +108,7 @@ async function getAssignmentForUser(
{ sync = false } = {} { sync = false } = {}
) { ) {
if (!Features.hasFeature('saas')) { if (!Features.hasFeature('saas')) {
return DEFAULT_ASSIGNMENT return _getNonSaasAssignment(splitTestName)
} }
const analyticsId = await UserAnalyticsIdCache.get(userId) const analyticsId = await UserAnalyticsIdCache.get(userId)
@ -131,7 +132,7 @@ async function getAssignmentForMongoUser(
{ sync = false } = {} { sync = false } = {}
) { ) {
if (!Features.hasFeature('saas')) { if (!Features.hasFeature('saas')) {
return DEFAULT_ASSIGNMENT return _getNonSaasAssignment(splitTestName)
} }
return _getAssignment(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 = { module.exports = {
getPercentile, getPercentile,
getAssignment: callbackify(getAssignment), getAssignment: callbackify(getAssignment),

View file

@ -2,7 +2,9 @@ const Path = require('path')
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const sinon = require('sinon') const sinon = require('sinon')
const { ObjectId } = require('mongodb') 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( const MODULE_PATH = Path.join(
__dirname, __dirname,
@ -39,6 +41,17 @@ describe('SplitTestHandler', function () {
for (const splitTest of this.splitTests) { for (const splitTest of this.splitTests) {
this.SplitTestCache.get.withArgs(splitTest.name).resolves(splitTest) 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, { this.SplitTestHandler = SandboxedModule.require(MODULE_PATH, {
requires: { requires: {
@ -46,10 +59,14 @@ describe('SplitTestHandler', function () {
'./SplitTestCache': this.SplitTestCache, './SplitTestCache': this.SplitTestCache,
'../../models/SplitTest': { SplitTest: this.SplitTest }, '../../models/SplitTest': { SplitTest: this.SplitTest },
'../User/UserUpdater': {}, '../User/UserUpdater': {},
'../Analytics/AnalyticsManager': {}, '../Analytics/AnalyticsManager': this.AnalyticsManager,
'./LocalsHelper': {}, './LocalsHelper': this.LocalsHelper,
'@overleaf/settings': this.Settings,
}, },
}) })
this.req = new MockRequest()
this.res = new MockResponse()
}) })
describe('with an existing user', function () { 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( function makeSplitTest(