mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Project health tests (#3028)
This commit is contained in:
		
							parent
							
								
									627958d30f
								
							
						
					
					
						commit
						ab9712812a
					
				
							
								
								
									
										203
									
								
								src/lib/domain/project-health/project-health.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/lib/domain/project-health/project-health.test.ts
									
									
									
									
									
										Normal 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);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -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 } =
 | 
				
			||||||
@ -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> {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user