11import { describe , expect , it } from 'vitest'
22import { applyBlocklistCorrection } from '../../../../app/utils/download-anomalies'
3- import type { WeeklyDataPoint } from '../../../../app/types/chart'
3+ import type {
4+ MonthlyDataPoint ,
5+ WeeklyDataPoint ,
6+ YearlyDataPoint ,
7+ } from '../../../../app/types/chart'
48
59/** Helper to build a WeeklyDataPoint from a start date and value. */
610function week ( weekStart : string , value : number ) : WeeklyDataPoint {
@@ -18,6 +22,22 @@ function week(weekStart: string, value: number): WeeklyDataPoint {
1822 }
1923}
2024
25+ function month ( monthStr : string , value : number ) : MonthlyDataPoint {
26+ return {
27+ value,
28+ month : monthStr ,
29+ timestamp : new Date ( `${ monthStr } -01T00:00:00Z` ) . getTime ( ) ,
30+ }
31+ }
32+
33+ function year ( yearStr : string , value : number ) : YearlyDataPoint {
34+ return {
35+ value,
36+ year : yearStr ,
37+ timestamp : new Date ( `${ yearStr } -01-01T00:00:00Z` ) . getTime ( ) ,
38+ }
39+ }
40+
2141describe ( 'applyBlocklistCorrection' , ( ) => {
2242 // Anomaly Nov 2022: start=2022-11-15, end=2022-11-30
2343 it ( 'corrects weeks overlapping the anomaly' , ( ) => {
@@ -90,4 +110,74 @@ describe('applyBlocklistCorrection', () => {
90110 expect ( result [ 1 ] ! . hasAnomaly ) . toBe ( true )
91111 expect ( result [ 1 ] ! . value ) . toBeLessThan ( 1_000_000 )
92112 } )
113+
114+ // Vite anomaly: start=2025-08-04, end=2025-09-08 (spans Aug-Sep)
115+ it ( 'does not over-correct a month that only touches the anomaly end boundary' , ( ) => {
116+ const data = [
117+ month ( '2025-07' , 30_000_000 ) ,
118+ month ( '2025-08' , 100_000_000 ) , // contains spike
119+ month ( '2025-09' , 100_000_000 ) , // contains spike (Sep 1-7)
120+ month ( '2025-10' , 30_000_000 ) , // after anomaly end — normal!
121+ ]
122+
123+ const result = applyBlocklistCorrection ( {
124+ data,
125+ packageName : 'vite' ,
126+ granularity : 'monthly' ,
127+ } ) as MonthlyDataPoint [ ]
128+
129+ expect ( result [ 1 ] ! . hasAnomaly ) . toBe ( true )
130+ expect ( result [ 2 ] ! . hasAnomaly ) . toBe ( true )
131+
132+ // October must NOT be modified
133+ expect ( result [ 3 ] ! . value ) . toBe ( 30_000_000 )
134+ expect ( result [ 3 ] ! . hasAnomaly ) . toBeUndefined ( )
135+ } )
136+
137+ it ( 'does not over-correct a month that only touches the anomaly start boundary' , ( ) => {
138+ const data = [
139+ month ( '2025-07' , 30_000_000 ) , // before anomaly start — normal!
140+ month ( '2025-08' , 100_000_000 ) , // contains spike
141+ month ( '2025-09' , 100_000_000 ) , // contains spike
142+ month ( '2025-10' , 30_000_000 ) ,
143+ ]
144+
145+ const result = applyBlocklistCorrection ( {
146+ data,
147+ packageName : 'vite' ,
148+ granularity : 'monthly' ,
149+ } ) as MonthlyDataPoint [ ]
150+
151+ // July must NOT be modified
152+ expect ( result [ 0 ] ! . value ) . toBe ( 30_000_000 )
153+ expect ( result [ 0 ] ! . hasAnomaly ) . toBeUndefined ( )
154+
155+ expect ( result [ 1 ] ! . hasAnomaly ) . toBe ( true )
156+ expect ( result [ 2 ] ! . hasAnomaly ) . toBe ( true )
157+ } )
158+
159+ it ( 'does not over-correct a year that only touches the anomaly boundary' , ( ) => {
160+ const data = [
161+ year ( '2024' , 500_000_000 ) ,
162+ year ( '2025' , 2_000_000_000 ) , // contains spike
163+ year ( '2026' , 500_000_000 ) ,
164+ ]
165+
166+ const result = applyBlocklistCorrection ( {
167+ data,
168+ packageName : 'vite' ,
169+ granularity : 'yearly' ,
170+ } ) as YearlyDataPoint [ ]
171+
172+ // 2024 must NOT be modified
173+ expect ( result [ 0 ] ! . value ) . toBe ( 500_000_000 )
174+ expect ( result [ 0 ] ! . hasAnomaly ) . toBeUndefined ( )
175+
176+ // 2025 must be corrected
177+ expect ( result [ 1 ] ! . hasAnomaly ) . toBe ( true )
178+
179+ // 2026 must NOT be modified
180+ expect ( result [ 2 ] ! . value ) . toBe ( 500_000_000 )
181+ expect ( result [ 2 ] ! . hasAnomaly ) . toBeUndefined ( )
182+ } )
93183} )
0 commit comments