clone

nostr relays on demand at <name>.bind.ws: claim one with your key, pay in zaps past the free allowance

dependabot/npm_and_yarn/npm-f01a459b0f
bindws / test / conformance / grasp.test.ts
grasp.test.ts 5.1K
1// GRASP-01 conformance uses the stock Git client against the public Smart HTTP door.2import { execFile } from "node:child_process";3import { mkdtemp, writeFile, readFile, rm } from "node:fs/promises";4import { tmpdir } from "node:os";5import { promisify } from "node:util";6import { describe, expect, it } from "vitest";7import { finalizeEvent, getPublicKey, generateSecretKey } from "nostr-tools/pure";8import { KIND_REPO, KIND_REPO_STATE } from "../../src/kinds.ts";9import { Client, HTTP_URL, RELAY_URL, sleep, now } from "./helpers.ts";10import { npubEncode } from "nostr-tools/nip19";1112const exec = promisify(execFile);1314async function git(cwd: string, ...args: string[]): Promise<string> {15 const result = await exec("git", args, { cwd, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } });16 return result.stdout.trim();17}1819function repositoryPath(npub: string, identifier: string): string {20 return `/${npub}/${encodeURIComponent(identifier)}.git`;21}2223async function waitForRefs(url: string): Promise<void> {24 for (let attempt = 0; attempt < 60; attempt++) {25 const response = await fetch(`${url}/info/refs?service=git-upload-pack`);26 const text = await response.text();27 if (response.status === 200 && text.includes("# service=git-upload-pack")) return;28 await sleep(250);29 }30 throw new Error("GRASP repository did not become available");31}3233async function publish(c: Client, sk: Uint8Array, kind: number, tags: string[][], createdAt: number): Promise<void> {34 const event = finalizeEvent({ kind, content: "", tags, created_at: createdAt }, sk);35 await c.publish(event);36}3738describe("GRASP-01 stock Git", () => {39 it("clones, pushes an accepted state, fetches incrementally, and supports partial clones", async ({ skip }) => {40 const infoResponse = await fetch(HTTP_URL, { headers: { accept: "application/nostr+json" } });41 const info: any = await infoResponse.json();42 if (!info.supported_grasps?.includes("GRASP-01")) skip();4344 const root = await mkdtemp(`${tmpdir()}/bindws-grasp-`);45 const clone = await mkdtemp(`${tmpdir()}/bindws-grasp-clone-`);46 const partial = await mkdtemp(`${tmpdir()}/bindws-grasp-partial-`);47 const sk = generateSecretKey();48 const npub = npubEncode(getPublicKey(sk));49 const identifier = `stock-${Math.random().toString(36).slice(2, 10)}`;50 const path = repositoryPath(npub, identifier);51 const repoURL = `${HTTP_URL}${path}`;52 const relayURL = RELAY_URL;53 const createdAt = now();54 const c = await Client.connect(RELAY_URL);55 try {56 await git(root, "init", "--initial-branch=main");57 await git(root, "config", "user.email", "grasp-conformance@example.com");58 await git(root, "config", "user.name", "GRASP conformance");59 await writeFile(`${root}/README.md`, "initial GRASP repository\n");60 await git(root, "add", "README.md");61 await git(root, "commit", "-m", "initial");62 const first = await git(root, "rev-parse", "HEAD");63 await git(root, "tag", "-a", "v1", "-m", "first release");64 const tag = await git(root, "rev-parse", "refs/tags/v1^{tag}");6566 await publish(c, sk, KIND_REPO, [67 ["d", identifier],68 ["clone", repoURL],69 ["relays", relayURL],70 ["maintainers", getPublicKey(sk)],71 ], createdAt);72 await publish(c, sk, KIND_REPO_STATE, [73 ["d", identifier],74 ["HEAD", "ref: refs/heads/main"],75 ["refs/heads/main", first],76 ["refs/tags/v1", tag],77 ], createdAt + 1);7879 await waitForRefs(repoURL);80 await git(root, "push", repoURL, "HEAD:refs/heads/main", "refs/tags/v1:refs/tags/v1");81 await git(clone, "clone", repoURL, ".");82 expect(await git(clone, "rev-parse", "HEAD")).toBe(first);83 expect(await git(clone, "describe", "--tags")).toBe("v1");84 expect((await git(clone, "fsck", "--full"))).not.toContain("dangling");8586 await writeFile(`${root}/README.md`, "incremental GRASP repository\n");87 await git(root, "add", "README.md");88 await git(root, "commit", "-m", "incremental");89 const second = await git(root, "rev-parse", "HEAD");90 await publish(c, sk, KIND_REPO_STATE, [91 ["d", identifier],92 ["HEAD", "ref: refs/heads/main"],93 ["refs/heads/main", second],94 ["refs/tags/v1", tag],95 ], createdAt + 2);96 await git(root, "push", repoURL, "HEAD:refs/heads/main");97 await git(clone, "fetch", repoURL, "refs/heads/main:refs/remotes/origin/main");98 expect(await git(clone, "rev-parse", "refs/remotes/origin/main")).toBe(second);99100 await git(partial, "clone", "--filter=blob:none", "--no-checkout", repoURL, ".");101 await git(partial, "fsck", "--full");102 await git(partial, "checkout", "main");103 expect(await readFile(`${partial}/README.md`, "utf8")).toBe("incremental GRASP repository\n");104 } finally {105 await publish(c, sk, 5, [["a", `${KIND_REPO}:${getPublicKey(sk)}:${identifier}`], ["a", `${KIND_REPO_STATE}:${getPublicKey(sk)}:${identifier}`]], createdAt + 3).catch(() => {});106 c.close();107 await Promise.all([rm(root, { recursive: true, force: true }), rm(clone, { recursive: true, force: true }), rm(partial, { recursive: true, force: true })]);108 }109 }, 120_000);110});