github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/LogStore.test.ts (about) 1 import { logLinesToString } from "./logs" 2 import LogStore from "./LogStore" 3 4 describe("LogStore", () => { 5 function now() { 6 return new Date().toString() 7 } 8 9 function newGlobalSegment(text: string): Proto.webviewLogSegment { 10 return { text: text, time: now() } 11 } 12 function newGlobalLevelSegment( 13 level: string, 14 text: string 15 ): Proto.webviewLogSegment { 16 return { level: level, text: text, time: now() } 17 } 18 function newManifestSegment( 19 name: string, 20 text: string 21 ): Proto.webviewLogSegment { 22 return { spanId: name, text: text, time: now() } 23 } 24 25 it("handles simple printing", () => { 26 let logs = new LogStore() 27 logs.append({ 28 spans: { "": {} }, 29 segments: [newGlobalSegment("foo"), newGlobalSegment("bar")], 30 }) 31 32 expect(logLinesToString(logs.allLog(), true)).toEqual("foobar") 33 }) 34 35 it("handles caching", () => { 36 let logs = new LogStore() 37 logs.append({ 38 spans: { "": {} }, 39 segments: [newGlobalSegment("foo")], 40 fromCheckpoint: 0, 41 toCheckpoint: 1, 42 }) 43 44 expect(logLinesToString(logs.allLog(), true)).toEqual("foo") 45 expect(logs.lineCache[0].text).toEqual("foo") 46 expect(logs.allLog()[0]).toStrictEqual(logs.allLog()[0]) 47 48 logs.append({ 49 spans: { "": {} }, 50 segments: [newGlobalSegment("bar")], 51 fromCheckpoint: 1, 52 toCheckpoint: 2, 53 }) 54 55 expect(logLinesToString(logs.allLog(), true)).toEqual("foobar") 56 expect(logs.lineCache[0].text).toEqual("foobar") 57 }) 58 59 it("handles changing levels", () => { 60 let logs = new LogStore() 61 logs.append({ 62 spans: { "": {} }, 63 segments: [ 64 newGlobalLevelSegment("INFO", "foo"), 65 newGlobalLevelSegment("DEBUG", "bar"), 66 newGlobalLevelSegment("INFO", "baz"), 67 ], 68 }) 69 70 expect(logLinesToString(logs.allLog(), true)).toEqual("foo\nbar\nbaz") 71 }) 72 73 it("handles prefixes in all logs", () => { 74 let logs = new LogStore() 75 logs.append({ 76 spans: { "": {}, fe: { manifestName: "fe" } }, 77 segments: [ 78 newGlobalSegment("line1\n"), 79 newManifestSegment("fe", "line2\n"), 80 newGlobalSegment("line3\n"), 81 ], 82 }) 83 84 expect(logLinesToString(logs.allLog(), true)).toEqual( 85 "line1\nfe ┊ line2\nline3" 86 ) 87 }) 88 89 it("handles long-prefixes", () => { 90 let logs = new LogStore() 91 logs.append({ 92 spans: { 93 "": {}, 94 "cockroachdb-frontend": { manifestName: "cockroachdb-frontend" }, 95 }, 96 segments: [ 97 newGlobalSegment("line1\n"), 98 newManifestSegment("cockroachdb-frontend", "line2\n"), 99 newGlobalSegment("line3\n"), 100 ], 101 }) 102 103 expect(logLinesToString(logs.allLog(), true)).toEqual( 104 "line1\ncockroachdb…┊ line2\nline3" 105 ) 106 }) 107 108 it("handles manifest logs", () => { 109 let logs = new LogStore() 110 logs.append({ 111 spans: { "": {}, fe: { manifestName: "fe" } }, 112 segments: [ 113 newGlobalSegment("line1\n"), 114 newManifestSegment("fe", "line2\n"), 115 newGlobalSegment("line3\n"), 116 ], 117 }) 118 119 expect(logLinesToString(logs.manifestLog("fe"), false)).toEqual("line2") 120 }) 121 122 it("handles manifest spans with no segments", () => { 123 let logs = new LogStore() 124 logs.append({ 125 spans: { 126 "": {}, 127 fe: { manifestName: "fe" }, 128 foo: { manifestName: "fe" }, 129 }, 130 segments: [ 131 newGlobalSegment("line1\n"), 132 newManifestSegment("fe", "line2\n"), 133 newGlobalSegment("line3\n"), 134 ], 135 }) 136 137 expect(logLinesToString(logs.manifestLog("fe"), false)).toEqual("line2") 138 }) 139 140 it("handles multi-span manifest logs", () => { 141 let logs = new LogStore() 142 logs.append({ 143 spans: { 144 "pod-a": { manifestName: "fe" }, 145 "pod-b": { manifestName: "fe" }, 146 }, 147 segments: [ 148 { spanId: "pod-a", text: "pod-a: line1\n", time: now() }, 149 { spanId: "pod-b", text: "pod-b: line2\n", time: now() }, 150 { spanId: "pod-a", text: "pod-a: line3\n", time: now() }, 151 ], 152 }) 153 154 expect(logLinesToString(logs.manifestLog("fe"), false)).toEqual( 155 "pod-a: line1\npod-b: line2\npod-a: line3" 156 ) 157 expect(logLinesToString(logs.spanLog(["pod-a", "pod-b"]), false)).toEqual( 158 "pod-a: line1\npod-b: line2\npod-a: line3" 159 ) 160 expect(logLinesToString(logs.spanLog(["pod-a"]), false)).toEqual( 161 "pod-a: line1\npod-a: line3" 162 ) 163 expect(logLinesToString(logs.spanLog(["pod-b"]), false)).toEqual( 164 "pod-b: line2" 165 ) 166 expect(logLinesToString(logs.spanLog(["pod-b"]), false)).toEqual( 167 "pod-b: line2" 168 ) 169 expect(logLinesToString(logs.spanLog(["pod-c"]), false)).toEqual("") 170 }) 171 172 it("handles incremental logs", () => { 173 let logs = new LogStore() 174 logs.append({ 175 spans: { "": {} }, 176 segments: [newGlobalSegment("line1\n"), newGlobalSegment("line2\n")], 177 fromCheckpoint: 0, 178 toCheckpoint: 2, 179 }) 180 logs.append({ 181 spans: { "": {} }, 182 segments: [newGlobalSegment("line3\n"), newGlobalSegment("line4\n")], 183 fromCheckpoint: 2, 184 toCheckpoint: 4, 185 }) 186 logs.append({ 187 spans: { "": {} }, 188 segments: [ 189 newGlobalSegment("line4\n"), 190 newGlobalSegment("line4\n"), 191 newGlobalSegment("line5\n"), 192 ], 193 fromCheckpoint: 2, 194 toCheckpoint: 5, 195 }) 196 expect(logLinesToString(logs.allLog(), true)).toEqual( 197 "line1\nline2\nline3\nline4\nline5" 198 ) 199 }) 200 201 it("handles progressLogs interleaved", () => { 202 let logs = new LogStore() 203 logs.append({ 204 spans: { "": {} }, 205 segments: [ 206 { text: "layer 1: Pending\n", fields: { progressID: "layer 1" } }, 207 { text: "layer 2: Pending\n", fields: { progressID: "layer 2" } }, 208 { text: "layer 3: Pending\n", fields: { progressID: "layer 3" } }, 209 ], 210 fromCheckpoint: 0, 211 toCheckpoint: 3, 212 }) 213 214 expect(logLinesToString(logs.allLog(), true)).toEqual( 215 "layer 1: Pending\nlayer 2: Pending\nlayer 3: Pending" 216 ) 217 218 logs.append({ 219 segments: [ 220 { text: "layer 2: Finished\n", fields: { progressID: "layer 2" } }, 221 ], 222 fromCheckpoint: 3, 223 toCheckpoint: 4, 224 }) 225 226 expect(logLinesToString(logs.allLog(), true)).toEqual( 227 "layer 1: Pending\nlayer 2: Finished\nlayer 3: Pending" 228 ) 229 }) 230 231 it("handles progressLogs adjacent", () => { 232 let logs = new LogStore() 233 logs.append({ 234 spans: { "": {} }, 235 segments: [ 236 { text: "layer 1: Pending\n", fields: { progressID: "layer 1" } }, 237 ], 238 fromCheckpoint: 0, 239 toCheckpoint: 1, 240 }) 241 242 expect(logLinesToString(logs.allLog(), true)).toEqual("layer 1: Pending") 243 244 logs.append({ 245 segments: [ 246 { text: "layer 1: Finished\n", fields: { progressID: "layer 1" } }, 247 ], 248 fromCheckpoint: 1, 249 toCheckpoint: 2, 250 }) 251 252 expect(logLinesToString(logs.allLog(), true)).toEqual("layer 1: Finished") 253 }) 254 255 it("handles last trace", () => { 256 let logs = new LogStore() 257 logs.append({ 258 spans: { 259 "": {}, 260 "build:1": { manifestName: "fe" }, 261 "pod:1": { manifestName: "fe" }, 262 "build:2": { manifestName: "foo" }, 263 "pod:2": { manifestName: "foo" }, 264 }, 265 segments: [ 266 newManifestSegment("build:1", "build 1\n"), 267 newManifestSegment("pod:1", "pod 1\n"), 268 newManifestSegment("build:2", "build 2\n"), 269 newManifestSegment("pod:2", "pod 2\n"), 270 newManifestSegment("pod:1", "pod 1 line 2\n"), 271 ], 272 }) 273 274 expect(logLinesToString(logs.traceLog("build:1"), false)).toEqual( 275 "build 1\npod 1\npod 1 line 2" 276 ) 277 }) 278 279 it("handles trace ends at next build", () => { 280 let logs = new LogStore() 281 logs.append({ 282 spans: { 283 "": {}, 284 "build:1": { manifestName: "fe" }, 285 "pod:1": { manifestName: "fe" }, 286 "build:2": { manifestName: "fe" }, 287 "pod:2": { manifestName: "fe" }, 288 }, 289 segments: [ 290 newManifestSegment("build:1", "build 1\n"), 291 newManifestSegment("pod:1", "pod 1\n"), 292 newManifestSegment("build:2", "build 2\n"), 293 newManifestSegment("pod:2", "pod 2\n"), 294 ], 295 }) 296 297 expect(logLinesToString(logs.traceLog("build:1"), false)).toEqual( 298 "build 1\npod 1" 299 ) 300 }) 301 302 it("handles incremental logs", () => { 303 let logs = new LogStore() 304 logs.append({ 305 spans: { 306 "": {}, 307 "build:1": { manifestName: "fe" }, 308 }, 309 segments: [ 310 newManifestSegment("build:1", "build 1\n"), 311 newManifestSegment("build:1", "build 2\n"), 312 newManifestSegment("build:1", "build 3\n"), 313 newGlobalSegment("global line 1\n"), 314 ], 315 fromCheckpoint: 0, 316 toCheckpoint: 4, 317 }) 318 319 let patch = logs.manifestLogPatchSet("fe", 0) 320 expect(logLinesToString(patch.lines, false)).toEqual( 321 "build 1\nbuild 2\nbuild 3" 322 ) 323 324 logs.append({ 325 spans: { 326 "": {}, 327 "build:1": { manifestName: "fe" }, 328 }, 329 segments: [ 330 newGlobalSegment("global line 2\n"), 331 newManifestSegment("build:1", "build 4\n"), 332 newManifestSegment("build:1", "build 5\n"), 333 newManifestSegment("build:1", "build 6\n"), 334 ], 335 fromCheckpoint: 4, 336 toCheckpoint: 8, 337 }) 338 339 let patch3 = logs.manifestLogPatchSet("fe", patch.checkpoint) 340 expect(logLinesToString(patch3.lines, false)).toEqual( 341 "build 4\nbuild 5\nbuild 6" 342 ) 343 }) 344 345 it("handles incremental logs two spans", () => { 346 let logs = new LogStore() 347 logs.append({ 348 spans: { 349 "build:1": { manifestName: "fe" }, 350 }, 351 segments: [ 352 newManifestSegment("build:1", "build 1\n"), 353 newManifestSegment("build:1", "build 2\n"), 354 newManifestSegment("build:1", "build 3\n"), 355 ], 356 fromCheckpoint: 0, 357 toCheckpoint: 3, 358 }) 359 360 let patch = logs.manifestLogPatchSet("fe", 0) 361 expect(logLinesToString(patch.lines, false)).toEqual( 362 "build 1\nbuild 2\nbuild 3" 363 ) 364 365 logs.append({ 366 spans: { 367 "build:2": { manifestName: "fe" }, 368 }, 369 segments: [ 370 newManifestSegment("build:2", "build 4\n"), 371 newManifestSegment("build:2", "build 5\n"), 372 newManifestSegment("build:2", "build 6\n"), 373 ], 374 fromCheckpoint: 3, 375 toCheckpoint: 6, 376 }) 377 378 let patch2 = logs.manifestLogPatchSet("fe", patch.checkpoint) 379 expect(logLinesToString(patch2.lines, false)).toEqual( 380 "build 4\nbuild 5\nbuild 6" 381 ) 382 }) 383 384 it("handles incremental logs continuation", () => { 385 let logs = new LogStore() 386 logs.append({ 387 spans: { 388 "": {}, 389 "build:1": { manifestName: "fe" }, 390 }, 391 segments: [ 392 newManifestSegment("build:1", "build 1\n"), 393 newManifestSegment("build:1", "build 2\n"), 394 newManifestSegment("build:1", "build ..."), 395 newGlobalSegment("global line 1\n"), 396 ], 397 fromCheckpoint: 0, 398 toCheckpoint: 4, 399 }) 400 401 let patch = logs.manifestLogPatchSet("fe", 0) 402 expect(logLinesToString(patch.lines, false)).toEqual( 403 "build 1\nbuild 2\nbuild ..." 404 ) 405 406 let patch2 = logs.manifestLogPatchSet("fe", patch.checkpoint) 407 expect(patch2.lines).toEqual([]) 408 409 logs.append({ 410 spans: { 411 "": {}, 412 "build:1": { manifestName: "fe" }, 413 }, 414 segments: [ 415 newGlobalSegment("global line 2\n"), 416 newManifestSegment("build:1", "... 3\n"), 417 newManifestSegment("build:1", "build 4\n"), 418 newManifestSegment("build:1", "build 5\n"), 419 ], 420 fromCheckpoint: 4, 421 toCheckpoint: 8, 422 }) 423 424 let patch3 = logs.manifestLogPatchSet("fe", patch.checkpoint) 425 expect(logLinesToString(patch3.lines, false)).toEqual( 426 "build ...... 3\nbuild 4\nbuild 5" 427 ) 428 }) 429 430 it("truncates output for snapshots", () => { 431 let logs = new LogStore() 432 433 logs.append({ 434 spans: { 435 "build:1": { manifestName: "fe" }, 436 }, 437 segments: [newManifestSegment("build:1", "build 1\n")], 438 fromCheckpoint: 0, 439 toCheckpoint: 1, 440 }) 441 442 logs.append({ 443 spans: { 444 "build:2": { manifestName: "be" }, 445 }, 446 segments: [ 447 newManifestSegment("build:2", "build 2\n"), 448 newManifestSegment("build:2", "build 3\n"), 449 ], 450 fromCheckpoint: 1, 451 toCheckpoint: 3, 452 }) 453 454 logs.append({ 455 spans: { 456 "": {}, 457 }, 458 segments: [newGlobalSegment("global line 1\n")], 459 fromCheckpoint: 3, 460 toCheckpoint: 4, 461 }) 462 463 const logList = logs.toLogList(28) 464 // log should be truncated 465 expect(logList.segments?.length).toEqual(2) 466 // order should be preserved 467 expect(logList.segments![0].text).toEqual("build 3\n") 468 expect(logList.segments![1].text).toEqual("global line 1\n") 469 470 // only spans referenced by segments in the truncated output should exist 471 const spans = logList.spans as { [key: string]: Proto.webviewLogSpan } 472 expect(Object.keys(spans).length).toEqual(2) 473 expect(spans["build:2"].manifestName).toEqual("be") 474 expect(spans["_"].manifestName).toEqual("") 475 }) 476 477 it("removes manifests", () => { 478 let logs = new LogStore() 479 480 logs.append({ 481 spans: { 482 "build:1": { manifestName: "keep" }, 483 "build:2": { manifestName: "purge" }, 484 "": {}, 485 }, 486 segments: [ 487 newGlobalSegment("global line 1\n"), 488 newManifestSegment("build:1", "start of line - "), 489 newManifestSegment("build:2", "build 2\n"), 490 newManifestSegment("build:1", "middle of line - "), 491 newManifestSegment("build:2", "build 3\n"), 492 newManifestSegment("build:1", "end of line\n"), 493 ], 494 fromCheckpoint: 0, 495 toCheckpoint: 4, 496 }) 497 498 logs.removeSpans(["build:2"]) 499 expect(logs.segments).toHaveLength(4) 500 expect(logs.segmentToLine).toHaveLength(4) 501 expect(Object.keys(logs.spans).sort()).toEqual(["_", "build:1"]) 502 503 expect(logLinesToString(logs.allLog(), true)).toEqual( 504 "global line 1\nkeep ┊ start of line - middle of line - end of line" 505 ) 506 expect(logLinesToString(logs.manifestLog("be"), false)).toEqual("") 507 }) 508 509 it("weights on recently and length", () => { 510 let logs = new LogStore() 511 expect( 512 logs.heaviestManifestName({ 513 a: { name: "a", byteCount: 100, start: "2020-04" }, 514 b: { name: "b", byteCount: 100, start: "2021-04" }, 515 }) 516 ).toEqual("a") 517 518 expect( 519 logs.heaviestManifestName({ 520 a: { name: "a", byteCount: 100, start: "2020-04" }, 521 b: { name: "b", byteCount: 1000, start: "2021-04" }, 522 }) 523 ).toEqual("b") 524 525 expect( 526 logs.heaviestManifestName({ 527 a: { name: "a", byteCount: 100, start: "2020-04" }, 528 b: { name: "b", byteCount: 150, start: "2021-04" }, 529 }) 530 ).toEqual("a") 531 }) 532 533 it("truncates output for snapshots", () => { 534 let logs = new LogStore() 535 logs.maxLogLength = 40 536 537 logs.append({ 538 spans: { 539 "build:1": { manifestName: "fe" }, 540 }, 541 segments: [newManifestSegment("build:1", "build 1\n")], 542 fromCheckpoint: 0, 543 toCheckpoint: 1, 544 }) 545 546 logs.append({ 547 spans: { 548 "build:2": { manifestName: "be" }, 549 }, 550 segments: [ 551 newManifestSegment("build:2", "build 2\n"), 552 newManifestSegment("build:2", "build 3\n"), 553 newManifestSegment("build:2", "build 4\n"), 554 newManifestSegment("build:2", "build 5\n"), 555 newManifestSegment("build:2", "build 6\n"), 556 newManifestSegment("build:2", "build 7\n"), 557 ], 558 fromCheckpoint: 1, 559 toCheckpoint: 7, 560 }) 561 562 logs.append({ 563 spans: { 564 "": {}, 565 }, 566 segments: [newGlobalSegment("global line 1\n")], 567 fromCheckpoint: 7, 568 toCheckpoint: 8, 569 }) 570 571 expect(logLinesToString(logs.allLog(), true)).toEqual( 572 "fe ┊ build 1\nbe ┊ build 7\nglobal line 1" 573 ) 574 }) 575 576 it("handles starred resource logs", () => { 577 let logs = new LogStore() 578 logs.append({ 579 spans: { 580 "": {}, 581 "build:1": { manifestName: "res1" }, 582 "build:2": { manifestName: "res2" }, 583 "build:3": { manifestName: "res3" }, 584 }, 585 segments: [ 586 newManifestSegment("build:1", "build 1\n"), 587 newManifestSegment("build:2", "build 2\n"), 588 newManifestSegment("build:3", "build 3\n"), 589 ], 590 fromCheckpoint: 0, 591 toCheckpoint: 3, 592 }) 593 594 let patch = logs.starredLogPatchSet(["res1", "res2"], 0) 595 expect(logLinesToString(patch.lines, false)).toEqual("build 1\nbuild 2") 596 597 logs.append({ 598 spans: { 599 "": {}, 600 "build:1": { manifestName: "res1" }, 601 "build:2": { manifestName: "res2" }, 602 "build:3": { manifestName: "res3" }, 603 }, 604 segments: [ 605 newManifestSegment("build:1", "build 4\n"), 606 newManifestSegment("build:2", "build 5\n"), 607 newManifestSegment("build:3", "build 6\n"), 608 ], 609 fromCheckpoint: 3, 610 toCheckpoint: 6, 611 }) 612 613 let patch3 = logs.starredLogPatchSet(["res1", "res2"], patch.checkpoint) 614 expect(logLinesToString(patch3.lines, false)).toEqual("build 4\nbuild 5") 615 }) 616 })