Files

199 lines
8.5 KiB
JavaScript

import fs from 'fs';
import os from 'os';
import path from 'path';
import { afterAll, afterEach, describe, expect, test } from 'vitest';
const TEMP_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-git-providers-'));
process.env.OPENCHAMBER_DATA_DIR = TEMP_DATA_DIR;
const {
GIT_PROVIDER_DEFAULTS,
GIT_PROVIDER_DEFAULT_DETECT_URLS,
normalizeBaseUrl,
normalizeDetectionHost,
sanitizeGitProviders,
readGitProvidersConfig,
getProviderApiBaseUrl,
getProviderDetectUrls,
githubWebOriginFromApiBase,
} = await import('./config.js');
const SETTINGS_FILE = path.join(TEMP_DATA_DIR, 'settings.json');
afterAll(() => {
fs.rmSync(TEMP_DATA_DIR, { recursive: true, force: true });
});
afterEach(() => {
if (fs.existsSync(SETTINGS_FILE)) {
fs.unlinkSync(SETTINGS_FILE);
}
});
describe('normalizeBaseUrl', () => {
test('adds https scheme when missing', () => {
expect(normalizeBaseUrl('github.example.com')).toBe('https://github.example.com');
expect(normalizeBaseUrl('gitlab.example.com/gitlab')).toBe('https://gitlab.example.com/gitlab');
});
test('strips trailing slashes but preserves subpaths', () => {
expect(normalizeBaseUrl('https://github.example.com/api/v3/')).toBe('https://github.example.com/api/v3');
expect(normalizeBaseUrl('https://gitlab.example.com/')).toBe('https://gitlab.example.com');
expect(normalizeBaseUrl('https://gitlab.example.com/gitlab/')).toBe('https://gitlab.example.com/gitlab');
});
test('keeps an explicit non-https scheme', () => {
expect(normalizeBaseUrl('http://localhost:8080')).toBe('http://localhost:8080');
});
test('returns null for empty or unparseable input', () => {
expect(normalizeBaseUrl('')).toBeNull();
expect(normalizeBaseUrl(' ')).toBeNull();
expect(normalizeBaseUrl('not a url')).toBeNull();
expect(normalizeBaseUrl(null)).toBeNull();
expect(normalizeBaseUrl(undefined)).toBeNull();
expect(normalizeBaseUrl(42)).toBeNull();
});
});
describe('normalizeDetectionHost', () => {
test('extracts the host from https remotes', () => {
expect(normalizeDetectionHost('https://Github.Example.com/owner/repo.git')).toBe('github.example.com');
expect(normalizeDetectionHost('https://github.com/owner/repo')).toBe('github.com');
});
test('extracts the host from scp-like and ssh remotes', () => {
expect(normalizeDetectionHost('git@github.example.com:owner/repo.git')).toBe('github.example.com');
expect(normalizeDetectionHost('ssh://git@github.example.com/owner/repo.git')).toBe('github.example.com');
expect(normalizeDetectionHost('github.example.com:owner/repo.git')).toBe('github.example.com');
});
test('handles ports, user info, and IPv6', () => {
expect(normalizeDetectionHost('https://github.example.com:8443/owner/repo')).toBe('github.example.com');
expect(normalizeDetectionHost('ssh://user@host.example.com/owner/repo')).toBe('host.example.com');
expect(normalizeDetectionHost('[2001:db8::1]:owner/repo.git')).toBe('2001:db8::1');
expect(normalizeDetectionHost('2001:db8::1')).toBe('2001:db8::1');
});
test('returns null for empty or unparseable input', () => {
expect(normalizeDetectionHost('')).toBeNull();
expect(normalizeDetectionHost(null)).toBeNull();
expect(normalizeDetectionHost(42)).toBeNull();
expect(normalizeDetectionHost('C:\\foo')).toBeNull();
});
});
describe('sanitizeGitProviders', () => {
test('normalizes a valid payload', () => {
expect(sanitizeGitProviders({
github: { apiBaseUrl: 'https://github.example.com/api/v3', detectUrls: ['https://github.example.com/owner/repo.git'] },
gitlab: { apiBaseUrl: 'gitlab.example.com', detectUrls: [] },
gitea: { apiBaseUrl: '', detectUrls: ['gitea.example.com'] },
})).toEqual({
github: { apiBaseUrl: 'https://github.example.com/api/v3', detectUrls: ['github.example.com'] },
gitlab: { apiBaseUrl: 'https://gitlab.example.com' },
gitea: { detectUrls: ['gitea.example.com'] },
});
});
test('dedupes and lowercases detectUrls', () => {
expect(sanitizeGitProviders({
github: { detectUrls: ['GitHub.Example.com', 'https://github.example.com/x', 'other.example.com', 'other.example.com'] },
})).toEqual({
github: { detectUrls: ['github.example.com', 'other.example.com'] },
});
});
test('drops malformed or empty entries', () => {
expect(sanitizeGitProviders({ github: { apiBaseUrl: ' ' } })).toBeUndefined();
expect(sanitizeGitProviders({ github: { detectUrls: 'not-an-array' } })).toBeUndefined();
expect(sanitizeGitProviders({ unknown: { apiBaseUrl: 'https://x.example.com' } })).toBeUndefined();
expect(sanitizeGitProviders('not-an-object')).toBeUndefined();
expect(sanitizeGitProviders(null)).toBeUndefined();
expect(sanitizeGitProviders([])).toBeUndefined();
});
test('ignores unknown provider keys', () => {
expect(sanitizeGitProviders({
github: { apiBaseUrl: 'https://github.example.com' },
bitbucket: { apiBaseUrl: 'https://bitbucket.example.com' },
})).toEqual({
github: { apiBaseUrl: 'https://github.example.com' },
});
});
});
describe('readGitProvidersConfig / getProviderApiBaseUrl', () => {
test('returns {} / defaults when no settings file exists', () => {
expect(readGitProvidersConfig()).toEqual({});
expect(getProviderApiBaseUrl('github')).toBe('https://api.github.com');
expect(getProviderApiBaseUrl('gitlab')).toBe('https://gitlab.com');
expect(getProviderApiBaseUrl('gitea')).toBe('https://codeberg.org');
});
test('reads the configured values from settings.json', () => {
fs.writeFileSync(SETTINGS_FILE, JSON.stringify({
gitProviders: {
github: { apiBaseUrl: 'https://github.example.com/api/v3' },
gitlab: { apiBaseUrl: 'https://gitlab.example.com' },
},
}));
expect(readGitProvidersConfig()).toEqual({
github: { apiBaseUrl: 'https://github.example.com/api/v3' },
gitlab: { apiBaseUrl: 'https://gitlab.example.com' },
});
expect(getProviderApiBaseUrl('github')).toBe('https://github.example.com/api/v3');
expect(getProviderApiBaseUrl('gitlab')).toBe('https://gitlab.example.com');
expect(getProviderApiBaseUrl('gitea')).toBe('https://codeberg.org');
});
test('never throws on a malformed settings file', () => {
fs.writeFileSync(SETTINGS_FILE, '{not-json');
expect(readGitProvidersConfig()).toEqual({});
expect(getProviderApiBaseUrl('github')).toBe(GIT_PROVIDER_DEFAULTS.github);
});
});
describe('getProviderDetectUrls', () => {
test('returns the built-in default hosts when nothing is configured', () => {
expect(getProviderDetectUrls('github')).toEqual(['github.com']);
expect(getProviderDetectUrls('gitlab')).toEqual(['gitlab.com']);
expect(getProviderDetectUrls('gitea')).toEqual(['codeberg.org']);
expect(GIT_PROVIDER_DEFAULT_DETECT_URLS.gitea).toEqual(['codeberg.org']);
});
test('keeps the built-in hosts and appends configured detectUrls', () => {
fs.writeFileSync(SETTINGS_FILE, JSON.stringify({
gitProviders: {
github: { detectUrls: ['github.example.com'] },
gitea: { detectUrls: ['gitea.example.com', 'codeberg.org'] },
},
}));
expect(getProviderDetectUrls('github')).toEqual(['github.com', 'github.example.com']);
expect(getProviderDetectUrls('gitea')).toEqual(['codeberg.org', 'gitea.example.com']);
});
});
describe('githubWebOriginFromApiBase', () => {
test('maps the public api host to github.com', () => {
expect(githubWebOriginFromApiBase('https://api.github.com')).toBe('https://github.com');
});
test('maps enterprise api bases to the host', () => {
expect(githubWebOriginFromApiBase('https://github.example.com/api/v3')).toBe('https://github.example.com');
expect(githubWebOriginFromApiBase('https://github.example.com/api')).toBe('https://github.example.com');
});
test('keeps subpath prefixes and plain origins', () => {
expect(githubWebOriginFromApiBase('https://github.example.com/ghe/api/v3')).toBe('https://github.example.com/ghe');
expect(githubWebOriginFromApiBase('https://github.example.com')).toBe('https://github.example.com');
expect(githubWebOriginFromApiBase('https://github.example.com:8443/api/v3')).toBe('https://github.example.com:8443');
});
test('falls back for invalid input and never throws', () => {
expect(githubWebOriginFromApiBase('')).toBe('https://github.com');
expect(githubWebOriginFromApiBase(null)).toBe('https://github.com');
expect(githubWebOriginFromApiBase('not a url')).toBe('https://github.com');
});
});