@ -27,10 +27,11 @@ describe('stalledProjects (D22 — dashboard/MIS tile)', () => {
await setMilestone ( db , 'u1' , cm . id , 'setup_done' , true , '2026-03-01' )
await setMilestone ( db , 'u1' , cm . id , 'setup_done' , true , '2026-03-01' )
// last done_on = 2026-03-01, today = 2026-07-19 → ~140 days parked.
// last done_on = 2026-03-01, today = 2026-07-19 → ~140 days parked.
const stalled = await stalledProjects ( db , 30 , '2026-07-19' )
const stalled = await stalledProjects ( db , 30 , '2026-07-19' )
expect ( stalled . map( ( p ) = > p . clientModuleId ) ) . toContain ( cm . id )
expect ( stalled . rows. map( ( p ) = > p . clientModuleId ) ) . toContain ( cm . id )
const row = stalled . find( ( p ) = > p . clientModuleId === cm . id ) !
const row = stalled . rows. find( ( p ) = > p . clientModuleId === cm . id ) !
expect ( row . clientName ) . toBe ( 'Kothavara SCB' )
expect ( row . clientName ) . toBe ( 'Kothavara SCB' )
expect ( row . module Code ) . toBe ( 'MISC' )
expect ( row . module Code ) . toBe ( 'MISC' )
expect ( stalled . unknownAgeExcluded ) . toBe ( 0 )
} )
} )
it ( 'excludes a recently-progressed project' , async ( ) = > {
it ( 'excludes a recently-progressed project' , async ( ) = > {
@ -39,7 +40,8 @@ describe('stalledProjects (D22 — dashboard/MIS tile)', () => {
await setMilestone ( db , 'u1' , cm . id , 'advance_paid' , true , '2026-03-01' )
await setMilestone ( db , 'u1' , cm . id , 'advance_paid' , true , '2026-03-01' )
await setMilestone ( db , 'u1' , cm . id , 'setup_done' , true , '2026-03-01' )
await setMilestone ( db , 'u1' , cm . id , 'setup_done' , true , '2026-03-01' )
const stalled = await stalledProjects ( db , 365 , '2026-07-19' )
const stalled = await stalledProjects ( db , 365 , '2026-07-19' )
expect ( stalled ) . toHaveLength ( 0 )
expect ( stalled . rows ) . toHaveLength ( 0 )
expect ( stalled . unknownAgeExcluded ) . toBe ( 0 )
} )
} )
it ( 'excludes projects with no pending steps (fully ticked) and inactive/live projects' , async ( ) = > {
it ( 'excludes projects with no pending steps (fully ticked) and inactive/live projects' , async ( ) = > {
@ -50,7 +52,7 @@ describe('stalledProjects (D22 — dashboard/MIS tile)', () => {
await setMilestone ( db , 'u1' , done . id , key , true , '2026-01-01' )
await setMilestone ( db , 'u1' , done . id , key , true , '2026-01-01' )
}
}
const stalled = await stalledProjects ( db , 1 , '2026-07-19' )
const stalled = await stalledProjects ( db , 1 , '2026-07-19' )
expect ( stalled . map( ( p ) = > p . clientModuleId ) ) . not . toContain ( done . id )
expect ( stalled . rows. map( ( p ) = > p . clientModuleId ) ) . not . toContain ( done . id )
// 'live' status (from ticking go_live) also excludes it, which the loop above proves.
// 'live' status (from ticking go_live) also excludes it, which the loop above proves.
expect ( ( await db . get < { status : string } > ( ` SELECT status FROM client_module WHERE id=? ` , done . id ) ) ! . status ) . toBe ( 'live' )
expect ( ( await db . get < { status : string } > ( ` SELECT status FROM client_module WHERE id=? ` , done . id ) ) ! . status ) . toBe ( 'live' )
} )
} )
@ -69,7 +71,8 @@ describe('stalledProjects (D22 — dashboard/MIS tile)', () => {
// to be — this test is about the QUERY logic, not the real-time write path.
// to be — this test is about the QUERY logic, not the real-time write path.
await db . run ( ` UPDATE client_module SET created_at=? WHERE id=? ` , '2026-07-19T09:00:00.000Z' , cm . id )
await db . run ( ` UPDATE client_module SET created_at=? WHERE id=? ` , '2026-07-19T09:00:00.000Z' , cm . id )
const stalled = await stalledProjects ( db , 30 , '2026-07-19' )
const stalled = await stalledProjects ( db , 30 , '2026-07-19' )
expect ( stalled . map ( ( p ) = > p . clientModuleId ) ) . not . toContain ( cm . id )
expect ( stalled . rows . map ( ( p ) = > p . clientModuleId ) ) . not . toContain ( cm . id )
expect ( stalled . unknownAgeExcluded ) . toBe ( 0 )
} )
} )
it ( 'a project created long ago with zero ticks IS stalled' , async ( ) = > {
it ( 'a project created long ago with zero ticks IS stalled' , async ( ) = > {
@ -77,7 +80,7 @@ describe('stalledProjects (D22 — dashboard/MIS tile)', () => {
const cm = await assignModule ( db , 'u1' , { clientId : c.id , module Id : m . id , kind : 'yearly' } )
const cm = await assignModule ( db , 'u1' , { clientId : c.id , module Id : m . id , kind : 'yearly' } )
await db . run ( ` UPDATE client_module SET created_at=? WHERE id=? ` , '2025-01-01T09:00:00.000Z' , cm . id )
await db . run ( ` UPDATE client_module SET created_at=? WHERE id=? ` , '2025-01-01T09:00:00.000Z' , cm . id )
const stalled = await stalledProjects ( db , 30 , '2026-07-19' )
const stalled = await stalledProjects ( db , 30 , '2026-07-19' )
expect ( stalled . map( ( p ) = > p . clientModuleId ) ) . toContain ( cm . id )
expect ( stalled . rows. map( ( p ) = > p . clientModuleId ) ) . toContain ( cm . id )
} )
} )
it ( 'a no-tick project with an UNKNOWN creation date (created_at NULL) is excluded, not assumed ancient' , async ( ) = > {
it ( 'a no-tick project with an UNKNOWN creation date (created_at NULL) is excluded, not assumed ancient' , async ( ) = > {
@ -87,7 +90,18 @@ describe('stalledProjects (D22 — dashboard/MIS tile)', () => {
// doesn't stamp it) — created_at is genuinely unknown.
// doesn't stamp it) — created_at is genuinely unknown.
await db . run ( ` UPDATE client_module SET created_at=NULL WHERE id=? ` , cm . id )
await db . run ( ` UPDATE client_module SET created_at=NULL WHERE id=? ` , cm . id )
const stalled = await stalledProjects ( db , 0 , '2026-07-19' ) // even a 0-day cutoff must not flag it
const stalled = await stalledProjects ( db , 0 , '2026-07-19' ) // even a 0-day cutoff must not flag it
expect ( stalled . map ( ( p ) = > p . clientModuleId ) ) . not . toContain ( cm . id )
expect ( stalled . rows . map ( ( p ) = > p . clientModuleId ) ) . not . toContain ( cm . id )
} )
// FIX 4 (no silent caps): the row above used to just vanish with no trace. It must now be
// counted so the UI can surface "N of unknown age" instead of silently under-reporting.
it ( 'a no-tick row with NULL created_at lands in the unknownAgeExcluded count, not just silently dropped' , async ( ) = > {
const { db , c , m } = await world ( )
const cm = await assignModule ( db , 'u1' , { clientId : c.id , module Id : m . id , kind : 'yearly' } )
await db . run ( ` UPDATE client_module SET created_at=NULL WHERE id=? ` , cm . id )
const stalled = await stalledProjects ( db , 0 , '2026-07-19' )
expect ( stalled . rows . map ( ( p ) = > p . clientModuleId ) ) . not . toContain ( cm . id )
expect ( stalled . unknownAgeExcluded ) . toBe ( 1 )
} )
} )
it ( 'assignModule itself stamps a real (non-null) created_at' , async ( ) = > {
it ( 'assignModule itself stamps a real (non-null) created_at' , async ( ) = > {