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  })