testContributeReviewMerge

tcmts.cli.test.e2e.contributions.testContributeReviewMerge · #btyzmkle · term

contribute, review, comment and merge; conflicts are refused until the branch takes the target in

import { codebase, LIB } from "#codebase/tcmts/cli/test/e2e/contributions";
import { tempDir } from "#codebase/tcmts/cli/test/e2e/harness";
import assert from "node:assert/strict";

export const testContributeReviewMerge = () => {
  const root = tempDir("contributions");
  const server = codebase(root, "server");
  server.write(LIB);
  server.tcm("add");
  const me = codebase(root, "me");
  me.tcm("remote", "add", "origin", server.dir);
  me.tcm("pull");

  // contributing needs a branch
  assert.match(me.run("contribute").out, /this is main: make a branch for the change/);
  me.tcm("branch", "faster");
  me.tcm("switch", "faster");
  me.write(`// tcm: namespace lib\nexport const square = (x: number): number => x ** 2;\nexport const cube = (x: number): number => x ** 3;\n`);
  me.tcm("update");
  assert.match(me.tcm("contribute", "--title", "Faster squares", "--description", "Uses ** now."), /opened contribution #1 on origin\n {2}review: tcm contribution view 1/);
  assert.match(me.tcm("contribution"), /#1 +open +Faster squares +faster → main · ada/);

  // reviewed as definitions: the edit, what followed it, what's new
  const view = me.tcm("contribution", "view", "1");
  assert.match(view, /#1 Faster squares {2}open\n {2}faster → main · opened by ada/);
  assert.match(view, /Uses \*\* now\./);
  assert.match(view, /✓ merges as a fast-forward/);
  assert.match(view, /edited\n {4}~ lib\.square[\s\S]*- export const square = \(x: number\): number => x \* x\n {6}\+ export const square = \(x: number\): number => x \*\* 2/);
  assert.match(view, /updated because a dependency changed \(code unchanged\)\n {4}~ lib\.sumSquares/);
  assert.match(view, /added\n {4}\+ lib\.cube/);

  me.tcm("contribution", "comment", "1", "Nice.", "Cube too?", "--about", "lib.cube");
  assert.match(me.tcm("contribution", "view", "1"), /Comments\n {2}ada \S+ \S+ on lib\.cube\n {4}Nice\. Cube too\?/);

  // pushing again updates the same contribution
  me.write(`// tcm: namespace lib\nexport const cube = (x: number): number => x * x * x;\n`);
  me.tcm("update");
  assert.match(me.tcm("contribute"), /updated contribution #1/);

  // merged on the remote, by history: pulling it back is a fast-forward
  assert.match(me.tcm("contribution", "merge", "1"), /merged #1 on origin as #\w{8}/);
  assert.equal(server.tcm("run", "lib.cube", "3").trim(), "27");
  me.tcm("switch", "main");
  assert.match(me.tcm("pull"), /fast-forwarded main to origin\/main/);
  assert.match(me.tcm("contribution", "--all"), /#1 +merged +Faster squares/);
  assert.match(me.tcm("contribution", "view", "1"), /merged into main as #\w{8}[\s\S]*\+ lib\.cube/);
  assert.match(me.run("contribution", "merge", "1").out, /#1 is merged/);

  // two contributions change the same definition: the second conflicts once the first is in
  me.tcm("branch", "a");
  me.tcm("branch", "b");
  me.tcm("switch", "a");
  me.write(`// tcm: namespace lib\nexport const square = (x: number): number => Math.pow(x, 2);\n`);
  me.tcm("update");
  me.tcm("contribute", "--title", "pow");
  me.tcm("switch", "b");
  me.write(`// tcm: namespace lib\nexport const square = (x: number): number => x * x * 1;\n`);
  me.tcm("update");
  me.tcm("contribute", "--title", "times one");
  me.tcm("contribution", "merge", "2");
  assert.match(me.tcm("contribution", "view", "3"), /✗ conflicts with main in lib\.square/);
  const refused = me.run("contribution", "merge", "3");
  assert.equal(refused.status, 1);
  assert.match(refused.out, /#3 conflicts with main in lib\.square: merge main into b \(`tcm pull <remote> main` on that branch\)/);

  // the contributor brings main in, settles the conflict, pushes; now it merges
  me.run("pull", "origin", "main");
  const [first] = [...me.tcm("ls", "lib.square").matchAll(/#([a-z2-7]{8})/g)].map((m) => m[1]);
  me.tcm("resolve", "lib.square", `#${first}`);
  assert.match(me.tcm("contribute", "--title", "times one"), /updated contribution #3/);
  assert.match(me.tcm("contribution", "view", "3"), /✓ merges as a fast-forward/);
  me.tcm("contribution", "close", "3");
  assert.match(me.tcm("contribution", "--all"), /#3 +closed/);
  me.tcm("contribution", "reopen", "3");
  assert.match(me.tcm("contribution", "merge", "3"), /merged #3/);
}

uses