| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import type { RepoSettings } from "@g1t/contracts"; |
| 5 | |
| 6 | import { |
| 7 | type PullCommit, |
| 8 | type TemplatePull, |
| 9 | SAMPLE_PULL, |
| 10 | allowedMethods, |
| 11 | choicesFromSettings, |
| 12 | defaultMessage, |
| 13 | fillTemplate, |
| 14 | isMergeMethod, |
| 15 | splitMessage, |
| 16 | } from "./merge.ts"; |
| 17 | |
| 18 | // The same inputs as services/work/src/merge_message.rs's tests, which must |
| 19 | // give the same outputs: the settings preview and the server agree. |
| 20 | |
| 21 | const commit = (message: string, email: string): PullCommit => ({ message, authorEmail: email }); |
| 22 | |
| 23 | const facts = (commits: PullCommit[] = [], issues: number[] = []): TemplatePull => ({ |
| 24 | number: 12, |
| 25 | title: "Add forks ", |
| 26 | body: "Forks are ordinary repositories.\n", |
| 27 | branch: "forks", |
| 28 | headOwner: "ana", |
| 29 | headRepo: "ana/g1t", |
| 30 | base: "main", |
| 31 | author: "ana", |
| 32 | commits, |
| 33 | issues, |
| 34 | }); |
| 35 | |
| 36 | const asPair = (text: { title: string; message: string }) => [text.title, text.message]; |
| 37 | |
| 38 | test("a merge commit names where it came from by default", () => { |
| 39 | assert.deepEqual(asPair(defaultMessage("merge", "default", null, facts())), ["Merge pull request #12 from ana/forks", "Add forks"]); |
| 40 | assert.deepEqual(asPair(defaultMessage("merge", "pull_title", null, facts())), ["Add forks (#12)", ""]); |
| 41 | assert.deepEqual(asPair(defaultMessage("merge", "pull_title_and_description", null, facts())), [ |
| 42 | "Add forks (#12)", |
| 43 | "Forks are ordinary repositories.", |
| 44 | ]); |
| 45 | // Listing commits is for squashes: a merge falls back to its default. |
| 46 | assert.deepEqual(defaultMessage("merge", "pull_title_and_commits", null, facts()), defaultMessage("merge", "default", null, facts())); |
| 47 | }); |
| 48 | |
| 49 | test("a squash lists its commits when it has more than one", () => { |
| 50 | const one = [commit("Add forks\n\nLong story.", "ana@example.com")]; |
| 51 | assert.deepEqual(asPair(defaultMessage("squash", "default", null, facts(one))), ["Add forks (#12)", ""]); |
| 52 | const two = [commit("Add forks\n\nLong story.", "ana@example.com"), commit("Fix the test", "bo@example.com")]; |
| 53 | assert.deepEqual(asPair(defaultMessage("squash", "default", null, facts(two))), ["Add forks (#12)", "* Add forks\n* Fix the test"]); |
| 54 | assert.deepEqual(asPair(defaultMessage("squash", "pull_title_and_commits", null, facts(two))), [ |
| 55 | "Add forks (#12)", |
| 56 | "Add forks\n\nLong story.\n\nFix the test", |
| 57 | ]); |
| 58 | assert.equal(defaultMessage("squash", "pull_title_and_description", null, facts(two)).message, "Forks are ordinary repositories."); |
| 59 | }); |
| 60 | |
| 61 | test("a template is filled in once", () => { |
| 62 | const commits = [commit("One", "ana@example.com"), commit("Two", "Ana@Example.com"), commit("Three", "bo@example.com")]; |
| 63 | const template = |
| 64 | "{title} (#{number}) into {base}\n\n{body}\nFrom {head_repo}:{branch} by {author}\n{commits}\nCloses {issues}\n{co_authors}\n{unknown} {title"; |
| 65 | const { title, message } = defaultMessage("squash", "custom", template, facts(commits, [3, 4])); |
| 66 | assert.equal(title, "Add forks (#12) into main"); |
| 67 | assert.equal( |
| 68 | message, |
| 69 | "Forks are ordinary repositories.\nFrom ana/g1t:forks by ana\n* One\n* Two\n* Three\nCloses #3, #4\nCo-authored-by: ana <ana@example.com>\nCo-authored-by: bo <bo@example.com>\n{unknown} {title", |
| 70 | ); |
| 71 | // A value holding a placeholder is not filled in again. |
| 72 | assert.equal(defaultMessage("merge", "custom", "{title}", { ...facts(), title: "{body}" }).title, "{body}"); |
| 73 | // A template that fills in nothing falls back to the default. |
| 74 | assert.equal(defaultMessage("merge", "custom", "{body}", { ...facts(), body: "" }).title, "Merge pull request #12 from ana/forks"); |
| 75 | assert.deepEqual(defaultMessage("squash", "custom", " ", facts()), defaultMessage("squash", "default", null, facts())); |
| 76 | assert.deepEqual(defaultMessage("squash", "custom", null, facts()), defaultMessage("squash", "default", null, facts())); |
| 77 | }); |
| 78 | |
| 79 | test("a message splits into title and body", () => { |
| 80 | assert.deepEqual(asPair(splitMessage("Title\n\n\nBody\n")), ["Title", "Body"]); |
| 81 | assert.deepEqual(asPair(splitMessage(" Title ")), ["Title", ""]); |
| 82 | }); |
| 83 | |
| 84 | test("the sample pull request previews as the server writes it", () => { |
| 85 | assert.deepEqual(asPair(defaultMessage("merge", "default", null, SAMPLE_PULL)), [ |
| 86 | "Merge pull request #128 from acme/webhook-retries", |
| 87 | "Retry failed webhook deliveries", |
| 88 | ]); |
| 89 | const filled = fillTemplate("{title} (#{number})\n\n{body}\n\nCloses {issues}\n{co_authors}\n{nope}", SAMPLE_PULL); |
| 90 | assert.equal(filled.title, "Retry failed webhook deliveries (#128)"); |
| 91 | assert.equal( |
| 92 | filled.message, |
| 93 | "Deliveries that time out are retried three times, with backoff.\n\nCloses #97\nCo-authored-by: ana <ana@example.com>\nCo-authored-by: ben <ben@example.com>\n{nope}", |
| 94 | ); |
| 95 | }); |
| 96 | |
| 97 | test("choices from settings leave the message to the service", () => { |
| 98 | const settings = { |
| 99 | allowMergeCommit: true, |
| 100 | allowSquashMerge: true, |
| 101 | allowRebaseMerge: false, |
| 102 | allowFastForward: false, |
| 103 | defaultMergeMethod: "squash", |
| 104 | allowAutoMerge: false, |
| 105 | deleteBranchOnMerge: false, |
| 106 | suggestUpdatingBranches: true, |
| 107 | } as unknown as RepoSettings; |
| 108 | const choices = choicesFromSettings(settings, false); |
| 109 | assert.deepEqual(choices.methods, ["merge", "squash"]); |
| 110 | assert.equal(choices.defaultMethod, "squash"); |
| 111 | assert.deepEqual(choices.messages, {}); |
| 112 | }); |
| 113 | |
| 114 | test("the allowed methods follow the settings, fast-forward included", () => { |
| 115 | assert.deepEqual( |
| 116 | allowedMethods({ allowMergeCommit: false, allowSquashMerge: true, allowRebaseMerge: false, allowFastForward: true }), |
| 117 | ["squash", "fast_forward"], |
| 118 | ); |
| 119 | assert.ok(isMergeMethod("fast_forward")); |
| 120 | assert.ok(!isMergeMethod("octopus")); |
| 121 | }); |