~james/core

core/web/src/lib/api/pull.test.ts -rw-r--r-- 4.4 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 15 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { afterEach, describe, expect, it, vi } from "vitest";
import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
import { atUriOf, didOf, gitOidOf, knotOf, nsidOf } from "#lib/api/syntax.ts";
import oauthMetadata from "#lib/oauth-client-metadata.ts";
import { missingPermissions } from "#lib/auth/scopes.ts";
import type * as Pull from "#lib/api/pull.ts";

const agent = { sub: "did:plc:limpet" } as unknown as OAuthUserAgent;
const knot = knotOf("knot.example.com");
const repo = didOf("did:plc:barnacle");
const ticketUri = atUriOf("at://did:plc:barnacle/org.tangled.track.ticket/3lk");
const patchUri = atUriOf("at://did:plc:barnacle/org.tangled.review.patch/bafypatch");
const range = { base: gitOidOf("a".repeat(40)), head: gitOidOf("b".repeat(40)) };
const source = { repo, branch: "feat/kelp" };

const load = async (responses: (() => Response)[]) => {
	vi.resetModules();
	const minted: string[] = [];
	vi.doMock("$app/env", () => ({ browser: true }));
	vi.doMock("#lib/auth/agent.ts", () => ({
		mintServiceAuth: async (_agent: unknown, { lxm }: { lxm: string }) => {
			minted.push(lxm);
			return "token";
		},
		serviceDidForHost: (host: string) => `did:web:${host}`
	}));
	const pull = (await import("#lib/api/pull.ts")) as typeof Pull;
	const calls: { url: string; body?: Record<string, unknown> }[] = [];
	vi.stubGlobal("fetch", async (input: URL | RequestInfo, init?: RequestInit) => {
		calls.push({ url: String(input), body: init?.body && JSON.parse(String(init.body)) });
		return (responses.shift() ?? (() => Response.json({})))();
	});
	return { pull, minted, calls };
};

afterEach(() => {
	vi.unstubAllGlobals();
});

describe("pull writes on the knot", () => {
	it("merges through knotmirror with a receive-pack token for the knot", async () => {
		const { pull, minted, calls } = await load([]);
		const bobbin = "https://bobbin.example.com";

		await pull.mergeCommit(agent, bobbin, knot, {
			target: { repo, branch: "main", commit: range.base },
			source: { repo, commit: range.head },
			style: "rebase"
		});

		expect(calls[0].url).toBe(`${bobbin}/xrpc/sh.tangled.git.mergeCommit`);
		expect(calls[0].body).toEqual({
			target: {
				$type: "sh.tangled.git.mergeCommit#target",
				repo,
				branch: "main",
				commit: range.base
			},
			source: { $type: "sh.tangled.git.mergeCommit#source", repo, commit: range.head },
			style: "rebase"
		});
		expect(minted).toEqual(["org.tangled.git.receivePack"]);
		const wanted = [
			{
				resource: "rpc",
				lxm: nsidOf("org.tangled.git.receivePack"),
				aud: `did:web:${knot}`
			}
		] as const;
		expect(missingPermissions(oauthMetadata.scope, wanted)).toEqual([]);
	});

	it("opens a pull as a ticket on the target branch plus a patch naming it", async () => {
		const { pull, calls } = await load([
			() => Response.json({ uri: ticketUri, cid: "bafyreiticket" + "a".repeat(46) }),
			() => Response.json({ uri: patchUri, cid: "bafyreipatch" + "a".repeat(47) })
		]);

		const opened = await pull.openPull(
			agent,
			knot,
			repo,
			{ title: "kelp", body: "" },
			"main",
			range,
			source
		);

		expect(opened).toMatchObject({ uri: ticketUri, cid: "bafyreiticket" + "a".repeat(46) });
		expect(calls[0].body).toMatchObject({
			collection: "org.tangled.track.ticket",
			record: { title: { text: "kelp" }, state: "open", targetBranch: "main" }
		});
		expect(calls[0].body?.record).not.toHaveProperty("body");
		expect(calls[1].url).toBe(`https://${knot}/xrpc/org.tangled.review.submitPatch`);
		expect(calls[1].body).toEqual({
			repo,
			patch: {
				ticket: "3lk",
				base: { oid: range.base },
				head: { oid: range.head },
				sourceRepo: source.repo,
				sourceBranch: source.branch
			}
		});
	});

	it("takes the ticket back down when the knot refuses its patch", async () => {
		const { pull, calls } = await load([
			() => Response.json({ uri: ticketUri, cid: "bafyreiticket" + "a".repeat(46) }),
			() =>
				Response.json(
					{ error: "InvalidRequest", message: "the patch's head isn't a commit" },
					{ status: 400 }
				),
			() => Response.json({ commit: {} })
		]);

		const thrown = await pull
			.openPull(agent, knot, repo, { title: "kelp", body: "" }, "main", range, source)
			.catch((cause) => cause);

		expect(thrown).toBeInstanceOf(Error);
		expect(calls[2].url).toBe(`https://${knot}/xrpc/com.atproto.repo.deleteRecord`);
		expect(calls[2].body).toEqual({
			repo,
			collection: "org.tangled.track.ticket",
			rkey: "3lk",
			swapRecord: "bafyreiticket" + "a".repeat(46)
		});
	});
});