| 1 | import { afterEach, assert, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { appAtomRegistry } from "../../state/atom-registry"; |
| 4 | import { getCachedNativeReviewDiffData } from "./nativeReviewDiffAdapter"; |
| 5 | import { |
| 6 | getCachedReviewParsedDiff, |
| 7 | getReviewAsyncStateSnapshot, |
| 8 | setReviewAsyncError, |
| 9 | setReviewTurnDiffLoading, |
| 10 | } from "./reviewState"; |
| 11 | |
| 12 | const reviewInput = { |
| 13 | threadKey: "env-local:thread-review", |
| 14 | sectionId: "turn:1", |
| 15 | diff: [ |
| 16 | "diff --git a/src/a.ts b/src/a.ts", |
| 17 | "new file mode 100644", |
| 18 | "--- /dev/null", |
| 19 | "+++ b/src/a.ts", |
| 20 | "@@ -0,0 +1 @@", |
| 21 | "+export const value = 1;", |
| 22 | ].join("\n"), |
| 23 | }; |
| 24 | |
| 25 | afterEach(() => { |
| 26 | appAtomRegistry.reset(); |
| 27 | }); |
| 28 | |
| 29 | it("stores review async loading and error state in atoms", () => { |
| 30 | const threadKey = `env-local:thread-review-state-${Date.now()}`; |
| 31 | |
| 32 | setReviewTurnDiffLoading(threadKey, "turn-1", true); |
| 33 | setReviewAsyncError(threadKey, "load failed"); |
| 34 | |
| 35 | assert.deepStrictEqual(getReviewAsyncStateSnapshot(threadKey), { |
| 36 | loadingTurnIds: { "turn-1": true }, |
| 37 | error: "load failed", |
| 38 | }); |
| 39 | |
| 40 | setReviewTurnDiffLoading(threadKey, "turn-1", false); |
| 41 | setReviewAsyncError(threadKey, null); |
| 42 | |
| 43 | assert.deepStrictEqual(getReviewAsyncStateSnapshot(threadKey), { |
| 44 | loadingTurnIds: {}, |
| 45 | error: null, |
| 46 | }); |
| 47 | }); |
| 48 | |
| 49 | it("reuses unchanged parsed diffs and replaces changed sections", () => { |
| 50 | const parsed = getCachedReviewParsedDiff(reviewInput); |
| 51 | |
| 52 | assert.strictEqual( |
| 53 | getCachedReviewParsedDiff({ ...reviewInput, diff: `\n${reviewInput.diff}\n` }), |
| 54 | parsed, |
| 55 | ); |
| 56 | |
| 57 | const changed = { ...reviewInput, diff: reviewInput.diff.replace("value = 1", "value = 2") }; |
| 58 | const updated = getCachedReviewParsedDiff(changed); |
| 59 | assert.notStrictEqual(updated, parsed); |
| 60 | assert.strictEqual(getCachedReviewParsedDiff(changed), updated); |
| 61 | }); |
| 62 | |
| 63 | it("evicts the least recently used section across threads without changing live results or IDs", () => { |
| 64 | const recentInput = { ...reviewInput, threadKey: "env-local:thread-recent" }; |
| 65 | const oldestInput = { ...reviewInput, threadKey: "env-remote:thread-oldest" }; |
| 66 | const recent = getCachedReviewParsedDiff(recentInput); |
| 67 | const oldest = getCachedReviewParsedDiff(oldestInput); |
| 68 | const native = getCachedNativeReviewDiffData({ parsedDiff: oldest }); |
| 69 | for (let index = 0; index < 6; index += 1) { |
| 70 | getCachedReviewParsedDiff({ ...reviewInput, threadKey: `env-local:thread-${index}` }); |
| 71 | } |
| 72 | |
| 73 | assert.strictEqual(getCachedReviewParsedDiff(recentInput), recent); |
| 74 | getCachedReviewParsedDiff({ ...reviewInput, threadKey: "env-local:thread-new" }); |
| 75 | |
| 76 | assert.strictEqual(getCachedReviewParsedDiff(recentInput), recent); |
| 77 | const rebuilt = getCachedReviewParsedDiff(oldestInput); |
| 78 | assert.notStrictEqual(rebuilt, oldest); |
| 79 | assert.deepStrictEqual(rebuilt, oldest); |
| 80 | assert.strictEqual(getCachedNativeReviewDiffData({ parsedDiff: oldest }), native); |
| 81 | assert.deepStrictEqual(getCachedNativeReviewDiffData({ parsedDiff: rebuilt }), native); |
| 82 | }); |
| 83 | |
| 84 | it("limits cached diffs to 4 Mi source characters before the entry limit", () => { |
| 85 | const firstInput = { ...reviewInput, diff: "x".repeat(2 * 1024 * 1024) }; |
| 86 | const secondInput = { ...firstInput, sectionId: "turn:2" }; |
| 87 | const first = getCachedReviewParsedDiff(firstInput); |
| 88 | const second = getCachedReviewParsedDiff(secondInput); |
| 89 | |
| 90 | getCachedReviewParsedDiff({ ...firstInput, sectionId: "turn:3" }); |
| 91 | |
| 92 | assert.strictEqual(getCachedReviewParsedDiff(secondInput), second); |
| 93 | assert.notStrictEqual(getCachedReviewParsedDiff(firstInput), first); |
| 94 | }); |
| 95 | |
| 96 | it("reclaims the source budget when a cached section changes", () => { |
| 97 | const firstInput = { ...reviewInput, diff: "x".repeat(2 * 1024 * 1024) }; |
| 98 | const secondInput = { ...firstInput, sectionId: "turn:2" }; |
| 99 | getCachedReviewParsedDiff(firstInput); |
| 100 | const second = getCachedReviewParsedDiff(secondInput); |
| 101 | |
| 102 | getCachedReviewParsedDiff({ ...firstInput, diff: null }); |
| 103 | getCachedReviewParsedDiff({ ...firstInput, sectionId: "turn:3" }); |
| 104 | |
| 105 | assert.strictEqual(getCachedReviewParsedDiff(secondInput), second); |
| 106 | }); |
| 107 | |
| 108 | it("counts the full source and skips oversized diffs without evicting cached sections", () => { |
| 109 | const cached = getCachedReviewParsedDiff(reviewInput); |
| 110 | const oversizedInput = { |
| 111 | ...reviewInput, |
| 112 | sectionId: "turn:oversized", |
| 113 | diff: `${reviewInput.diff}${" ".repeat(4 * 1024 * 1024)}`, |
| 114 | }; |
| 115 | const first = getCachedReviewParsedDiff(oversizedInput); |
| 116 | const second = getCachedReviewParsedDiff(oversizedInput); |
| 117 | |
| 118 | assert.notStrictEqual(second, first); |
| 119 | assert.deepStrictEqual(second, first); |
| 120 | assert.strictEqual(getCachedReviewParsedDiff(reviewInput), cached); |
| 121 | }); |
| 122 | |
| 123 | it("drops parsed diffs when the app registry resets", () => { |
| 124 | const parsed = getCachedReviewParsedDiff(reviewInput); |
| 125 | |
| 126 | appAtomRegistry.reset(); |
| 127 | |
| 128 | const rebuilt = getCachedReviewParsedDiff(reviewInput); |
| 129 | assert.notStrictEqual(rebuilt, parsed); |
| 130 | assert.deepStrictEqual(rebuilt, parsed); |
| 131 | }); |