github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/status.test.tsx (about) 1 import { Hold } from "./Hold" 2 import LogStore, { LogAlert, LogAlertIndex } from "./LogStore" 3 import { 4 buildStatus, 5 combinedStatus, 6 PendingBuildDescription, 7 runtimeStatus, 8 } from "./status" 9 import { oneResource } from "./testdata" 10 import { zeroTime } from "./time" 11 import { LogLevel, ResourceStatus, RuntimeStatus, UpdateStatus } from "./types" 12 13 class FakeAlertIndex implements LogAlertIndex { 14 alerts: { [key: string]: LogAlert[] } = {} 15 16 alertsForSpanId(spanId: string): LogAlert[] { 17 return this.alerts[spanId] || [] 18 } 19 } 20 21 function emptyResource() { 22 let res = oneResource({}) 23 res.status!.currentBuild = { startTime: zeroTime } 24 res.status!.buildHistory = [] 25 res.status!.pendingBuildSince = zeroTime 26 res.status!.runtimeStatus = "pending" 27 res.status!.updateStatus = "none" 28 return res 29 } 30 31 describe("combinedStatus", () => { 32 it("pending when no build info", () => { 33 let ls = new LogStore() 34 let res = emptyResource() 35 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 36 ResourceStatus.Pending 37 ) 38 }) 39 40 it("building when current build", () => { 41 let ls = new LogStore() 42 const ts = Date.now().toLocaleString() 43 let res = emptyResource() 44 res.status!.updateStatus = UpdateStatus.InProgress 45 res.status!.runtimeStatus = RuntimeStatus.Ok 46 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 47 ResourceStatus.Building 48 ) 49 }) 50 51 it("healthy when runtime ok", () => { 52 let ls = new LogStore() 53 let res = emptyResource() 54 res.status!.updateStatus = UpdateStatus.Ok 55 res.status!.runtimeStatus = RuntimeStatus.Ok 56 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 57 ResourceStatus.Healthy 58 ) 59 }) 60 61 it("unhealthy when runtime error", () => { 62 let ls = new LogStore() 63 let res = emptyResource() 64 res.status!.updateStatus = UpdateStatus.Ok 65 res.status!.runtimeStatus = RuntimeStatus.Error 66 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 67 ResourceStatus.Unhealthy 68 ) 69 }) 70 71 it("unhealthy when last build error", () => { 72 let ls = new LogStore() 73 let res = emptyResource() 74 res.status!.updateStatus = UpdateStatus.Error 75 res.status!.runtimeStatus = RuntimeStatus.Ok 76 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 77 ResourceStatus.Unhealthy 78 ) 79 }) 80 81 it("building when runtime status error, but also building", () => { 82 let ls = new LogStore() 83 let res = emptyResource() 84 res.status!.updateStatus = UpdateStatus.InProgress 85 res.status!.runtimeStatus = RuntimeStatus.Error 86 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 87 ResourceStatus.Building 88 ) 89 }) 90 91 it("unhealthy when warning and runtime error", () => { 92 let ls = new LogStore() 93 let res = emptyResource() 94 res.status!.runtimeStatus = RuntimeStatus.Error 95 if (!res.status!.k8sResourceInfo) throw new Error("missing k8s info") 96 res.status!.k8sResourceInfo.podRestarts = 1 97 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 98 ResourceStatus.Unhealthy 99 ) 100 }) 101 102 it("warning when container restarts", () => { 103 let ls = new FakeAlertIndex() 104 ls.alerts["pod-span-id"] = [{ level: LogLevel.WARN, lineIndex: 1 }] 105 const ts = Date.now().toLocaleString() 106 let res = emptyResource() 107 res.status!.updateStatus = UpdateStatus.Ok 108 res.status!.runtimeStatus = RuntimeStatus.Ok 109 if (!res.status!.k8sResourceInfo) throw new Error("missing k8s info") 110 res.status!.k8sResourceInfo.podRestarts = 1 111 res.status!.k8sResourceInfo.spanID = "pod-span-id" 112 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 113 ResourceStatus.Warning 114 ) 115 }) 116 117 it("none when n/a runtime status and no builds", () => { 118 let ls = new LogStore() 119 let res = emptyResource() 120 res.status!.updateStatus = UpdateStatus.None 121 res.status!.runtimeStatus = RuntimeStatus.NotApplicable 122 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 123 ResourceStatus.None 124 ) 125 }) 126 127 it("healthy when n/a runtime status and last build succeeded", () => { 128 let ls = new LogStore() 129 let res = emptyResource() 130 res.status!.runtimeStatus = RuntimeStatus.NotApplicable 131 res.status!.updateStatus = UpdateStatus.Ok 132 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 133 ResourceStatus.Healthy 134 ) 135 }) 136 137 it("unhealthy when n/a runtime status and last build failed", () => { 138 let ls = new LogStore() 139 let res = emptyResource() 140 res.status!.runtimeStatus = RuntimeStatus.NotApplicable 141 res.status!.updateStatus = UpdateStatus.Error 142 expect(combinedStatus(buildStatus(res, ls), runtimeStatus(res, ls))).toBe( 143 ResourceStatus.Unhealthy 144 ) 145 }) 146 }) 147 148 describe("PendingBuildDescription", () => { 149 it("shows a generic message if there is no hold", () => { 150 expect(PendingBuildDescription(null)).toBe("Update: pending") 151 }) 152 153 it("shows a generic message if there are no dependencies", () => { 154 let hold = new Hold({ 155 reason: "waiting-for-deploy", 156 on: [], 157 }) 158 expect(PendingBuildDescription(hold)).toBe("Update: pending") 159 }) 160 161 it("shows single image name", () => { 162 let hold = new Hold({ 163 reason: "waiting-for-deploy", 164 on: [{ kind: "ImageMap", name: "gcr.io/foo" }], 165 }) 166 expect(PendingBuildDescription(hold)).toBe( 167 "Update: waiting on image: gcr.io/foo" 168 ) 169 }) 170 171 it("shows single resource name", () => { 172 let hold = new Hold({ 173 reason: "waiting-for-deploy", 174 on: [{ kind: "UIResource", name: "bar" }], 175 }) 176 expect(PendingBuildDescription(hold)).toBe( 177 "Update: waiting on resource: bar" 178 ) 179 }) 180 181 it("shows multiple resource names without overflow", () => { 182 let hold = new Hold({ 183 reason: "waiting-for-deploy", 184 on: [ 185 { kind: "UIResource", name: "foo" }, 186 { kind: "UIResource", name: "bar" }, 187 { kind: "UIResource", name: "baz" }, 188 ], 189 }) 190 expect(PendingBuildDescription(hold)).toBe( 191 "Update: waiting on resources: foo, bar, baz" 192 ) 193 }) 194 195 it("shows multiple image names with overflow", () => { 196 let hold = new Hold({ 197 reason: "waiting-for-deploy", 198 on: ["a", "b", "c", "d", "e"].map((x) => ({ kind: "ImageMap", name: x })), 199 }) 200 expect(PendingBuildDescription(hold)).toBe( 201 "Update: waiting on images: a, b, c, and 2 more" 202 ) 203 }) 204 205 it("shows cluster name", () => { 206 let hold = new Hold({ 207 reason: "waiting-for-cluster", 208 on: [{ kind: "Cluster", name: "default" }], 209 }) 210 expect(PendingBuildDescription(hold)).toBe( 211 "Update: waiting on cluster: default" 212 ) 213 }) 214 215 it("prefers image over resource", () => { 216 let hold = new Hold({ 217 reason: "waiting-for-deploy", 218 on: [ 219 { kind: "UIResource", name: "foo" }, 220 { kind: "ImageMap", name: "bar" }, 221 ], 222 }) 223 expect(PendingBuildDescription(hold)).toBe("Update: waiting on image: bar") 224 }) 225 226 it("gracefully falls back for unknown types", () => { 227 let hold = new Hold({ 228 reason: "waiting-for-deploy", 229 on: [{ kind: "ThisIsNotARealKind", name: "foo" }], 230 }) 231 expect(PendingBuildDescription(hold)).toBe("Update: waiting on 1 object") 232 }) 233 })