Merge branch 'main' into feat/help-all-command

This commit is contained in:
Alex Alecu
2026-04-08 12:07:21 +03:00
70 changed files with 2571 additions and 333 deletions
@@ -1178,3 +1178,414 @@ test("diffFull with whitespace changes", async () => {
},
})
})
// ── Tests for snapshot optimizations (upstream #17878, #20564) ────────
test("concurrent track() calls return consistent results", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
await Snapshot.track()
await Filesystem.write(`${tmp.path}/a.txt`, "concurrent-change")
const results = await Promise.all([
Snapshot.track(),
Snapshot.track(),
Snapshot.track(),
Snapshot.track(),
Snapshot.track(),
])
const hashes = results.filter(Boolean)
expect(hashes.length).toBe(5)
// All concurrent calls must return the same hash
expect(new Set(hashes).size).toBe(1)
},
})
})
test("batch revert with many files sharing same hash", async () => {
await using tmp = await tmpdir({
git: true,
init: async (dir) => {
// Create enough files to trigger batching (>1 per hash)
for (let i = 0; i < 20; i++) {
await Filesystem.write(`${dir}/file${i}.txt`, `original-${i}`)
}
await $`git add .`.cwd(dir).quiet()
await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
// Modify all 20 files
for (let i = 0; i < 20; i++) {
await Filesystem.write(`${tmp.path}/file${i}.txt`, `changed-${i}`)
}
// Add 5 new files
for (let i = 0; i < 5; i++) {
await Filesystem.write(`${tmp.path}/new${i}.txt`, `new-${i}`)
}
const patch = await Snapshot.patch(before!)
expect(patch.files.length).toBe(25)
await Snapshot.revert([patch])
// Modified files should be restored
for (let i = 0; i < 20; i++) {
const content = await Filesystem.readText(`${tmp.path}/file${i}.txt`)
expect(content).toBe(`original-${i}`)
}
// New files should be deleted
for (let i = 0; i < 5; i++) {
expect(
await fs
.access(`${tmp.path}/new${i}.txt`)
.then(() => true)
.catch(() => false),
).toBe(false)
}
},
})
})
test("batch revert with files in nested subdirectories", async () => {
await using tmp = await tmpdir({
git: true,
init: async (dir) => {
await fs.mkdir(`${dir}/a/b/c`, { recursive: true })
await fs.mkdir(`${dir}/d/e`, { recursive: true })
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${dir}/a/b/c/file${i}.txt`, `deep-${i}`)
await Filesystem.write(`${dir}/d/e/file${i}.txt`, `other-${i}`)
}
await $`git add .`.cwd(dir).quiet()
await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${tmp.path}/a/b/c/file${i}.txt`, `modified-${i}`)
await Filesystem.write(`${tmp.path}/d/e/file${i}.txt`, `modified-${i}`)
}
await Snapshot.revert([await Snapshot.patch(before!)])
for (let i = 0; i < 10; i++) {
expect(await Filesystem.readText(`${tmp.path}/a/b/c/file${i}.txt`)).toBe(`deep-${i}`)
expect(await Filesystem.readText(`${tmp.path}/d/e/file${i}.txt`)).toBe(`other-${i}`)
}
},
})
})
test("incremental add skips new files larger than 2MB", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
// Create a file larger than 2MB
const largeContent = "x".repeat(3 * 1024 * 1024)
await Filesystem.write(`${tmp.path}/large.bin`, largeContent)
// Also create a small file
await Filesystem.write(`${tmp.path}/small.txt`, "small change")
const after = await Snapshot.track()
expect(after).toBeTruthy()
const patch = await Snapshot.patch(before!)
// Small file should be tracked, large file should be excluded
const files = patch.files.map((f) => path.basename(f))
expect(files).toContain("small.txt")
expect(files).not.toContain("large.bin")
},
})
})
test("incremental add: tracked files that grow past 2MB are still visible in patch", async () => {
// The 2MB size filter only prevents NEW large files from being added to the
// snapshot index. Already-tracked files that grow past 2MB still appear in
// patch/diff because those compare against the working tree. This matches
// upstream OpenCode behavior.
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
await Filesystem.write(`${tmp.path}/growing.txt`, "small")
const before = await Snapshot.track()
expect(before).toBeTruthy()
await Filesystem.write(`${tmp.path}/growing.txt`, "x".repeat(3 * 1024 * 1024))
await Filesystem.write(`${tmp.path}/a.txt`, "changed")
const patch = await Snapshot.patch(before!)
const files = patch.files.map((f) => path.basename(f))
expect(files).toContain("a.txt")
expect(files).toContain("growing.txt")
},
})
})
test("concurrent patch() calls return consistent results", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
await Filesystem.write(`${tmp.path}/a.txt`, "changed")
const results = await Promise.all([
Snapshot.patch(before!),
Snapshot.patch(before!),
Snapshot.patch(before!),
])
// All should report the same changed files
for (const result of results) {
expect(result.files).toContain(fwd(tmp.path, "a.txt"))
}
},
})
})
test("batch revert with mix of modified, new, and deleted files", async () => {
await using tmp = await tmpdir({
git: true,
init: async (dir) => {
for (let i = 0; i < 15; i++) {
await Filesystem.write(`${dir}/file${i}.txt`, `original-${i}`)
}
await $`git add .`.cwd(dir).quiet()
await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
// Modify some files
for (let i = 0; i < 5; i++) {
await Filesystem.write(`${tmp.path}/file${i}.txt`, `modified-${i}`)
}
// Delete some files
for (let i = 5; i < 10; i++) {
await fs.unlink(`${tmp.path}/file${i}.txt`)
}
// Add new files
for (let i = 0; i < 5; i++) {
await Filesystem.write(`${tmp.path}/added${i}.txt`, `added-${i}`)
}
await Snapshot.revert([await Snapshot.patch(before!)])
// Modified files restored
for (let i = 0; i < 5; i++) {
expect(await Filesystem.readText(`${tmp.path}/file${i}.txt`)).toBe(`original-${i}`)
}
// Deleted files restored
for (let i = 5; i < 10; i++) {
expect(await Filesystem.readText(`${tmp.path}/file${i}.txt`)).toBe(`original-${i}`)
}
// Untouched files unchanged
for (let i = 10; i < 15; i++) {
expect(await Filesystem.readText(`${tmp.path}/file${i}.txt`)).toBe(`original-${i}`)
}
// New files removed
for (let i = 0; i < 5; i++) {
expect(
await fs
.access(`${tmp.path}/added${i}.txt`)
.then(() => true)
.catch(() => false),
).toBe(false)
}
},
})
})
test("batch revert with multiple patches from different snapshots", async () => {
await using tmp = await tmpdir({
git: true,
init: async (dir) => {
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${dir}/file${i}.txt`, `v0-${i}`)
}
await $`git add .`.cwd(dir).quiet()
await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
// Snapshot A
const snapA = await Snapshot.track()
expect(snapA).toBeTruthy()
// Modify first 5 files
for (let i = 0; i < 5; i++) {
await Filesystem.write(`${tmp.path}/file${i}.txt`, `v1-${i}`)
}
// Snapshot B (captures v1 state)
const snapB = await Snapshot.track()
expect(snapB).toBeTruthy()
// Modify last 5 files
for (let i = 5; i < 10; i++) {
await Filesystem.write(`${tmp.path}/file${i}.txt`, `v2-${i}`)
}
// Revert with patches from two different hashes
const patchA = await Snapshot.patch(snapA!)
const patchB = await Snapshot.patch(snapB!)
// patchA covers files 0-4 (changed in v1) + files 5-9 (changed in v2)
// patchB covers files 5-9 (changed in v2)
// Reverting [patchA, patchB]: patchA's hash wins for all files (first seen)
await Snapshot.revert([patchA, patchB])
// All files should be at v0 (snapA's state)
for (let i = 0; i < 10; i++) {
expect(await Filesystem.readText(`${tmp.path}/file${i}.txt`)).toBe(`v0-${i}`)
}
},
})
})
test("concurrent track calls each produce a valid snapshot", async () => {
await using tmp = await tmpdir({
git: true,
init: async (dir) => {
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${dir}/file${i}.txt`, `original-${i}`)
}
await $`git add .`.cwd(dir).quiet()
await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
await Snapshot.track() // warm up
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${tmp.path}/file${i}.txt`, `changed-${i}`)
}
// Fire 5 concurrent tracks, then verify each hash is a usable snapshot
const hashes = (await Promise.all([
Snapshot.track(),
Snapshot.track(),
Snapshot.track(),
Snapshot.track(),
Snapshot.track(),
])).filter(Boolean) as string[]
expect(hashes.length).toBe(5)
// Every hash should produce a valid diffFull against itself (empty diff)
for (const hash of hashes) {
const diff = await Snapshot.diffFull(hash, hash)
expect(diff).toEqual([])
}
// Every hash should be usable for restore without error
await Snapshot.restore(hashes[0]!)
for (let i = 0; i < 10; i++) {
expect(await Filesystem.readText(`${tmp.path}/file${i}.txt`)).toBe(`changed-${i}`)
}
},
})
})
test("track after revert produces clean snapshot", async () => {
await using tmp = await tmpdir({
git: true,
init: async (dir) => {
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${dir}/file${i}.txt`, `original-${i}`)
}
await $`git add .`.cwd(dir).quiet()
await $`git commit --no-gpg-sign -m init`.cwd(dir).quiet()
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
for (let i = 0; i < 10; i++) {
await Filesystem.write(`${tmp.path}/file${i}.txt`, `changed-${i}`)
}
await Snapshot.revert([await Snapshot.patch(before!)])
// After revert, a new track should match the original snapshot
const after = await Snapshot.track()
expect(after).toBeTruthy()
expect(after).toBe(before)
},
})
})
test("incremental add tracks newly created files", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
// Create several new files in subdirectories
await fs.mkdir(`${tmp.path}/newdir/sub`, { recursive: true })
await Filesystem.write(`${tmp.path}/newdir/one.txt`, "one")
await Filesystem.write(`${tmp.path}/newdir/sub/two.txt`, "two")
await Filesystem.write(`${tmp.path}/three.txt`, "three")
const patch = await Snapshot.patch(before!)
const files = patch.files.map((f) => path.basename(f))
expect(files).toContain("one.txt")
expect(files).toContain("two.txt")
expect(files).toContain("three.txt")
},
})
})
test("incremental add tracks modified and deleted files", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
// Modify one file, delete the other
await Filesystem.write(`${tmp.path}/a.txt`, "modified-a")
await fs.unlink(`${tmp.path}/b.txt`)
const patch = await Snapshot.patch(before!)
const files = patch.files.map((f) => path.basename(f))
expect(files).toContain("a.txt")
expect(files).toContain("b.txt")
},
})
})