Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237import { describe, expect, it, vi } from "vitest";import { composeQuery, defaultSourceBranch, defaultTargetBranch, loadCompose, parseSource, sortBranchesByRecency, sourceBranchChoices} from "$lib/api/pullCompose";import type { BranchSummary } from "$lib/api/repo";
const branch = (name: string, when?: string, isDefault = false): BranchSummary => ({ name, hash: `hash-${name}`, when, isDefault});
// main is the default; feat is the newest of the restconst branches = [ branch("main", "2024-01-01T00:00:00Z", true), branch("old", "2024-01-02T00:00:00Z"), branch("feat", "2024-03-01T00:00:00Z")];
const names = (list: BranchSummary[]) => list.map((entry) => entry.name);
describe("sortBranchesByRecency", () => { it("puts the newest commit first", () => { expect(names(sortBranchesByRecency(branches))).toEqual(["feat", "old", "main"]); });
it("sinks branches with no commit without reordering them", () => { const list = [ branch("empty-a"), branch("dated", "2024-01-01T00:00:00Z"), branch("empty-b") ]; expect(names(sortBranchesByRecency(list))).toEqual(["dated", "empty-a", "empty-b"]); });});
describe("sourceBranchChoices", () => { it("drops the default branch and sorts the rest", () => { expect(names(sourceBranchChoices(branches))).toEqual(["feat", "old"]); });});
describe("defaultTargetBranch", () => { it("keeps a value that names a real branch", () => { expect(defaultTargetBranch(branches, "old")).toBe("old"); });
it("falls back to the default branch when the value is unknown or absent", () => { expect(defaultTargetBranch(branches, "gone")).toBe("main"); expect(defaultTargetBranch(branches, "")).toBe("main"); });
it("gives up when no branch is marked default", () => { expect(defaultTargetBranch([branch("a", "2024-01-01T00:00:00Z")], "")).toBe(""); });});
describe("defaultSourceBranch", () => { const choices = sourceBranchChoices(branches); const forkBranches = [ branch("fork-old", "2024-01-01T00:00:00Z"), branch("fork-new", "2024-05-01T00:00:00Z") ];
it("picks the most recent non-default branch", () => { expect(defaultSourceBranch("branch", "", choices, [])).toBe("feat"); });
it("replaces a value that is not a candidate", () => { // main is the default branch, so it never appears in the source list expect(defaultSourceBranch("branch", "main", choices, [])).toBe("feat"); });
it("keeps a value that is a candidate", () => { expect(defaultSourceBranch("branch", "old", choices, [])).toBe("old"); });
it("reads the fork's branches in fork mode", () => { expect(defaultSourceBranch("fork", "", choices, forkBranches)).toBe("fork-old"); expect(defaultSourceBranch("fork", "fork-new", choices, forkBranches)).toBe("fork-new"); // a branch of the target repo is not a candidate against a fork expect(defaultSourceBranch("fork", "feat", choices, forkBranches)).toBe("fork-old"); });
it("leaves the value alone in patch mode", () => { expect(defaultSourceBranch("patch", "whatever", choices, forkBranches)).toBe("whatever"); });
it("returns nothing when there are no candidates", () => { expect(defaultSourceBranch("branch", "feat", [], [])).toBe(""); });});
describe("parseSource", () => { it("accepts the three modes case-insensitively", () => { expect(parseSource("branch")).toBe("branch"); expect(parseSource("FORK")).toBe("fork"); expect(parseSource("patch")).toBe("patch"); });
it("rejects anything else", () => { expect(parseSource("")).toBeNull(); expect(parseSource(null)).toBeNull(); expect(parseSource("stack")).toBeNull(); });});
describe("composeQuery", () => { it("omits the default source and a fork picked outside fork mode", () => { expect( composeQuery({ source: "branch", sourceBranch: "feat", targetBranch: "main", fork: "did:x" }) ).toBe("sourceBranch=feat&targetBranch=main"); });
it("carries the fork only in fork mode", () => { expect( composeQuery({ source: "fork", sourceBranch: "feat", targetBranch: "main", fork: "did:x" }) ).toBe("source=fork&sourceBranch=feat&targetBranch=main&fork=did%3Ax"); });
it("is empty when nothing is selected", () => { expect( composeQuery({ source: "branch", sourceBranch: "", targetBranch: "", fork: "" }) ).toBe(""); });});
describe("loadCompose", () => { const UPSTREAM = "did:plc:upstream"; const FORK = "did:plc:fork"; const upstreamMain = "a".repeat(40); const forkMain = "f".repeat(40); const mergeBase = "b".repeat(40); const signature = { name: "alice", email: "alice@example.com", when: "2026-01-01T00:00:00Z" };
it("compares a fork branch against the same-named upstream branch by commit id", async () => { const fetchMock = vi.fn<typeof globalThis.fetch>(async (input) => { const url = new URL(String(input)); const param = (name: string) => url.searchParams.get(name); switch (url.pathname.replace(/^\/xrpc\//, "")) { case "org.tangled.temp.git.listBranches": return Response.json({ defaultBranch: "main", branches: [ { name: "main", oid: param("repo") === FORK ? forkMain : upstreamMain, committer: signature, message: "tip" } ] }); case "org.tangled.repo.listRepos": return Response.json({ items: [ { uri: `at://${FORK}/sh.tangled.repo/self`, value: { source: { did: UPSTREAM }, declaration: { name: "core" } } } ] }); case "org.tangled.temp.git.listCommits": return Response.json({ commits: [ { oid: forkMain, parents: [mergeBase], tree: "tree", author: signature, committer: signature, message: "fork only", extraHeaders: [] } ] }); case "org.tangled.temp.git.getMergeBase": return Response.json({ commit: mergeBase }); default: return Response.json({ error: "NotFound" }, { status: 404 }); } });
const data = await loadCompose({ config: { bobbinUrl: "https://bobbin.test" }, repo: { uri: `at://${UPSTREAM}/sh.tangled.repo/self`, repoDid: UPSTREAM, ownerHandle: "upstream.test", name: "core", knot: "knot.test" } as never, viewer: { did: "did:plc:alice", handle: "alice.test" }, params: new URLSearchParams({ source: "fork", fork: FORK, sourceBranch: "main", targetBranch: "main" }), fetch: fetchMock });
const call = (nsid: string) => fetchMock.mock.calls .map(([input]) => new URL(String(input))) .find((url) => url.pathname === `/xrpc/${nsid}`)!; const commits = call("org.tangled.temp.git.listCommits"); expect(commits.searchParams.getAll("repos")).toEqual([FORK, UPSTREAM]); expect(commits.searchParams.getAll("ranges")).toEqual([`${upstreamMain}..${forkMain}`]); const base = call("org.tangled.temp.git.getMergeBase"); expect(base.searchParams.getAll("repos")).toEqual([FORK, UPSTREAM]); expect(base.searchParams.get("base")).toBe(upstreamMain); expect(base.searchParams.get("head")).toBe(forkMain);
expect(data.commitsError).toBe(""); expect(data.commits.map((commit) => commit.hash)).toEqual([forkMain]); expect(data.mergeBase).toBe(mergeBase); expect(data.sourceHead).toBe(forkMain); });});