1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-04 00:18:40 +01:00

Project health tests (#3028)

This commit is contained in:
Tymoteusz Czech 2023-02-02 12:01:16 +01:00 committed by GitHub
parent 627958d30f
commit ab9712812a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 217 additions and 19 deletions

View File

@ -0,0 +1,203 @@
import { subDays } from 'date-fns';
import type { IFeatureType } from 'lib/types/stores/feature-type-store';
import {
calculateProjectHealth,
calculateHealthRating,
} from './project-health';
const exampleFeatureTypes: IFeatureType[] = [
{
id: 'default',
name: 'Default',
description: '',
lifetimeDays: 30,
},
{
id: 'short-lived',
name: 'Short lived',
description: '',
lifetimeDays: 7,
},
{
id: 'non-expiring',
name: 'Long lived',
description: '',
lifetimeDays: null,
},
];
describe('calculateProjectHealth', () => {
it('works with empty features', () => {
expect(calculateProjectHealth([], exampleFeatureTypes)).toEqual({
activeCount: 0,
staleCount: 0,
potentiallyStaleCount: 0,
});
});
it('counts active toggles', () => {
const features = [{ stale: false }, {}];
expect(calculateProjectHealth(features, exampleFeatureTypes)).toEqual({
activeCount: 2,
staleCount: 0,
potentiallyStaleCount: 0,
});
});
it('counts stale toggles', () => {
const features = [{ stale: true }, { stale: false }, {}];
expect(calculateProjectHealth(features, exampleFeatureTypes)).toEqual({
activeCount: 2,
staleCount: 1,
potentiallyStaleCount: 0,
});
});
it('takes feature type into account when calculating potentially stale toggles', () => {
expect(
calculateProjectHealth(
[
{
stale: false,
createdAt: subDays(Date.now(), 15),
type: 'default',
},
],
exampleFeatureTypes,
),
).toEqual({
activeCount: 1,
staleCount: 0,
potentiallyStaleCount: 0,
});
expect(
calculateProjectHealth(
[
{
stale: false,
createdAt: subDays(Date.now(), 31),
type: 'default',
},
],
exampleFeatureTypes,
),
).toEqual({
activeCount: 1,
staleCount: 0,
potentiallyStaleCount: 1,
});
expect(
calculateProjectHealth(
[
{
stale: false,
createdAt: subDays(Date.now(), 15),
type: 'short-lived',
},
],
exampleFeatureTypes,
),
).toEqual({
activeCount: 1,
staleCount: 0,
potentiallyStaleCount: 1,
});
});
it("doesn't count stale toggles as potentially stale or stale as active", () => {
const features = [
{
stale: true,
createdAt: subDays(Date.now(), 31),
type: 'default',
},
{
stale: false,
createdAt: subDays(Date.now(), 31),
type: 'default',
},
];
expect(calculateProjectHealth(features, exampleFeatureTypes)).toEqual({
activeCount: 1,
staleCount: 1,
potentiallyStaleCount: 1,
});
});
it('counts non-expiring types properly', () => {
const features = [
{
createdAt: subDays(Date.now(), 366),
type: 'non-expiring',
},
{
createdAt: subDays(Date.now(), 366),
type: 'default',
},
];
expect(calculateProjectHealth(features, exampleFeatureTypes)).toEqual({
activeCount: 2,
staleCount: 0,
potentiallyStaleCount: 1,
});
});
});
describe('calculateHealthRating', () => {
it('works with empty feature toggles', () => {
expect(calculateHealthRating([], exampleFeatureTypes)).toEqual(100);
});
it('works with stale and active feature toggles', () => {
expect(
calculateHealthRating(
[{ stale: true }, { stale: true }],
exampleFeatureTypes,
),
).toEqual(0);
expect(
calculateHealthRating(
[{ stale: true }, { stale: false }],
exampleFeatureTypes,
),
).toEqual(50);
expect(
calculateHealthRating(
[{ stale: false }, { stale: true }, { stale: false }],
exampleFeatureTypes,
),
).toEqual(67);
});
it('counts potentially stale toggles', () => {
expect(
calculateHealthRating(
[
{ createdAt: subDays(Date.now(), 1), type: 'default' },
{
stale: true,
createdAt: subDays(Date.now(), 1),
type: 'default',
},
{
stale: true,
createdAt: subDays(Date.now(), 31),
type: 'default',
},
{
stale: false,
createdAt: subDays(Date.now(), 31),
type: 'default',
},
],
exampleFeatureTypes,
),
).toEqual(25);
});
});

View File

@ -2,12 +2,14 @@ import { hoursToMilliseconds } from 'date-fns';
import type { IProjectHealthReport } from 'lib/types'; import type { IProjectHealthReport } from 'lib/types';
import type { IFeatureType } from 'lib/types/stores/feature-type-store'; import type { IFeatureType } from 'lib/types/stores/feature-type-store';
type IPartialFeatures = Array<{
stale?: boolean;
createdAt?: Date;
type?: string;
}>;
const getPotentiallyStaleCount = ( const getPotentiallyStaleCount = (
features: Array<{ features: IPartialFeatures,
stale?: boolean;
createdAt?: Date;
type?: string;
}>,
featureTypes: IFeatureType[], featureTypes: IFeatureType[],
) => { ) => {
const today = new Date().valueOf(); const today = new Date().valueOf();
@ -20,17 +22,14 @@ const getPotentiallyStaleCount = (
return ( return (
!feature.stale && !feature.stale &&
featureTypeExpectedLifetime !== null &&
diff >= featureTypeExpectedLifetime * hoursToMilliseconds(24) diff >= featureTypeExpectedLifetime * hoursToMilliseconds(24)
); );
}).length; }).length;
}; };
export const calculateProjectHealth = ( export const calculateProjectHealth = (
features: Array<{ features: IPartialFeatures,
stale?: boolean;
createdAt?: Date;
type?: string;
}>,
featureTypes: IFeatureType[], featureTypes: IFeatureType[],
): Pick< ): Pick<
IProjectHealthReport, IProjectHealthReport,
@ -41,12 +40,8 @@ export const calculateProjectHealth = (
staleCount: features.filter((f) => f.stale).length, staleCount: features.filter((f) => f.stale).length,
}); });
export const getHealthRating = ( export const calculateHealthRating = (
features: Array<{ features: IPartialFeatures,
stale?: boolean;
createdAt?: Date;
type?: string;
}>,
featureTypes: IFeatureType[], featureTypes: IFeatureType[],
): number => { ): number => {
const { potentiallyStaleCount, activeCount, staleCount } = const { potentiallyStaleCount, activeCount, staleCount } =

View File

@ -11,8 +11,8 @@ import type { IProjectStore } from '../types/stores/project-store';
import ProjectService from './project-service'; import ProjectService from './project-service';
import { import {
calculateProjectHealth, calculateProjectHealth,
getHealthRating, calculateHealthRating,
} from '../domain/calculate-project-health/calculate-project-health'; } from '../domain/project-health/project-health';
export default class ProjectHealthService { export default class ProjectHealthService {
private logger: Logger; private logger: Logger;
@ -82,7 +82,7 @@ export default class ProjectHealthService {
archived: false, archived: false,
}); });
return getHealthRating(toggles, this.featureTypes); return calculateHealthRating(toggles, this.featureTypes);
} }
async setHealthRating(): Promise<void> { async setHealthRating(): Promise<void> {