testConflictUsingAnotherConflict

tcmts.cli.test.e2e.merge.testConflictUsingAnotherConflict · #jofxx6ah · term

A conflict whose sides merge cleanly but that uses another conflicted name can't typecheck until that's settled: tcm edit says so (not that it "didn't typecheck"), as tcm check does, and editing both puts them in one file, where they check together; saving it resolves both, named so in history.

import { okIn, tcmIn, tempDir } from "#codebase/tcmts/cli/test/e2e/harness";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";

export const testConflictUsingAnotherConflict = () => {
  const dir = tempDir("merge uses conflict");
  const tcm = (...args: string[]) => okIn(dir, ...args);
  const update = (code: string) => {
    fs.writeFileSync(path.join(dir, "scratch.ts"), `// tcm: namespace lib\n${code}\n`);
    tcm("update");
  };
  const k = (a: number) => `export const k = (): number => {\n  const a = ${a};\n  return a;\n};`;
  const m = (x: string, r: string) =>
    `export const m = (): number => {\n  const x = ${x};\n  const y = 1;\n  return ${r};\n};`;
  tcm("init");
  update(`${k(1)}\n${m("k()", "x + y")}`);
  tcm("branch", "s");
  tcm("switch", "s");
  update(`${k(10)}\n${m("k() * 2", "x + y")}`);
  tcm("switch", "main");
  update(`${k(100)}\n${m("k()", "x + y + 1")}`);
  tcm("switch", "s");
  assert.equal(tcmIn(dir, "merge", "main").status, 1);
  const todo = tcm("todo");
  assert.match(todo, /conflict lib\.k/);
  assert.match(todo, /conflict lib\.m/);

  const alone = tcm("edit", "lib.m");
  assert.match(alone, /^wrote lib\.m to lib\.m\.ts$/m);
  assert.match(
    alone,
    /no lines both changed, but it can't typecheck yet: uses lib\.k, which is still conflicted: settle that first .*or edit both together: `tcm edit lib\.m lib\.k`/,
  );
  assert.doesNotMatch(alone, /didn't typecheck/);
  const checked = tcmIn(dir, "check", "lib.m.ts");
  assert.notEqual(checked.status, 0);
  assert.match(checked.out, /lib\.m\.ts uses lib\.k, which is still conflicted/);

  // both, in one file (the one written for lib.m alone, untouched, is replaced)
  assert.match(tcm("edit", "lib.m", "lib.k"), /^wrote lib\.m, lib\.k to lib\.m\.ts$/m);
  const file = path.join(dir, "lib.m.ts");
  const text = fs.readFileSync(file, "utf8");
  assert.doesNotMatch(text, /import \{ k \}/, "what it holds isn't imported");
  assert.match(text, /const x = k\(\) \* 2;[\s\S]*return x \+ y \+ 1;/, "both sides of lib.m");
  fs.writeFileSync(
    file,
    text.replace(/<<<<<<< [\s\S]*>>>>>>> theirs \(main\)\n/, "  const a = 1000;\n"),
  );
  assert.match(tcm("check", "lib.m.ts"), /resolves +lib\.m[\s\S]*resolves +lib\.k/);
  tcm("update", "lib.m.ts");
  assert.doesNotMatch(tcm("todo"), /conflict/);
  assert.equal(tcm("run", "lib.m").trim(), String(1000 * 2 + 1 + 1));
  assert.match(tcm("history"), /resolve lib\.m \(merged\), lib\.k \(merged\)/);
}

uses