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