Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

75 lines · 4.6 KBRaw History
1import { createHash } from "node:crypto";
2import { describe, expect, it } from "vitest";
3import { readFileSync } from "node:fs";
4import path from "node:path";
5import { authorizeUrl, clientId, editorRedirect, installHintPath, normalizeServer, pkce, readCallback, readInstallHint, serverInputError, serverLabel } from "../src/util";
6
7const S = "https://diffinery.com";
8
9describe("server and OAuth", () => {
10 it("normalizes the server setting", () => {
11 expect(normalizeServer(undefined)).toBe(S);
12 expect(normalizeServer(" https://diffinery.com/ ")).toBe(S);
13 expect(normalizeServer("diffinery.example.org")).toBe("https://diffinery.example.org");
14 expect(normalizeServer("http://localhost:8787")).toBe("http://localhost:8787");
15 expect(() => normalizeServer("http://diffinery.com")).toThrow(/https/);
16 expect(() => normalizeServer("https://")).toThrow(/valid URL/);
17 });
18
19 it("checks a server address someone types, and shows it by its host", () => {
20 expect(serverInputError("git.example.com")).toBeNull();
21 expect(serverInputError(" https://git.example.com/ ")).toBeNull();
22 expect(serverInputError("http://localhost:8787")).toBeNull();
23 expect(serverInputError("")).toMatch(/Enter/);
24 expect(serverInputError("git example")).toMatch(/spaces/);
25 expect(serverInputError("http://git.example.com")).toMatch(/https/);
26 expect(serverInputError("https://")).toMatch(/valid/);
27 expect(serverLabel(S)).toBe("diffinery.com");
28 expect(serverLabel("https://git.example.com:8443")).toBe("git.example.com:8443");
29 });
30
31 it("reads the server the installer left, where the installers write it", () => {
32 expect(installHintPath({ HOME: "/home/ada" }, "/ignored")).toBe(path.join("/home/ada", ".config", "diffinery", "editor-server"));
33 expect(installHintPath({ HOME: "/home/ada", XDG_CONFIG_HOME: "/cfg" }, "/ignored")).toBe(path.join("/cfg", "diffinery", "editor-server"));
34 expect(installHintPath({ XDG_CONFIG_HOME: "relative" }, "/home/bo")).toBe(path.join("/home/bo", ".config", "diffinery", "editor-server"));
35 expect(readInstallHint("https://git.example.com/\n")).toBe("https://git.example.com");
36 expect(readInstallHint("git.example.com\r\n")).toBe("https://git.example.com");
37 expect(readInstallHint("http://git.example.com\n")).toBeNull();
38 expect(readInstallHint("")).toBeNull();
39 expect(readInstallHint("{\"server\": 1}")).toBeNull();
40 const sh = readFileSync(path.join(__dirname, "../install/install.sh"), "utf8");
41 const ps1 = readFileSync(path.join(__dirname, "../install/install.ps1"), "utf8");
42 expect(sh).toContain('"$conf/diffinery/editor-server"');
43 expect(ps1).toContain('"diffinery\\editor-server"');
44 });
45
46 it("registers every command it contributes", () => {
47 const pkg = JSON.parse(readFileSync(path.join(__dirname, "../package.json"), "utf8"));
48 const src = readFileSync(path.join(__dirname, "../src/extension.ts"), "utf8");
49 for (const c of pkg.contributes.commands as { command: string }[]) expect(src).toContain(`"${c.command}"`);
50 });
51
52 it("returns to the editor by its own scheme when the server knows it", () => {
53 expect(editorRedirect("vscode")).toBe("vscode://codician.diffinery/oauth");
54 expect(editorRedirect("cursor")).toBe("cursor://codician.diffinery/oauth");
55 expect(editorRedirect("some-fork")).toBeNull();
56 expect(clientId(S)).toBe("https://diffinery.com/oauth/clients/editor.json");
57 });
58
59 it("makes a PKCE pair and an authorization URL", () => {
60 const p = pkce();
61 expect(p.challenge).toBe(createHash("sha256").update(p.verifier).digest("base64url"));
62 const u = new URL(authorizeUrl(S, "cursor://codician.diffinery/oauth", p.challenge, p.state));
63 expect(u.origin + u.pathname).toBe(`${S}/oauth/authorize`);
64 expect(Object.fromEntries(u.searchParams)).toEqual({ response_type: "code", client_id: clientId(S), redirect_uri: "cursor://codician.diffinery/oauth", code_challenge: p.challenge, code_challenge_method: "S256", state: p.state, scope: "write" });
65 });
66
67 it("accepts only the callback of this sign-in, from this server", () => {
68 const expect_ = { state: "st", server: S };
69 expect(readCallback(`code=dfa_1&state=st&iss=${encodeURIComponent(S)}`, expect_)).toEqual({ code: "dfa_1" });
70 expect(readCallback("code=dfa_1&state=other", expect_)).toHaveProperty("error");
71 expect(readCallback("code=dfa_1&state=st&iss=https%3A%2F%2Fevil.example", expect_)).toEqual({ error: expect.stringMatching(/evil\.example/) });
72 expect(readCallback("error=access_denied&state=st", expect_)).toEqual({ error: "Sign-in was cancelled in the browser." });
73 expect(readCallback("state=st", expect_)).toHaveProperty("error");
74 });
75});