import { describe, expect, test } from "bun:test"; import { gitUrlToPath, parseDeepLink, pickRoomMatch, repoKey, resolveDevTarget, resolveRepoTarget, shareableDeepLink, toPosixPath } from "./repo"; import type { PeerInfo, WorkspaceInfo } from "./signaling"; import { parseGitRemote } from "../cli/git"; describe("toPosixPath", () => { // VS Code web's ?folder= on Windows wants the file-URI authority form // (/C:/ws), NOT git-bash /c/ws — the latter reports "workspace does not exist". test("Windows drive path -> /:/... (file-URI form)", () => { expect(toPosixPath("C:\\ws")).toBe("/C:/ws"); expect(toPosixPath("C:\\Users\\taku")).toBe("/C:/Users/taku"); }); test("preserves drive-letter case (drive is case-insensitive on Windows)", () => { expect(toPosixPath("D:\\foo")).toBe("/D:/foo"); expect(toPosixPath("c:\\ws")).toBe("/c:/ws"); }); test("drive root collapses to /: (no trailing slash)", () => { expect(toPosixPath("C:\\")).toBe("/C:"); expect(toPosixPath("C:")).toBe("/C:"); }); test("forward-slash Windows paths normalize too", () => { expect(toPosixPath("C:/ws")).toBe("/C:/ws"); }); test("POSIX absolute paths are unchanged (mac/linux not broken)", () => { expect(toPosixPath("/Users/sno/ws")).toBe("/Users/sno/ws"); expect(toPosixPath("/home/x/proj")).toBe("/home/x/proj"); expect(toPosixPath("/")).toBe("/"); }); test("already-normalized path is idempotent", () => { expect(toPosixPath("/C:/ws")).toBe("/C:/ws"); }); test("trims trailing backslashes/slashes on a drive path", () => { expect(toPosixPath("C:\\ws\\")).toBe("/C:/ws"); }); // Regression: the value VS Code web receives via ?folder= (URL-decoded) must // be exactly /C:/ws so serve-web resolves it to the real C:\ws on disk. test("?folder= round-trip: encode(toPosixPath) decodes back to /C:/ws", () => { const folderParam = encodeURIComponent(toPosixPath("C:\\ws")); expect(folderParam).toBe("%2FC%3A%2Fws"); expect(decodeURIComponent(folderParam)).toBe("/C:/ws"); }); }); describe("parseGitRemote", () => { test("GitHub https / ssh / git@ -> host-agnostic key", () => { expect(parseGitRemote("https://github.com/snomiao/codehost.git")).toBe("github.com/snomiao/codehost"); expect(parseGitRemote("git@github.com:snomiao/codehost.git")).toBe("github.com/snomiao/codehost"); expect(parseGitRemote("ssh://git@github.com/snomiao/codehost")).toBe("github.com/snomiao/codehost"); }); test("other hosts work too (gitlab, bitbucket, self-hosted with port)", () => { expect(parseGitRemote("https://gitlab.com/group/proj.git")).toBe("gitlab.com/group/proj"); expect(parseGitRemote("git@bitbucket.org:team/repo.git")).toBe("bitbucket.org/team/repo"); expect(parseGitRemote("ssh://git@git.company.com:2222/team/svc.git")).toBe("git.company.com/team/svc"); }); test("lowercases host, strips .git, ignores deeper path segments", () => { expect(parseGitRemote("https://GitHub.com/Owner/Repo")).toBe("github.com/Owner/Repo"); expect(parseGitRemote("https://gitlab.com/group/sub/proj.git")).toBe("gitlab.com/group/sub"); }); test("returns undefined for empty / unparseable / single-segment remotes", () => { expect(parseGitRemote("")).toBeUndefined(); expect(parseGitRemote("not a url")).toBeUndefined(); expect(parseGitRemote("https://github.com/onlyowner")).toBeUndefined(); }); }); describe("parseDeepLink + repoKey round-trip", () => { test("//owner/repo -> that host's key", () => { const dl = parseDeepLink("/github.com/snomiao/codehost/tree/main"); expect(dl?.type).toBe("repo"); if (dl?.type === "repo") { expect(repoKey(dl.target)).toBe("github.com/snomiao/codehost"); expect(dl.target.branch).toBe("main"); expect(dl.target.machine).toBeUndefined(); } }); test("/@//owner/repo pins the machine", () => { const dl = parseDeepLink("/sno@Mac/github.com/snomiao/codehost/tree/main"); expect(dl?.type).toBe("repo"); if (dl?.type === "repo") { expect(repoKey(dl.target)).toBe("github.com/snomiao/codehost"); expect(dl.target.machine).toBe("Mac"); expect(dl.target.user).toBe("sno"); expect(dl.target.branch).toBe("main"); } }); test("the user half is optional (/@Mac/...)", () => { const dl = parseDeepLink("/@Mac/github.com/snomiao/codehost"); expect(dl?.type === "repo" && dl.target.machine).toBe("Mac"); expect(dl?.type === "repo" && dl.target.user).toBeUndefined(); }); test("/@/ -> folder mount on that machine", () => { const dl = parseDeepLink("/sno@Mac/Users/sno/ws"); expect(dl?.type === "dev" && dl.target.host).toBe("Mac"); expect(dl?.type === "dev" && dl.target.user).toBe("sno"); expect(dl?.type === "dev" && dl.target.path).toBe("/Users/sno/ws"); }); test("a Windows drive path under a machine scope isn't mistaken for a git host", () => { const path = shareableDeepLink({ folder: toPosixPath("C:\\ws"), machine: "Win", user: "sno" })!; expect(path).toBe("/sno@Win/C:/ws"); const dl = parseDeepLink(path); expect(dl?.type === "dev" && dl.target.path).toBe("/C:/ws"); }); test("a dotfile path segment isn't mistaken for a git host", () => { const dl = parseDeepLink("/sno@Mac/.config/codehost"); expect(dl?.type === "dev" && dl.target.path).toBe("/.config/codehost"); }); test("/@ alone -> that machine's settings page", () => { const dl = parseDeepLink("/sno@Mac"); expect(dl?.type === "hostSettings" && dl.host).toBe("Mac"); }); test("legacy /gh/owner/repo -> github.com key", () => { const dl = parseDeepLink("/gh/snomiao/codehost"); expect(dl?.type).toBe("repo"); if (dl?.type === "repo") { expect(repoKey(dl.target)).toBe("github.com/snomiao/codehost"); expect(dl.target.branch).toBeUndefined(); } }); test("legacy /gh/owner/repo/tree/branch keeps the branch (slashes allowed)", () => { const dl = parseDeepLink("/gh/snomiao/codehost/tree/feat/x"); expect(dl?.type === "repo" && dl.target.branch).toBe("feat/x"); }); test("legacy /git//owner/repo -> that host's key", () => { const dl = parseDeepLink("/git/gitlab.com/group/proj/tree/dev"); expect(dl?.type).toBe("repo"); if (dl?.type === "repo") { expect(repoKey(dl.target)).toBe("gitlab.com/group/proj"); expect(dl.target.branch).toBe("dev"); } }); test("legacy /dev/ -> dev target with leading slash", () => { const dl = parseDeepLink("/dev/Users/sno/ws"); expect(dl?.type === "dev" && dl.target.path).toBe("/Users/sno/ws"); }); test("/dev/ round-trips (colon in a non-leading segment)", () => { // shareableDeepLink -> address bar -> parseDeepLink must preserve /C:/ws. const path = shareableDeepLink({ folder: toPosixPath("C:\\ws") })!; expect(path).toBe("/dev/C:/ws"); // no machine known -> legacy host-agnostic form const dl = parseDeepLink(path); expect(dl?.type === "dev" && dl.target.path).toBe("/C:/ws"); }); test("legacy /host// -> host-scoped dev target", () => { const dl = parseDeepLink("/host/Mac/Users/taku"); expect(dl?.type === "dev" && dl.target.host).toBe("Mac"); expect(dl?.type === "dev" && dl.target.path).toBe("/Users/taku"); }); test("a machine-scoped Windows drive path round-trips", () => { const path = shareableDeepLink({ folder: "/C:/ws", host: "EC2AMAZ-PH8C4K1" })!; expect(path).toBe("/@EC2AMAZ-PH8C4K1/C:/ws"); const dl = parseDeepLink(path); expect(dl?.type === "dev" && dl.target.host).toBe("EC2AMAZ-PH8C4K1"); expect(dl?.type === "dev" && dl.target.path).toBe("/C:/ws"); }); test("legacy /dev/ still parses host-agnostic (no host)", () => { const dl = parseDeepLink("/dev/C:/ws"); expect(dl?.type === "dev" && dl.target.host).toBeUndefined(); expect(dl?.type === "dev" && dl.target.path).toBe("/C:/ws"); }); test("non-deep-link -> null", () => { expect(parseDeepLink("/")).toBeNull(); expect(parseDeepLink("/settings")).toBeNull(); }); test("legacy ?machine= pins a repo link; absent by default", () => { const pinned = parseDeepLink("/gh/snomiao/codehost", "?machine=Mac"); expect(pinned?.type === "repo" && pinned.target.machine).toBe("Mac"); const unpinned = parseDeepLink("/gh/snomiao/codehost"); expect(unpinned?.type === "repo" && unpinned.target.machine).toBeUndefined(); }); }); describe("resolveDevTarget host scoping", () => { const mk = (peerId: string, host: string, cwd: string): PeerInfo => ({ peerId, role: "server", meta: { name: host, host, cwd }, }); // Same served path on two different machines — the ambiguity host scoping fixes. const servers = [mk("pA", "boxA", "/C:/ws"), mk("pB", "boxB", "/C:/ws")]; test("host-scoped target picks the matching host", () => { expect(resolveDevTarget(servers, { host: "boxB", path: "/C:/ws" })?.peerId).toBe("pB"); expect(resolveDevTarget(servers, { host: "boxA", path: "/C:/ws" })?.peerId).toBe("pA"); }); test("host-scoped target with no matching host -> null (won't cross machines)", () => { expect(resolveDevTarget(servers, { host: "boxC", path: "/C:/ws" })).toBeNull(); }); test("legacy host-agnostic target matches by path alone", () => { expect(resolveDevTarget(servers, { path: "/C:/ws" })?.peerId).toBe("pA"); }); test("leading/trailing slash differences still match (e.g. expose cwd)", () => { const ex = [mk("pE", "boxE", "localhost:8090")]; expect(resolveDevTarget(ex, { host: "boxE", path: "/localhost:8090" })?.peerId).toBe("pE"); }); }); describe("shareableDeepLink", () => { test("repo -> ///, host in the path", () => { expect(shareableDeepLink({ repo: "github.com/snomiao/codehost", branch: "main" })).toBe( "/github.com/snomiao/codehost/tree/main", ); expect(shareableDeepLink({ repo: "github.com/snomiao/codehost" })).toBe( "/github.com/snomiao/codehost", ); expect(shareableDeepLink({ repo: "gitlab.com/group/proj", branch: "dev" })).toBe( "/gitlab.com/group/proj/tree/dev", ); }); test("a pinned repo gains the @ prefix", () => { expect( shareableDeepLink({ repo: "github.com/snomiao/codehost", branch: "main", machine: "Mac", user: "sno", }), ).toBe("/sno@Mac/github.com/snomiao/codehost/tree/main"); }); test("no repo -> machine-scoped folder, or /dev/ when no machine is known", () => { expect(shareableDeepLink({ folder: "/C:/ws" })).toBe("/dev/C:/ws"); expect(shareableDeepLink({ folder: "/Users/sno/ws" })).toBe("/dev/Users/sno/ws"); expect(shareableDeepLink({ folder: "/Users/sno/ws", host: "Mac", user: "sno" })).toBe( "/sno@Mac/Users/sno/ws", ); }); test("nothing addressable -> null", () => { expect(shareableDeepLink({})).toBeNull(); }); test("round-trips: shareableDeepLink output parses back to the same key", () => { const path = shareableDeepLink({ repo: "gitlab.com/group/proj", branch: "dev" })!; const dl = parseDeepLink(path); expect(dl?.type === "repo" && repoKey(dl.target)).toBe("gitlab.com/group/proj"); }); }); describe("resolveRepoTarget root selection", () => { const root = (peerId: string, cwd: string): PeerInfo => ({ peerId, role: "server", meta: { name: peerId, host: "Mac", cwd, kind: "root" }, }); test("among nested roots, picks the deepest (longest cwd)", () => { const servers = [root("shallow", "/Users/sno"), root("deep", "/Users/sno/ws")]; const res = resolveRepoTarget(servers, { host: "github.com", owner: "snomiao", name: "codehost" }); expect(res?.peerId).toBe("deep"); expect(res?.folder).toBe("/Users/sno/ws/snomiao/codehost/tree/main"); }); test("an exact repo daemon still wins over any root", () => { const servers: PeerInfo[] = [ root("deep", "/Users/sno/ws"), { peerId: "exact", role: "server", meta: { name: "x", host: "Mac", cwd: "/x", repo: "github.com/snomiao/codehost" } }, ]; expect(resolveRepoTarget(servers, { host: "github.com", owner: "snomiao", name: "codehost" })?.peerId).toBe("exact"); }); }); describe("resolveRepoTarget machine pin", () => { const target = { host: "github.com", owner: "snomiao", name: "codehost" }; const root = (peerId: string, host: string, cwd: string): PeerInfo => ({ peerId, role: "server", meta: { name: peerId, host, cwd, kind: "root" }, }); test("machine pin selects only that machine's daemon, even if another is a better match", () => { const servers = [root("other", "Other", "/x"), root("mac", "Mac", "/Users/sno/ws")]; const res = resolveRepoTarget(servers, { ...target, machine: "Mac" }); expect(res?.peerId).toBe("mac"); }); test("machine pin with no live daemon on that machine fails loud (null), no silent fallback", () => { const servers = [root("other", "Other", "/x")]; expect(resolveRepoTarget(servers, { ...target, machine: "Mac" })).toBeNull(); }); test("pinned to a machine that hasn't checked the repo out -> the layout path to provision into", () => { // The point of /@//…: not "who already has this // repo?" but "put it on this machine" — the root daemon's setup hook clones // into the synthesized layout path. const res = resolveRepoTarget([root("mac", "Mac", "/Users/sno/ws")], { ...target, branch: "main", machine: "Mac", user: "sno", }); expect(res).toEqual({ peerId: "mac", folder: "/Users/sno/ws/snomiao/codehost/tree/main" }); }); }); describe("resolveDevTarget advertised workspaces", () => { const root: PeerInfo = { peerId: "root1", role: "server", meta: { name: "mac", host: "mbp", cwd: "/Users/sno", kind: "root", workspaces: [{ path: "/Users/sno/scratch/tool" }], }, }; test("a /host// link matches a registered workspace on a root daemon", () => { const res = resolveDevTarget([root], { host: "mbp", path: "/Users/sno/scratch/tool" }); expect(res?.peerId).toBe("root1"); expect(res?.folder).toBe("/Users/sno/scratch/tool"); expect(res?.exact).toBe(true); }); test("host scoping still applies to workspace matches", () => { expect(resolveDevTarget([root], { host: "other", path: "/Users/sno/scratch/tool" })).toBeNull(); }); test("an exact cwd mount still wins (no folder synthesized)", () => { const res = resolveDevTarget([root], { host: "mbp", path: "/Users/sno" }); expect(res).toEqual({ peerId: "root1" }); }); }); describe("resolveRepoTarget enumerated workspaces (exact)", () => { const target = { host: "github.com", owner: "snomiao", name: "codehost" }; const root = (peerId: string, cwd: string, workspaces?: WorkspaceInfo[]): PeerInfo => ({ peerId, role: "server", meta: { name: peerId, host: "Mac", cwd, kind: "root", workspaces }, }); test("an enumerated checkout beats a deeper root's synthesized fallback", () => { const servers = [ root("deep", "/Users/sno/ws"), root("listed", "/Users/sno", [ { path: "/Users/sno/snomiao/codehost/tree/main", repo: "github.com/snomiao/codehost", branch: "main" }, ]), ]; const res = resolveRepoTarget(servers, target); expect(res?.peerId).toBe("listed"); expect(res?.folder).toBe("/Users/sno/snomiao/codehost/tree/main"); expect(res?.exact).toBe(true); }); test("branch mismatch falls back to the synthesized path", () => { const servers = [ root("listed", "/Users/sno/ws", [ { path: "/Users/sno/ws/snomiao/codehost/tree/main", repo: "github.com/snomiao/codehost", branch: "main" }, ]), ]; const res = resolveRepoTarget(servers, { ...target, branch: "dev" }); expect(res?.exact).toBeUndefined(); expect(res?.folder).toBe("/Users/sno/ws/snomiao/codehost/tree/dev"); }); test("pickRoomMatch ranks an exact enumerated match like a repo daemon", () => { const fallback = { token: "room-a", resolution: { peerId: "x", folder: "/a/b" } }; const exact = { token: "room-b", resolution: { peerId: "y", folder: "/c/d", exact: true } }; expect(pickRoomMatch([fallback, exact])?.token).toBe("room-b"); }); }); describe("resolveRepoTarget machine preference (history)", () => { const target = { host: "github.com", owner: "snomiao", name: "codehost" }; const repoDaemon = (peerId: string, hostId?: string, host = "Mac"): PeerInfo => ({ peerId, role: "server", meta: { name: peerId, host, hostId, cwd: "/x", repo: "github.com/snomiao/codehost" }, }); const root = (peerId: string, cwd: string, hostId?: string, host = "Mac"): PeerInfo => ({ peerId, role: "server", meta: { name: peerId, host, hostId, cwd, kind: "root" }, }); test("among equal repo daemons, prefers the history hostId", () => { const servers = [repoDaemon("a", "id-a"), repoDaemon("b", "id-b")]; expect(resolveRepoTarget(servers, target, { hostId: "id-b" })?.peerId).toBe("b"); expect(resolveRepoTarget(servers, target, { hostId: "id-a" })?.peerId).toBe("a"); }); test("hostname fallback matches entries/daemons without a hostId", () => { const servers = [repoDaemon("a", undefined, "mbp"), repoDaemon("b", undefined, "win")]; expect(resolveRepoTarget(servers, target, { host: "win" })?.peerId).toBe("b"); }); test("preferred root beats a deeper root on another machine", () => { const servers = [root("deep", "/Users/sno/ws/x", "id-other"), root("mine", "/Users/sno", "id-mine")]; expect(resolveRepoTarget(servers, target, { hostId: "id-mine" })?.peerId).toBe("mine"); }); test("no preference keeps the existing order (deepest root, first repo daemon)", () => { const servers = [root("shallow", "/Users/sno", "id-1"), root("deep", "/Users/sno/ws", "id-2")]; expect(resolveRepoTarget(servers, target)?.peerId).toBe("deep"); }); test("a stale preference (machine offline) falls back gracefully", () => { const servers = [repoDaemon("a", "id-a")]; expect(resolveRepoTarget(servers, target, { hostId: "gone" })?.peerId).toBe("a"); }); }); describe("gitUrlToPath", () => { test("a repo URL is near-identity: drop the scheme, keep the host", () => { expect(gitUrlToPath("https://github.com/snomiao/codehost")).toBe("/github.com/snomiao/codehost"); expect(gitUrlToPath("https://github.com/snomiao/codehost/tree/main")).toBe("/github.com/snomiao/codehost/tree/main"); expect(gitUrlToPath("github.com/snomiao/codehost")).toBe("/github.com/snomiao/codehost"); expect(gitUrlToPath("https://github.com/snomiao/codehost.git")).toBe("/github.com/snomiao/codehost"); expect(gitUrlToPath("https://github.com/snomiao/codehost/tree/feat/x")).toBe("/github.com/snomiao/codehost/tree/feat/x"); expect(gitUrlToPath("https://github.com/snomiao/codehost/")).toBe("/github.com/snomiao/codehost"); expect(gitUrlToPath("https://github.com/snomiao/codehost?tab=readme#x")).toBe("/github.com/snomiao/codehost"); }); test("other hosts + scp form keep their host too", () => { expect(gitUrlToPath("https://gitlab.com/group/proj/tree/dev")).toBe("/gitlab.com/group/proj/tree/dev"); expect(gitUrlToPath("git@github.com:snomiao/codehost.git")).toBe("/github.com/snomiao/codehost"); }); test("non-repo / junk -> null", () => { expect(gitUrlToPath("")).toBeNull(); expect(gitUrlToPath("not a url")).toBeNull(); expect(gitUrlToPath("https://github.com/snomiao")).toBeNull(); // no repo expect(gitUrlToPath("/gh/snomiao/codehost")).toBeNull(); // already a deep link, no host }); }); describe("pickRoomMatch (cross-room ranking)", () => { const exact = { token: "tA", resolution: { peerId: "p1" } }; // no folder = exact const root = { token: "tB", resolution: { peerId: "p2", folder: "/work/me/repo" } }; test("exact match (no folder) beats a root fallback, regardless of order", () => { expect(pickRoomMatch([root, exact])?.token).toBe("tA"); expect(pickRoomMatch([exact, root])?.token).toBe("tA"); }); test("only root fallbacks -> first root", () => { const root2 = { token: "tC", resolution: { peerId: "p3", folder: "/x" } }; expect(pickRoomMatch([root, root2])?.token).toBe("tB"); }); test("no matches -> null", () => { expect(pickRoomMatch([])).toBeNull(); }); });