Blame · Line-by-line history
extension.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 63fe719 | 1 | /** |
| 2 | * Pure unit tests for the git-remote parser. | |
| 3 | * | |
| 4 | * Run with: node --test out/__tests__/extension.test.js | |
| 5 | * (i.e. after `tsc -p .`) — keeps the test runner zero-dependency, matches | |
| 6 | * the rest of the extension's "no extra dev deps" stance. | |
| 7 | */ | |
| 8 | ||
| 9 | import { strict as assert } from "node:assert"; | |
| 10 | import { test } from "node:test"; | |
| 11 | import { | |
| 12 | buildBlobUrl, | |
| 13 | isGluecronRemote, | |
| 14 | parseGitRemote, | |
| 15 | } from "../git"; | |
| 16 | ||
| 17 | test("parseGitRemote: https remote with .git suffix", () => { | |
| 18 | const r = parseGitRemote("https://gluecron.com/ccantynz/Gluecron.com.git"); | |
| 19 | assert.deepEqual(r, { | |
| 20 | owner: "ccantynz", | |
| 21 | repo: "Gluecron.com", | |
| 22 | host: "gluecron.com", | |
| 23 | }); | |
| 24 | }); | |
| 25 | ||
| 26 | test("parseGitRemote: https remote without .git suffix", () => { | |
| 27 | const r = parseGitRemote("https://gluecron.com/acme/widgets"); | |
| 28 | assert.deepEqual(r, { owner: "acme", repo: "widgets", host: "gluecron.com" }); | |
| 29 | }); | |
| 30 | ||
| 31 | test("parseGitRemote: scp-style git@host:owner/repo.git", () => { | |
| 32 | const r = parseGitRemote("git@gluecron.com:ccantynz/Gluecron.com.git"); | |
| 33 | assert.deepEqual(r, { | |
| 34 | owner: "ccantynz", | |
| 35 | repo: "Gluecron.com", | |
| 36 | host: "gluecron.com", | |
| 37 | }); | |
| 38 | }); | |
| 39 | ||
| 40 | test("parseGitRemote: ssh:// URL with port", () => { | |
| 41 | const r = parseGitRemote("ssh://git@gluecron.com:2222/acme/widgets.git"); | |
| 42 | assert.deepEqual(r, { owner: "acme", repo: "widgets", host: "gluecron.com" }); | |
| 43 | }); | |
| 44 | ||
| 45 | test("parseGitRemote: http localhost with port", () => { | |
| 46 | const r = parseGitRemote("http://localhost:3000/me/repo.git"); | |
| 47 | assert.deepEqual(r, { owner: "me", repo: "repo", host: "localhost" }); | |
| 48 | }); | |
| 49 | ||
| 50 | test("parseGitRemote: URL with embedded credentials", () => { | |
| 51 | const r = parseGitRemote("https://user:pat@gluecron.com/acme/widgets.git"); | |
| 52 | assert.deepEqual(r, { owner: "acme", repo: "widgets", host: "gluecron.com" }); | |
| 53 | }); | |
| 54 | ||
| 55 | test("parseGitRemote: nested path prefix takes the last two segments", () => { | |
| 56 | const r = parseGitRemote("https://example.com/git/acme/widgets.git"); | |
| 57 | assert.deepEqual(r, { owner: "acme", repo: "widgets", host: "example.com" }); | |
| 58 | }); | |
| 59 | ||
| 60 | test("parseGitRemote: empty + garbage inputs return null", () => { | |
| 61 | assert.equal(parseGitRemote(""), null); | |
| 62 | assert.equal(parseGitRemote(" "), null); | |
| 63 | assert.equal(parseGitRemote("not-a-url"), null); | |
| 64 | assert.equal(parseGitRemote("https://gluecron.com/justowner"), null); | |
| 65 | // @ts-expect-error — runtime guard for callers that lose types. | |
| 66 | assert.equal(parseGitRemote(null), null); | |
| 67 | // @ts-expect-error | |
| 68 | assert.equal(parseGitRemote(undefined), null); | |
| 69 | }); | |
| 70 | ||
| 71 | test("isGluecronRemote: matches plain hostname", () => { | |
| 72 | assert.equal(isGluecronRemote("gluecron.com", "https://gluecron.com"), true); | |
| 73 | assert.equal(isGluecronRemote("Gluecron.com", "https://gluecron.com"), true); | |
| 74 | assert.equal(isGluecronRemote("gluecron.com", "https://example.com"), false); | |
| 75 | }); | |
| 76 | ||
| 77 | test("isGluecronRemote: ignores port + path on the configured host", () => { | |
| 78 | assert.equal( | |
| 79 | isGluecronRemote("localhost", "http://localhost:3000/some/path"), | |
| 80 | true | |
| 81 | ); | |
| 82 | }); | |
| 83 | ||
| 84 | test("isGluecronRemote: handles a bare hostname (no scheme) config value", () => { | |
| 85 | assert.equal(isGluecronRemote("gluecron.com", "gluecron.com"), true); | |
| 86 | }); | |
| 87 | ||
| 88 | test("buildBlobUrl: composes owner/repo/branch/path", () => { | |
| 89 | const u = buildBlobUrl( | |
| 90 | "https://gluecron.com", | |
| 91 | "acme", | |
| 92 | "widgets", | |
| 93 | "main", | |
| 94 | "src/index.ts" | |
| 95 | ); | |
| 96 | assert.equal(u, "https://gluecron.com/acme/widgets/blob/main/src/index.ts"); | |
| 97 | }); | |
| 98 | ||
| 99 | test("buildBlobUrl: appends 1-indexed line anchor", () => { | |
| 100 | const u = buildBlobUrl( | |
| 101 | "https://gluecron.com", | |
| 102 | "acme", | |
| 103 | "widgets", | |
| 104 | "main", | |
| 105 | "src/index.ts", | |
| 106 | 41 | |
| 107 | ); | |
| 108 | assert.equal( | |
| 109 | u, | |
| 110 | "https://gluecron.com/acme/widgets/blob/main/src/index.ts#L42" | |
| 111 | ); | |
| 112 | }); | |
| 113 | ||
| 114 | test("buildBlobUrl: strips trailing slashes from host + leading slashes from path", () => { | |
| 115 | const u = buildBlobUrl( | |
| 116 | "https://gluecron.com/", | |
| 117 | "acme", | |
| 118 | "widgets", | |
| 119 | "main", | |
| 120 | "/src/index.ts" | |
| 121 | ); | |
| 122 | assert.equal(u, "https://gluecron.com/acme/widgets/blob/main/src/index.ts"); | |
| 123 | }); |