Merge pull request #32244 from overleaf/ab-labs-in-feature-flags
[web] Merge Labs programme into the Feature flags system GitOrigin-RevId: db75e07bf3272becc11ef1eeda3850098b3daa9d
This commit is contained in:
committed by
Copybot
parent
6f3e0963b8
commit
671df33da3
@@ -499,21 +499,322 @@ describe('SplitTestHandler', function () {
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
})
|
||||
|
||||
it('should assign to default if userCount is undefined', async function (ctx) {
|
||||
describe('labs phase assignment (split test)', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.AnalyticsManager.getIdsFromSession.returns({
|
||||
userId: 'abc123abc123',
|
||||
})
|
||||
ctx.cachedSplitTests.set(
|
||||
'active-test',
|
||||
makeSplitTest('active-test', { userLimit: 100, userCount: undefined })
|
||||
'labs-experiment',
|
||||
makeSplitTest('labs-experiment', { phase: 'labs' })
|
||||
)
|
||||
})
|
||||
|
||||
it('should assign to variant when user is in labs program and has opted into the experiment', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'active-test'
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
|
||||
it('should assign to default when user is in labs program but has not opted into the experiment', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['some-other-experiment'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('default')
|
||||
})
|
||||
|
||||
it('should assign to default when user is not in labs program', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: false,
|
||||
labsExperiments: [],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('default')
|
||||
})
|
||||
|
||||
it('should assign to default when user has no labsExperiments field', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('default')
|
||||
})
|
||||
|
||||
it('should assign to variant when under user limit', async function (ctx) {
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment',
|
||||
makeSplitTest('labs-experiment', {
|
||||
phase: 'labs',
|
||||
userLimit: 10,
|
||||
userCount: 5,
|
||||
})
|
||||
)
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
|
||||
it('should assign enrolled user to variant even when user limit is reached', async function (ctx) {
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment',
|
||||
makeSplitTest('labs-experiment', {
|
||||
phase: 'labs',
|
||||
userLimit: 10,
|
||||
userCount: 10,
|
||||
})
|
||||
)
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
|
||||
it('should allow already-assigned user even when limit is reached', async function (ctx) {
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment',
|
||||
makeSplitTest('labs-experiment', {
|
||||
phase: 'labs',
|
||||
userLimit: 10,
|
||||
userCount: 10,
|
||||
})
|
||||
)
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {
|
||||
'labs-experiment': [
|
||||
{
|
||||
variantName: 'variant-1',
|
||||
versionNumber: 1,
|
||||
assignedAt: new Date(),
|
||||
phase: 'labs',
|
||||
},
|
||||
],
|
||||
},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('labs phase assignment (gradual rollout)', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.AnalyticsManager.getIdsFromSession.returns({
|
||||
userId: 'abc123abc123',
|
||||
})
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment-gr',
|
||||
makeSplitTest('labs-experiment-gr', {
|
||||
phase: 'labs',
|
||||
analyticsEnabled: false,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should assign to variant when user is in labs program and has opted in', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment-gr'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment-gr'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
|
||||
it('should assign to default when user has not opted in', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: [],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment-gr'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('default')
|
||||
})
|
||||
|
||||
it('should assign to default when user is not in labs program', async function (ctx) {
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: false,
|
||||
labsExperiments: [],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment-gr'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('default')
|
||||
})
|
||||
|
||||
it('should assign to variant when under user limit', async function (ctx) {
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment-gr',
|
||||
makeSplitTest('labs-experiment-gr', {
|
||||
phase: 'labs',
|
||||
analyticsEnabled: false,
|
||||
userLimit: 10,
|
||||
userCount: 5,
|
||||
})
|
||||
)
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment-gr'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment-gr'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
|
||||
it('should assign enrolled user to variant even when user limit is reached', async function (ctx) {
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment-gr',
|
||||
makeSplitTest('labs-experiment-gr', {
|
||||
phase: 'labs',
|
||||
analyticsEnabled: false,
|
||||
userLimit: 10,
|
||||
userCount: 10,
|
||||
})
|
||||
)
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment-gr'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment-gr'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
|
||||
it('should allow already-assigned user even when limit is reached', async function (ctx) {
|
||||
ctx.cachedSplitTests.set(
|
||||
'labs-experiment-gr',
|
||||
makeSplitTest('labs-experiment-gr', {
|
||||
phase: 'labs',
|
||||
analyticsEnabled: false,
|
||||
userLimit: 10,
|
||||
userCount: 10,
|
||||
})
|
||||
)
|
||||
ctx.SplitTestUserGetter.promises.getUser.resolves({
|
||||
_id: new ObjectId('abc123abc123abc123abc123'),
|
||||
splitTests: {
|
||||
'labs-experiment-gr': [
|
||||
{
|
||||
variantName: 'variant-1',
|
||||
versionNumber: 1,
|
||||
assignedAt: new Date(),
|
||||
phase: 'labs',
|
||||
},
|
||||
],
|
||||
},
|
||||
labsProgram: true,
|
||||
labsExperiments: ['labs-experiment-gr'],
|
||||
})
|
||||
|
||||
const assignment = await ctx.SplitTestHandler.promises.getAssignment(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
'labs-experiment-gr'
|
||||
)
|
||||
|
||||
expect(assignment.variant).to.equal('variant-1')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -325,4 +325,52 @@ describe('SplitTestSessionHandler', function () {
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
describe('clearCachedVariant', function () {
|
||||
it('should remove all cached entries for a given split test name', function (ctx) {
|
||||
const session = {
|
||||
cachedSplitTestAssignments: {
|
||||
'my-test-1': 'variant-1',
|
||||
'my-test-2': 'variant-2',
|
||||
'other-test-1': 'variant-1',
|
||||
},
|
||||
}
|
||||
ctx.SplitTestSessionHandler.clearCachedVariant(session, 'my-test')
|
||||
expect(session.cachedSplitTestAssignments).to.deep.equal({
|
||||
'other-test-1': 'variant-1',
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle split test names with hyphens correctly', function (ctx) {
|
||||
const session = {
|
||||
cachedSplitTestAssignments: {
|
||||
'monthly-texlive-1': 'enabled',
|
||||
'monthly-texlive-2': 'enabled',
|
||||
'monthly-1': 'variant-1',
|
||||
},
|
||||
}
|
||||
ctx.SplitTestSessionHandler.clearCachedVariant(session, 'monthly-texlive')
|
||||
expect(session.cachedSplitTestAssignments).to.deep.equal({
|
||||
'monthly-1': 'variant-1',
|
||||
})
|
||||
})
|
||||
|
||||
it('should do nothing when session has no cached assignments', function (ctx) {
|
||||
const session = {}
|
||||
ctx.SplitTestSessionHandler.clearCachedVariant(session, 'my-test')
|
||||
expect(session.cachedSplitTestAssignments).to.be.undefined
|
||||
})
|
||||
|
||||
it('should do nothing when there are no matching entries', function (ctx) {
|
||||
const session = {
|
||||
cachedSplitTestAssignments: {
|
||||
'other-test-1': 'variant-1',
|
||||
},
|
||||
}
|
||||
ctx.SplitTestSessionHandler.clearCachedVariant(session, 'my-test')
|
||||
expect(session.cachedSplitTestAssignments).to.deep.equal({
|
||||
'other-test-1': 'variant-1',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user