/**
* Grouping is only safe when it hides nothing. These check the rule that
* matters: a file whose changes are not fully explained by the group's
* substitutions must never be folded into it.
*
* Run with `npm test`. TypeScript is stripped by node's type stripping.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { diffLines, withElisions } from "../diff.ts";
import { editsOf, groupFiles, describe, signatureOf } from "../diff-group.ts";
const body = (op, call, extra = "") =>
`pub async fn ${op}(req: Request) -> Result<Response, NetError> {\n` +
` let policy = Policy::default()${extra};\n` +
` ${call}(policy, || transport::${op}(&req)).await\n}\n`;
function editsFor(before, after) {
return editsOf(withElisions(diffLines(before, after), 4));
}
test("a one-token rename reads as a single substitution", () => {
const e = editsFor(body("fetch", "retry"), body("fetch", "retry_with_jitter"));
assert.equal(e.substitutions.length, 1);
assert.equal(describe(e.substitutions[0]), "retry → retry_with_jitter");
assert.ok(e.fullyExplained);
});
test("the same rename in different files shares a signature", () => {
const a = editsFor(body("fetch", "retry"), body("fetch", "retry_with_jitter"));
const b = editsFor(body("push", "retry"), body("push", "retry_with_jitter"));
assert.equal(signatureOf(a), signatureOf(b));
});
test("a file with an extra change is NOT foldable", () => {
const e = editsFor(
body("upload", "retry"),
body("upload", "retry_with_jitter", ".timeout(Duration::from_secs(90))"),
);
// Two substitutions here, not one — so its signature differs from the bulk.
const bulk = editsFor(body("fetch", "retry"), body("fetch", "retry_with_jitter"));
assert.notEqual(signatureOf(e), signatureOf(bulk));
});
test("eight identical renames fold into one group; the odd file stays out", () => {
const files = {};
for (const op of ["fetch", "push", "poll", "stream", "probe", "resolve", "handshake"]) {
files[`src/net/${op}.rs`] = editsFor(body(op, "retry"), body(op, "retry_with_jitter"));
}
files["src/net/upload.rs"] = editsFor(
body("upload", "retry"),
body("upload", "retry_with_jitter", ".timeout(Duration::from_secs(90))"),
);
const paths = Object.keys(files);
const { groups, singles } = groupFiles(paths, (p) => files[p]);
assert.equal(groups.length, 1, "one bulk rename");
assert.equal(groups[0].paths.length, 7, "seven clean files folded");
assert.ok(
singles.includes("src/net/upload.rs"),
"the file with an extra edit must be read on its own",
);
assert.equal(groups[0].substitutions[0].count, 7, "counts sum across the group");
});
test("a pure insertion is never folded", () => {
const e = editsFor("a\nb\n", "a\nNEW\nb\n");
assert.equal(signatureOf(e), null);
});
test("a lone matching file is not a group", () => {
const only = { "x.rs": editsFor(body("fetch", "retry"), body("fetch", "retry_with_jitter")) };
const { groups, singles } = groupFiles(["x.rs"], (p) => only[p]);
assert.equal(groups.length, 0);
assert.deepEqual(singles, ["x.rs"]);
});