1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: frontend variant weights distribution (#4347)

## About the changes
Unit-tested way of distributing weights between variants.
This commit is contained in:
Tymoteusz Czech 2023-07-26 16:08:11 +02:00 committed by GitHub
parent 7095e87061
commit d2a4763eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 129 additions and 7 deletions

View File

@ -0,0 +1,117 @@
import { updateWeightEdit } from './util';
const variantTemplate = {
id: '0',
name: 'A',
weight: 0,
weightType: 'variable' as const,
stickiness: 'default',
isValid: true,
new: false,
};
describe('updateWeightEdit', () => {
it('can assign weight to one only variant', () => {
const variants = [variantTemplate];
expect(updateWeightEdit(variants, 100)).toMatchInlineSnapshot(`
[
{
"id": "0",
"isValid": true,
"name": "A",
"new": false,
"stickiness": "default",
"weight": 100,
"weightType": "variable",
},
]
`);
});
it('can distribute weight between 2 variants evenly', () => {
const variants = [
variantTemplate,
{ ...variantTemplate, id: '2', name: 'B' },
];
updateWeightEdit(variants, 100).forEach(variant => {
expect(variant).toHaveProperty('weight', 50);
});
});
it('can distribute weight between 8 variants evenly', () => {
const variants = Array.from({ length: 8 }, (_, i) => ({
...variantTemplate,
id: `${i}`,
name: `${i}`,
weight: i,
}));
updateWeightEdit(variants, 1000).forEach(variant => {
expect(variant).toHaveProperty('weight', 125);
});
});
it('can distribute weight between 8 variants evenly and assign the remainder to the last variant', () => {
const variants = Array.from({ length: 8 }, (_, i) => ({
...variantTemplate,
id: `${i}`,
name: `${i}`,
weight: i,
}));
const weights = updateWeightEdit(variants, 100).map(
variant => variant.weight
);
expect(weights).toEqual([13, 12, 13, 12, 13, 12, 13, 12]);
});
it('can adjust variable weight to get correct sum', () => {
const variants = [
{ ...variantTemplate, weightType: 'fix' as const, weight: 333 },
{ ...variantTemplate, id: '2', name: 'B' },
];
const weights = updateWeightEdit(variants, 1000).map(
variant => variant.weight
);
expect(weights).toEqual([333, 667]);
});
it('can deal with complex example', () => {
const variants = [
{ ...variantTemplate, weightType: 'fix' as const, weight: 333 },
{ ...variantTemplate, id: '2', name: 'B' },
{ ...variantTemplate, id: '3', name: 'C' },
{ ...variantTemplate, id: '4', name: 'D' },
{ ...variantTemplate, id: '5', name: 'E' },
{
...variantTemplate,
id: '6',
name: 'F',
weightType: 'fix' as const,
weight: 111,
},
{ ...variantTemplate, id: '7', name: 'G' },
{ ...variantTemplate, id: '8', name: 'H' },
];
const weights = updateWeightEdit(variants, 1000).map(
variant => variant.weight
);
expect(weights).toEqual([333, 93, 93, 93, 92, 111, 93, 92]);
});
it('can deal with 0-weight variable variant', () => {
const variants = [
{ ...variantTemplate, weightType: 'fix' as const, weight: 500 },
{
...variantTemplate,
weightType: 'fix' as const,
weight: 500,
id: '2',
name: 'B',
},
{ ...variantTemplate, id: '3', name: 'C' },
];
const weights = updateWeightEdit(variants, 1000).map(
variant => variant.weight
);
expect(weights).toEqual([500, 500, 0]);
});
});

View File

@ -98,11 +98,12 @@ export function updateWeightEdit(
if (variants.length === 0) {
return [];
}
const { remainingPercentage, variableVariantCount } = variants.reduce(
let { remainingPercentage, variableVariantCount } = variants.reduce(
({ remainingPercentage, variableVariantCount }, variant) => {
if (variant.weight && variant.weightType === weightTypes.FIX) {
remainingPercentage -= Number(variant.weight);
} else {
}
if (variant.weightType === weightTypes.VARIABLE) {
variableVariantCount += 1;
}
return {
@ -113,14 +114,18 @@ export function updateWeightEdit(
{ remainingPercentage: totalWeight, variableVariantCount: 0 }
);
const percentage = parseInt(
String(remainingPercentage / variableVariantCount)
);
const getPercentage = () =>
Math.round(remainingPercentage / variableVariantCount);
return variants.map(variant => {
if (variant.weightType !== weightTypes.FIX) {
const percentage = getPercentage(); // round "as we go" - clean best effort approach
remainingPercentage -= percentage;
variableVariantCount -= 1;
variant.weight = percentage;
}
return variant;
});
}

View File

@ -17,7 +17,7 @@ test('should render variants', async () => {
name: 'variantName',
stickiness: 'default',
weight: 1000,
weightType: 'variable',
weightType: 'variable' as const,
payload: {
type: 'string',
value: 'variantValue',

View File

@ -52,7 +52,7 @@ export interface IFeatureVariant {
name: string;
stickiness: string;
weight: number;
weightType: string;
weightType: 'fix' | 'variable';
overrides?: IOverride[];
payload?: IPayload;
}