@@ -106,6 +106,10 @@ function createEnv() {
106106 } ;
107107}
108108
109+ function scheduledGroupId ( timestamp : number ) : string {
110+ return `cron:${ timestamp } :${ Math . trunc ( timestamp / ( 10 * 60 * 1000 ) ) } ` ;
111+ }
112+
109113describe ( "scheduled task runner and admin API" , ( ) => {
110114 it ( "records successful runs and exposes logs through the admin API" , async ( ) => {
111115 const { env, d1 } = createEnv ( ) ;
@@ -220,7 +224,7 @@ describe("scheduled task runner and admin API", () => {
220224
221225 expect ( response . status ) . toBe ( 200 ) ;
222226 expect ( payload . runs . map ( ( run ) => run . id ) ) . toEqual (
223- [ 5 , 6 , 7 , 8 , 9 ] . map ( ( index ) => `cron: ${ now - index * 60_000 } ` ) ,
227+ [ 5 , 6 , 7 , 8 , 9 ] . map ( ( index ) => scheduledGroupId ( now - index * 60_000 ) ) ,
224228 ) ;
225229 expect ( payload . runsMeta ) . toEqual ( {
226230 page : 2 ,
@@ -229,11 +233,75 @@ describe("scheduled task runner and admin API", () => {
229233 hasMore : true ,
230234 nextPage : 3 ,
231235 } ) ;
232- expect ( payload . selectedRun ?. id ) . toBe ( `cron: ${ now - 10 * 60_000 } ` ) ;
236+ expect ( payload . selectedRun ?. id ) . toBe ( scheduledGroupId ( now - 10 * 60_000 ) ) ;
233237 expect ( payload . selectedRun ?. runs [ 0 ] ?. id ) . toBe ( "run-10" ) ;
234238 d1 . close ( ) ;
235239 } ) ;
236240
241+ it ( "counts skipped runs as successful in run groups" , async ( ) => {
242+ const { env, d1 } = createEnv ( ) ;
243+ const now = Date . now ( ) ;
244+ const scheduledAt = now - 60_000 ;
245+ const expiresAt = Math . floor ( ( now + 30 * 24 * 60 * 60 * 1000 ) / 1000 ) ;
246+
247+ for ( const [ index , status ] of [ "success" , "skipped" ] . entries ( ) ) {
248+ d1 . db
249+ . prepare (
250+ `
251+ INSERT INTO scheduled_task_runs (
252+ id,
253+ invocation_id,
254+ task_key,
255+ task_name,
256+ trigger_type,
257+ status,
258+ scheduled_at_ms,
259+ started_at_ms,
260+ finished_at_ms,
261+ duration_ms,
262+ summary_json,
263+ created_at,
264+ expires_at
265+ )
266+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
267+ ` ,
268+ )
269+ . run (
270+ `run-${ index } ` ,
271+ `invocation-${ index } ` ,
272+ index === 0 ? "notification_tick" : "visit_hourly_rollup" ,
273+ index === 0 ? "Notification dispatch" : "Hourly visit aggregation" ,
274+ "cron" ,
275+ status ,
276+ scheduledAt ,
277+ scheduledAt + index * 1_000 ,
278+ scheduledAt + index * 1_000 + 250 ,
279+ 250 ,
280+ "{}" ,
281+ Math . floor ( now / 1000 ) ,
282+ expiresAt ,
283+ ) ;
284+ }
285+
286+ const response = await handleScheduledTasksAdmin (
287+ new Request ( "https://edge.test/api/private/admin/scheduled-tasks" ) ,
288+ env ,
289+ new URL ( "https://edge.test/api/private/admin/scheduled-tasks" ) ,
290+ async ( ) => ( { isAdmin : true } ) ,
291+ ) ;
292+ const payload = ( await response . json ( ) ) as ScheduledTasksData ;
293+
294+ expect ( response . status ) . toBe ( 200 ) ;
295+ expect ( payload . runs [ 0 ] ) . toMatchObject ( {
296+ status : "success" ,
297+ taskCount : 2 ,
298+ successCount : 2 ,
299+ skippedCount : 1 ,
300+ } ) ;
301+ expect ( payload . health . successRate24h ) . toBe ( 1 ) ;
302+ d1 . close ( ) ;
303+ } ) ;
304+
237305 it ( "marks failed runs when the handler throws" , async ( ) => {
238306 const { env, d1 } = createEnv ( ) ;
239307
0 commit comments