File
Blob: test/util/test-helpers.ts
| 1 | /** |
| 2 | * Shared test utilities for Git operations. |
| 3 | */ |
| 4 | |
| 5 | import { exports as workerExports } from "cloudflare:workers"; |
| 6 | import { lookupPushAuth } from "./repoSeed"; |
| 7 | |
| 8 | type StringEnvKey = { |
| 9 | [K in keyof Env]: Env[K] extends string | undefined ? K : never; |
| 10 | }[keyof Env]; |
| 11 | |
| 12 | /** |
| 13 | * Temporarily override selected env bindings for the duration of fn(), restoring afterwards. |
| 14 | */ |
| 15 | export async function withEnvOverrides<T, K extends StringEnvKey>( |
| 16 | env: Env, |
| 17 | overrides: Pick<Env, K>, |
| 18 | fn: () => Promise<T> |
| 19 | ): Promise<T> { |
| 20 | const keys = Object.keys(overrides) as K[]; |
| 21 | const prev = new Map<K, Env[K]>(); |
| 22 | for (const key of keys) { |
| 23 | prev.set(key, env[key]); |
| 24 | env[key] = overrides[key]; |
| 25 | } |
| 26 | try { |
| 27 | return await fn(); |
| 28 | } finally { |
| 29 | for (const key of keys) { |
| 30 | const value = prev.get(key); |
| 31 | if (value !== undefined) { |
| 32 | env[key] = value; |
| 33 | } else { |
| 34 | delete env[key]; |
| 35 | } |
| 36 | } |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | export * from "./do-retry"; |
| 41 | export * from "./git-pack"; |
| 42 | export * from "./packed-repo"; |
| 43 | |
| 44 | export function toRequestBody(bytes: Uint8Array): ArrayBuffer { |
| 45 | const body = new ArrayBuffer(bytes.byteLength); |
| 46 | new Uint8Array(body).set(bytes); |
| 47 | return body; |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * Generate a per-test unique repo id suffix to avoid shared storage collisions |
| 52 | * when isolatedStorage is disabled. |
| 53 | */ |
| 54 | export function uniqueRepoId(prefix = "r"): string { |
| 55 | return `${prefix}-${Math.random().toString(36).slice(2, 10)}`; |
| 56 | } |
| 57 | |
| 58 | // POST to git-receive-pack with the seeded push PAT auto-attached. The owner |
| 59 | // and repo are parsed from the URL and matched against the auth registered |
| 60 | // by `setupRepoForTests`. Tests that want to assert auth failures should |
| 61 | // build the request themselves rather than going through this helper. |
| 62 | export async function postReceivePack( |
| 63 | url: string, |
| 64 | body: Uint8Array, |
| 65 | options?: { authHeader?: string; signal?: AbortSignal } |
| 66 | ): Promise<Response> { |
| 67 | const headers: Record<string, string> = { |
| 68 | "Content-Type": "application/x-git-receive-pack-request", |
| 69 | }; |
| 70 | const match = /https?:\/\/[^/]+\/([^/]+)\/([^/]+)\/git-receive-pack/.exec(url); |
| 71 | const auth = options?.authHeader ?? (match ? lookupPushAuth(match[1]!, match[2]!) : undefined); |
| 72 | if (auth) headers.Authorization = auth; |
| 73 | return await workerExports.default.fetch(url, { |
| 74 | method: "POST", |
| 75 | headers, |
| 76 | body: toRequestBody(body), |
| 77 | signal: options?.signal, |
| 78 | }); |
| 79 | } |