testResolveRepointsDependents

tcmts.cli.test.e2e.merge.testResolveRepointsDependents · #7hcxooir · term

The same, with edits that overlap: the class is a conflict, labelled by side. The code using the conflicted class through what's between uses this side's, so the other side's code that needs its own doesn't typecheck: it's to do (not left using versions nothing names). Resolving the conflict to the other side re-derives it, and this side's code, against the one kept.

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 testResolveRepointsDependents = () => {
  const dir = tempDir("merge resolve");
  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");
  };
  tcm("init");
  update(`export class Store {\n  a(): number {\n    return 1;\n  }\n}`);
  update(`import { Store } from "#codebase/lib";
export class Codebase {
  store = new Store();
}
export const render = (cb: Codebase) => cb.store.a();
export const main = () => render(new Codebase());`);
  tcm("branch", "b");
  tcm("branch", "m");
  tcm("switch", "m");

  update(`export class Store {\n  a(): number {\n    return 100;\n  }\n}`);
  tcm("switch", "b");
  update(
    `export class Store {\n  a(): number {\n    return 2;\n  }\n  y(): number {\n    return 3;\n  }\n}`,
  );
  update(
    `import { Codebase } from "#codebase/lib";\nexport const render = (cb: Codebase) => cb.store.a() + cb.store.y();`,
  );
  tcm("switch", "m");

  const merged = tcmIn(dir, "merge", "b");
  assert.equal(merged.status, 1, "a conflict makes merge exit non-zero");
  assert.doesNotMatch(merged.out, /merged as text/);
  const line =
    /conflict lib\.Store: (\S+) \((ours|theirs): (m|b)\) vs (\S+) \((ours|theirs): (m|b)\)/.exec(
      merged.out,
    );
  assert.ok(line, merged.out);
  assert.notEqual(line[2], line[5], "one is each side's");
  const theirs = line[2] === "theirs" ? line[1]! : line[4]!;
  assert.match(merged.out, /no longer typecheck after the merge: .*lib\.render/);
  const todo = tcm("todo");
  assert.match(todo, /conflict lib\.Store/);
  assert.match(todo, /needs fixing lib\.render: Property 'y' does not exist/);
  assert.doesNotMatch(todo, /unnamed version/);

  const resolved = tcm("resolve", "lib.Store", theirs);
  assert.match(resolved, /updated/, "what used main's Store is re-derived against b's");
  assert.equal(tcm("run", "lib.main").trim(), "5");
  const after = tcm("todo");
  assert.doesNotMatch(after, /unnamed version/);
  assert.match(after, /nothing to do/);
}

uses