testContributeReviewMerge
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/); }