| 1 | import assert from "node:assert/strict"; |
| 2 | import { sessionCatalogNotice } from "../lib/sessionCatalogPresentation"; |
| 3 | import type { SessionCatalogStatus } from "../lib/sessionCatalogTypes"; |
| 4 | |
| 5 | const status = (overrides: Partial<SessionCatalogStatus> = {}): SessionCatalogStatus => ({ |
| 6 | state: "ready", |
| 7 | revision: 1, |
| 8 | indexed: 10, |
| 9 | total: 10, |
| 10 | repairPending: 0, |
| 11 | canRebuild: false, |
| 12 | ...overrides, |
| 13 | }); |
| 14 | |
| 15 | assert.deepEqual( |
| 16 | sessionCatalogNotice(status({ state: "opening", indexed: 0, total: 0 })), |
| 17 | "indexing", |
| 18 | "opening without a known total uses the generic working label", |
| 19 | ); |
| 20 | assert.deepEqual( |
| 21 | sessionCatalogNotice(status({ state: "rebuilding", indexed: 7, total: 10, canRebuild: true })), |
| 22 | "indexing", |
| 23 | "rebuilding preserves known progress and never offers another rebuild", |
| 24 | ); |
| 25 | assert.deepEqual( |
| 26 | sessionCatalogNotice(status({ repairPending: 1, canRebuild: true })), |
| 27 | "repair-active", |
| 28 | "older backends map their aggregate repair backlog to active repair", |
| 29 | ); |
| 30 | assert.equal( |
| 31 | sessionCatalogNotice(status({ repairPending: 3, repairActive: 0, repairDeferred: 3, repairBlocked: 0 })), |
| 32 | "repair-deferred", |
| 33 | "deferred repair is static and does not reuse indexing progress", |
| 34 | ); |
| 35 | assert.equal( |
| 36 | sessionCatalogNotice(status({ repairPending: 2, repairActive: 0, repairDeferred: 0, repairBlocked: 2 })), |
| 37 | "repair-blocked", |
| 38 | "blocked repair is distinct from active work", |
| 39 | ); |
| 40 | assert.equal( |
| 41 | sessionCatalogNotice(status({ repairPending: 4, repairActive: 1, repairDeferred: 2, repairBlocked: 1 })), |
| 42 | "repair-active", |
| 43 | "active work takes precedence while a mixed backlog is running", |
| 44 | ); |
| 45 | assert.equal( |
| 46 | sessionCatalogNotice(status({ state: "degraded", canRebuild: true, repairActive: 2, repairDeferred: 1 })), |
| 47 | "rebuild", |
| 48 | "global degraded state takes precedence over repair activity", |
| 49 | ); |
| 50 | assert.equal( |
| 51 | sessionCatalogNotice(status({ state: "degraded", canRebuild: true, unindexedTargetCount: 1, repairActive: 2 })), |
| 52 | "rebuild", |
| 53 | "global degraded state takes precedence over unindexed targets", |
| 54 | ); |
| 55 | assert.equal( |
| 56 | sessionCatalogNotice(status({ lastError: "private backend detail", repairBlocked: 3 })), |
| 57 | "failed", |
| 58 | "global catalog failure takes precedence over blocked rows", |
| 59 | ); |
| 60 | assert.equal( |
| 61 | sessionCatalogNotice(status({ lastError: "private backend detail", unindexedTargetCount: 1 })), |
| 62 | "failed", |
| 63 | "global catalog failure takes precedence over unindexed targets", |
| 64 | ); |
| 65 | assert.equal( |
| 66 | sessionCatalogNotice(status({ state: "rebuilding", lastError: "previous failure", repairActive: 1 })), |
| 67 | "indexing", |
| 68 | "an explicit rebuild remains the authoritative working state", |
| 69 | ); |
| 70 | assert.equal(sessionCatalogNotice(status()), null, "healthy ready catalogs render no notice"); |
| 71 | assert.equal( |
| 72 | sessionCatalogNotice(status({ unindexedTargetCount: 1, repairActive: 2 })), |
| 73 | "indexing", |
| 74 | "unindexed targets take precedence over row repair", |
| 75 | ); |
| 76 | assert.deepEqual( |
| 77 | sessionCatalogNotice(status({ state: "degraded", canRebuild: true })), |
| 78 | "rebuild", |
| 79 | "a degraded catalog can expose the explicit repair action", |
| 80 | ); |
| 81 | assert.deepEqual( |
| 82 | sessionCatalogNotice(status({ lastError: "private backend detail", canRebuild: true })), |
| 83 | "rebuild", |
| 84 | "backend errors select a generic failed presentation without exposing the detail", |
| 85 | ); |
| 86 | assert.deepEqual( |
| 87 | sessionCatalogNotice(status({ state: "degraded", canRebuild: undefined })), |
| 88 | "failed", |
| 89 | "older backends without canRebuild fail closed", |
| 90 | ); |
| 91 | |
| 92 | console.log("session catalog presentation tests passed"); |
| 93 |