| 1 |
// The process wrapper against the real oxy binary on a scratch repository. |
| 2 |
// Runs with `npm test`; needs oxy on PATH, at ~/.local/bin/oxy, or in OXY_BIN. |
| 3 |
|
| 4 |
import { test } from "node:test"; |
| 5 |
import assert from "node:assert/strict"; |
| 6 |
import { mkdtempSync, writeFileSync, mkdirSync } from "node:fs"; |
| 7 |
import { tmpdir } from "node:os"; |
| 8 |
import { join } from "node:path"; |
| 9 |
import { Oxy, findBinary } from "../out/oxy.mjs"; |
| 10 |
|
| 11 |
const binary = process.env.OXY_BIN ?? findBinary(undefined); |
| 12 |
const oxy = new Oxy(binary); |
| 13 |
|
| 14 |
test("root, status, cat, log and commit round trip", async () => { |
| 15 |
const dir = mkdtempSync(join(tmpdir(), "oxy-vscode-")); |
| 16 |
const home = join(dir, "home"); |
| 17 |
mkdirSync(home); |
| 18 |
process.env.OXY_HOME = home; |
| 19 |
await oxy.check(dir, ["init", "repo"]); |
| 20 |
const root = join(dir, "repo"); |
| 21 |
await oxy.check(root, ["identity", "create", "--name", "Test", "--email", "[email protected]"]); |
| 22 |
|
| 23 |
const version = await oxy.version(); |
| 24 |
assert.equal(version.protocol, 1); |
| 25 |
assert.equal(await oxy.root(dir), null, "outside a repository"); |
| 26 |
const info = await oxy.root(root); |
| 27 |
assert.equal(info?.bookmark, "main"); |
| 28 |
assert.ok(info?.root.endsWith("repo")); |
| 29 |
|
| 30 |
writeFileSync(join(root, "a.txt"), "one\n"); |
| 31 |
let status = await oxy.status(root); |
| 32 |
assert.deepEqual(status.paths, [{ path: "a.txt", status: "Added" }]); |
| 33 |
assert.equal(status.clean, false); |
| 34 |
await oxy.commit(root, "first"); |
| 35 |
status = await oxy.status(root); |
| 36 |
assert.equal(status.clean, true); |
| 37 |
|
| 38 |
writeFileSync(join(root, "a.txt"), "one\ntwo\n"); |
| 39 |
assert.equal((await oxy.cat(root, "a.txt")).toString(), "one\n", "the base version"); |
| 40 |
const log = await oxy.log(root, 10); |
| 41 |
assert.equal(log.changes.filter((c) => !c.working_copy).length, 1); |
| 42 |
assert.equal(log.changes.find((c) => !c.working_copy)?.signature?.status, "unsigned"); |
| 43 |
assert.ok((await oxy.prompt(root)).includes("main")); |
| 44 |
const bookmarks = await oxy.bookmarks(root); |
| 45 |
assert.deepEqual(bookmarks.map((b) => [b.name, b.current]), [["main", true]]); |
| 46 |
await assert.rejects(oxy.cat(root, "missing.txt"), /not in that state/); |
| 47 |
|
| 48 |
// Amend folds the edit into the single head. |
| 49 |
await oxy.amend(root, "first, amended"); |
| 50 |
assert.equal((await oxy.status(root)).clean, true); |
| 51 |
assert.ok((await oxy.log(root, 10)).changes.some((c) => c.message === "first, amended")); |
| 52 |
|
| 53 |
// Hunks: commit only b.txt's, leave a.txt's edit, then discard it. |
| 54 |
writeFileSync(join(root, "a.txt"), "one\ntwo\nthree\n"); |
| 55 |
writeFileSync(join(root, "b.txt"), "b\n"); |
| 56 |
const hunks = await oxy.hunks(root); |
| 57 |
assert.ok(hunks.length >= 2, JSON.stringify(hunks)); |
| 58 |
const bHunk = hunks.find((h) => h.path === "b.txt"); |
| 59 |
await oxy.commitHunks(root, "b only", [bHunk.index]); |
| 60 |
status = await oxy.status(root); |
| 61 |
assert.deepEqual(status.paths.map((p) => p.path), ["a.txt"], "a.txt stays uncommitted"); |
| 62 |
assert.equal((await oxy.cat(root, "b.txt")).toString(), "b\n", "committed with its content"); |
| 63 |
await oxy.discard(root, ["a.txt"]); |
| 64 |
assert.equal((await oxy.status(root)).clean, true); |
| 65 |
|
| 66 |
// The operation log, and undo of the last operation (the commit). |
| 67 |
const ops = await oxy.opLog(root); |
| 68 |
assert.ok(ops[0].current && ops.length > 3); |
| 69 |
assert.match(await oxy.undo(root), /Undone/); |
| 70 |
status = await oxy.status(root); |
| 71 |
assert.ok(status.paths.some((p) => p.path === "b.txt"), "the commit is undone; b.txt is uncommitted work again"); |
| 72 |
}); |
| 73 |
|
| 74 |
test("bookmarks, remotes and sync against a local origin", async () => { |
| 75 |
const dir = mkdtempSync(join(tmpdir(), "oxy-vscode-sync-")); |
| 76 |
const home = join(dir, "home"); |
| 77 |
mkdirSync(home); |
| 78 |
process.env.OXY_HOME = home; |
| 79 |
await oxy.check(dir, ["init", "origin"]); |
| 80 |
await oxy.clone(dir, join(dir, "origin"), join(dir, "work")); |
| 81 |
const root = join(dir, "work"); |
| 82 |
writeFileSync(join(root, "a.txt"), "a\n"); |
| 83 |
await oxy.commit(root, "a"); |
| 84 |
const remotes = await oxy.remotes(root); |
| 85 |
assert.equal(remotes[0].name, "origin"); |
| 86 |
let push = await oxy.push(root); |
| 87 |
assert.equal(push[0].ok, true); |
| 88 |
let bookmarks = await oxy.bookmarks(root); |
| 89 |
assert.deepEqual(bookmarks.map((b) => [b.name, b.current, b.ahead, b.behind]), [["main", true, 0, 0]]); |
| 90 |
|
| 91 |
await oxy.bookmarkCreate(root, "feature"); |
| 92 |
await oxy.bookmarkSwitch(root, "feature"); |
| 93 |
writeFileSync(join(root, "a.txt"), "a\nfeature\n"); |
| 94 |
await oxy.commit(root, "feature work"); |
| 95 |
bookmarks = await oxy.bookmarks(root); |
| 96 |
assert.equal(bookmarks.find((b) => b.current)?.name, "feature"); |
| 97 |
await oxy.bookmarkSwitch(root, "main"); |
| 98 |
assert.equal((await oxy.cat(root, "a.txt")).toString(), "a\n"); |
| 99 |
writeFileSync(join(root, "b.txt"), "b\n"); |
| 100 |
await assert.rejects(oxy.bookmarkSwitch(root, "feature"), /has changes/); |
| 101 |
await oxy.commit(root, "b on main"); |
| 102 |
assert.match(await oxy.merge(root, "feature"), /Merged/); |
| 103 |
assert.equal((await oxy.cat(root, "a.txt")).toString(), "a\nfeature\n"); |
| 104 |
push = await oxy.push(root, "origin", "feature"); |
| 105 |
assert.equal(push[0].ok, true); |
| 106 |
push = await oxy.push(root); |
| 107 |
assert.equal(push[0].ok, true); |
| 108 |
const fetch = await oxy.fetch(root); |
| 109 |
assert.equal(fetch.remote, "origin"); |
| 110 |
const pull = await oxy.pull(root); |
| 111 |
assert.equal(pull.changed, false); |
| 112 |
await oxy.bookmarkDelete(root, "feature"); |
| 113 |
bookmarks = await oxy.bookmarks(root); |
| 114 |
assert.deepEqual(bookmarks.map((b) => b.name), ["main"]); |
| 115 |
}); |
| 116 |
|