CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
gitUtils.test.ts275 linesDownload Raw Back to utils
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { vi, describe, expect, it, afterEach, beforeEach } from 'vitest';8import * as child_process from 'node:child_process';9import {10  isGitHubRepositoryAsync,11  getGitRepoRootAsync,12  getLatestGitHubRelease,13  getGitHubRepoInfoAsync,14} from './gitUtils.js';15 16vi.mock('node:child_process');17 18function mockExecFileStdout(stdout: string): void {19  vi.mocked(child_process.execFile).mockImplementation(((20    _cmd,21    _args,22    _opts,23    cb,24  ) => {25    (cb as (err: Error | null, stdout: string, stderr: string) => void)(26      null,27      stdout,28      '',29    );30    return {} as ReturnType<typeof child_process.execFile>;31  }) as typeof child_process.execFile);32}33 34function mockExecFileError(error: Error): void {35  vi.mocked(child_process.execFile).mockImplementation(((36    _cmd,37    _args,38    _opts,39    cb,40  ) => {41    (cb as (err: Error, stdout: string, stderr: string) => void)(error, '', '');42    return {} as ReturnType<typeof child_process.execFile>;43  }) as typeof child_process.execFile);44}45 46describe('isGitHubRepositoryAsync', async () => {47  beforeEach(() => {48    vi.resetAllMocks();49  });50 51  afterEach(() => {52    vi.restoreAllMocks();53  });54 55  it('returns false if the git command fails', async () => {56    mockExecFileError(new Error('oops'));57 58    await expect(isGitHubRepositoryAsync()).resolves.toBe(false);59  });60 61  it.each([62    [63      'non-GitHub remote',64      `origin  https://gitlab.com/owner/repo.git (fetch)65origin  https://gitlab.com/owner/repo.git (push)`,66    ],67    [68      'github.com lookalike host',69      `origin  https://github.com.evil/owner/repo.git (fetch)70origin  https://github.com.evil/owner/repo.git (push)`,71    ],72    [73      'github.com only in path',74      `origin  https://gitlab.com/owner/github.com-mirror.git (fetch)75origin  https://gitlab.com/owner/github.com-mirror.git (push)`,76    ],77    [78      'GitHub SSH lookalike host',79      `origin  git@github.com.evil:owner/repo.git (fetch)80origin  git@github.com.evil:owner/repo.git (push)`,81    ],82  ])('returns false for %s', async (_name, remotes) => {83    mockExecFileStdout(remotes);84 85    await expect(isGitHubRepositoryAsync()).resolves.toBe(false);86  });87 88  it.each([89    [90      'HTTPS GitHub remote',91      `origin  https://github.com/sethvargo/gemini-cli (fetch)92origin  https://github.com/sethvargo/gemini-cli (push)`,93    ],94    [95      'GitHub SSH remote',96      `origin  git@github.com:owner/repo.git (fetch)97origin  git@github.com:owner/repo.git (push)`,98    ],99    [100      'GitHub SSH URL remote',101      `origin  ssh://git@github.com/owner/repo.git (fetch)102origin  ssh://git@github.com/owner/repo.git (push)`,103    ],104    [105      'GitHub SSH URL remote with explicit port',106      `origin  ssh://git@github.com:22/owner/repo.git (fetch)107origin  ssh://git@github.com:22/owner/repo.git (push)`,108    ],109  ])('returns true for %s', async (_name, remotes) => {110    mockExecFileStdout(remotes);111 112    await expect(isGitHubRepositoryAsync()).resolves.toBe(true);113  });114 115  it('returns true for GitHub remotes without blocking execSync', async () => {116    mockExecFileStdout('origin  https://github.com/owner/repo.git (fetch)\n');117 118    await expect(isGitHubRepositoryAsync()).resolves.toBe(true);119    expect(child_process.execSync).not.toHaveBeenCalled();120  });121});122 123describe('getGitHubRepoInfoAsync', async () => {124  beforeEach(() => {125    vi.resetAllMocks();126  });127 128  afterEach(() => {129    vi.restoreAllMocks();130  });131 132  it('throws an error if github repo info cannot be determined', async () => {133    mockExecFileError(new Error('oops'));134 135    await expect(getGitHubRepoInfoAsync()).rejects.toThrowError(/oops/);136  });137 138  it.each([139    ['empty remote', ''],140    ['non-GitHub SSH URL', 'git@gitlab.com:owner/repo.git'],141    ['non-GitHub HTTPS URL', 'https://gitlab.com/owner/repo.git'],142  ])('throws if owner/repo cannot be determined for %s', async (_name, url) => {143    mockExecFileStdout(url);144 145    await expect(getGitHubRepoInfoAsync()).rejects.toThrowError(146      /Owner & repo could not be extracted from remote URL/,147    );148  });149 150  it.each([151    ['plain HTTPS URL', 'https://github.com/owner/repo.git'],152    [153      'classic PAT token',154      'https://ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@github.com/owner/repo.git',155    ],156    [157      'fine-grained PAT token',158      'https://github_pat_xxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@github.com/owner/repo.git',159    ],160    [161      'username:password credentials',162      'https://username:password@github.com/owner/repo.git',163    ],164    [165      'OAuth token credentials',166      'https://oauth2:gho_xxxxxxxxxxxx@github.com/owner/repo.git',167    ],168    [169      'GitHub Actions token credentials',170      'https://x-access-token:ghs_xxxxxxxxxxxx@github.com/owner/repo.git',171    ],172    ['uppercase host', 'https://GITHUB.COM/owner/repo.git'],173    ['mixed case host', 'https://GitHub.Com/owner/repo.git'],174    ['SCP-style SSH URL', 'git@github.com:owner/repo.git'],175    ['SSH URL with explicit port', 'ssh://git@github.com:22/owner/repo.git'],176    ['URL without .git suffix', 'https://github.com/owner/repo'],177  ])('returns owner and repo for %s', async (_name, url) => {178    mockExecFileStdout(url);179 180    await expect(getGitHubRepoInfoAsync()).resolves.toEqual({181      owner: 'owner',182      repo: 'repo',183    });184  });185 186  it('handles repo names containing .git substring', async () => {187    mockExecFileStdout('https://github.com/owner/my.git.repo.git');188 189    await expect(getGitHubRepoInfoAsync()).resolves.toEqual({190      owner: 'owner',191      repo: 'my.git.repo',192    });193  });194 195  it('returns the owner and repo without blocking execSync', async () => {196    mockExecFileStdout('git@github.com:owner/repo.git\n');197 198    await expect(getGitHubRepoInfoAsync()).resolves.toEqual({199      owner: 'owner',200      repo: 'repo',201    });202    expect(child_process.execSync).not.toHaveBeenCalled();203  });204});205 206describe('getGitRepoRootAsync', async () => {207  beforeEach(() => {208    vi.resetAllMocks();209  });210 211  afterEach(() => {212    vi.restoreAllMocks();213  });214 215  it('throws an error if git root cannot be determined', async () => {216    mockExecFileError(new Error('oops'));217 218    await expect(getGitRepoRootAsync()).rejects.toThrowError(/oops/);219  });220 221  it('throws an error if git root is empty', async () => {222    mockExecFileStdout('');223 224    await expect(getGitRepoRootAsync()).rejects.toThrowError(225      /Git repo returned empty value/,226    );227  });228 229  it('returns the root without blocking execSync', async () => {230    mockExecFileStdout('/path/to/git/repo\n');231 232    await expect(getGitRepoRootAsync()).resolves.toBe('/path/to/git/repo');233    expect(child_process.execSync).not.toHaveBeenCalled();234  });235});236 237describe('getLatestRelease', async () => {238  beforeEach(() => {239    vi.resetAllMocks();240  });241 242  afterEach(() => {243    vi.restoreAllMocks();244  });245 246  it('throws an error if the fetch fails', async () => {247    global.fetch = vi.fn(() => Promise.reject('nope'));248    await expect(getLatestGitHubRelease()).rejects.toThrowError(249      /Unable to determine the latest/,250    );251  });252 253  it('throws an error if the fetch does not return a json body', async () => {254    global.fetch = vi.fn(() =>255      Promise.resolve({256        ok: true,257        json: () => Promise.resolve({ foo: 'bar' }),258      } as Response),259    );260    await expect(getLatestGitHubRelease()).rejects.toThrowError(261      /Unable to determine the latest/,262    );263  });264 265  it('returns the release version', async () => {266    global.fetch = vi.fn(() =>267      Promise.resolve({268        ok: true,269        json: () => Promise.resolve({ tag_name: 'v1.2.3' }),270      } as Response),271    );272    await expect(getLatestGitHubRelease()).resolves.toBe('v1.2.3');273  });274});275 
basant307/AI_Governance_Project · CoolFace