Skip to content

Commit 36a862b

Browse files
committed
cleanup and test config
1 parent bafd72f commit 36a862b

File tree

9 files changed

+148
-18
lines changed

9 files changed

+148
-18
lines changed

web-server/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module.exports = {
22
preset: 'ts-jest/presets/js-with-babel', // Use the TypeScript preset with Babel
33
testEnvironment: 'jsdom', // Use jsdom as the test environment (for browser-like behavior)
4+
setupFiles: ['<rootDir>/jest.setup.js'],
45
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
56
testMatch: [
67
'**/__tests__/**/*.test.(ts|tsx|js|jsx)',

web-server/jest.setup.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { TextEncoder, TextDecoder } = require('util');
2+
global.TextEncoder = TextEncoder;
3+
global.TextDecoder = TextDecoder;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
jest.mock('@/utils/db', () => ({
2+
db: jest.fn(),
3+
}));
4+
5+
import { db } from '@/utils/db';
6+
import * as githubUtils from '../utils';
7+
import { DEFAULT_GH_URL } from '@/constants/urls';
8+
9+
describe('GitHub URL utilities', () => {
10+
afterEach(() => {
11+
jest.resetAllMocks();
12+
});
13+
14+
describe('getGitHubCustomDomain', () => {
15+
it('returns custom_domain when present', async () => {
16+
const mockMeta = [{ custom_domain: 'custom.sujai.com' }];
17+
(db as jest.Mock).mockReturnValue({
18+
where: jest.fn().mockReturnThis(),
19+
then: jest.fn().mockResolvedValue(mockMeta)
20+
});
21+
22+
const domain = await githubUtils.getGitHubCustomDomain();
23+
expect(domain).toBe('custom.sujai.com');
24+
});
25+
26+
it('returns null when no provider_meta found', async () => {
27+
(db as jest.Mock).mockReturnValue({
28+
where: jest.fn().mockReturnThis(),
29+
then: jest.fn().mockResolvedValue([])
30+
});
31+
32+
const domain = await githubUtils.getGitHubCustomDomain();
33+
expect(domain).toBeNull();
34+
});
35+
36+
it('returns null on db error and logs error', async () => {
37+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
38+
(db as jest.Mock).mockImplementation(() => {
39+
throw new Error('DB failure');
40+
});
41+
42+
const domain = await githubUtils.getGitHubCustomDomain();
43+
expect(domain).toBeNull();
44+
expect(consoleSpy).toHaveBeenCalledWith(
45+
'Error occured while getting custom domain from database:',
46+
expect.any(Error)
47+
);
48+
});
49+
});
50+
51+
describe('getGitHubRestApiUrl', () => {
52+
it('uses default URL when no custom domain', async () => {
53+
jest.spyOn(githubUtils, 'getGitHubCustomDomain').mockResolvedValue(null);
54+
const url = await githubUtils.getGitHubRestApiUrl('path/to/repo');
55+
expect(url).toBe(`${DEFAULT_GH_URL}/path/to/repo`);
56+
});
57+
58+
it('uses custom domain when provided', async () => {
59+
jest.spyOn(githubUtils, 'getGitHubCustomDomain').mockResolvedValue('git.sujai.com');
60+
const url = await githubUtils.getGitHubRestApiUrl('repos/owner/repo');
61+
expect(url).toBe('https://git.sujai.com/api/v3/repos/owner/repo');
62+
});
63+
64+
it('normalizes multiple slashes in URL', async () => {
65+
jest.spyOn(githubUtils, 'getGitHubCustomDomain').mockResolvedValue('git.sujai.com/');
66+
const url = await githubUtils.getGitHubRestApiUrl('/repos//owner//repo');
67+
expect(url).toBe('https://git.sujai.com/api/v3/repos/owner/repo');
68+
});
69+
});
70+
71+
describe('getGitHubGraphQLUrl', () => {
72+
it('uses default GraphQL endpoint when no custom domain', async () => {
73+
jest.spyOn(githubUtils, 'getGitHubCustomDomain').mockResolvedValue(null);
74+
const url = await githubUtils.getGitHubGraphQLUrl();
75+
expect(url).toBe(`${DEFAULT_GH_URL}/graphql`);
76+
});
77+
78+
it('uses custom domain for GraphQL endpoint', async () => {
79+
jest.spyOn(githubUtils, 'getGitHubCustomDomain').mockResolvedValue('api.github.local');
80+
const url = await githubUtils.getGitHubGraphQLUrl();
81+
expect(url).toBe('https://api.github.local/api/graphql');
82+
});
83+
});
84+
});

web-server/pages/api/internal/[org_id]/utils.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { Row } from '@/constants/db';
55
import { Integration } from '@/constants/integrations';
66
import { BaseRepo } from '@/types/resources';
77
import { db } from '@/utils/db';
8-
9-
const GITHUB_API_URL = 'https://api.github.com';
8+
import { DEFAULT_GH_URL } from '@/constants/urls';
109

1110
type GithubRepo = {
1211
name: string;
@@ -307,7 +306,7 @@ const replaceURL = async (url: string): Promise<string> => {
307306
return url;
308307
};
309308

310-
const getGitHubCustomDomain = async (): Promise<string | null> => {
309+
export const getGitHubCustomDomain = async (): Promise<string | null> => {
311310
try {
312311
const provider_meta = await db('Integration')
313312
.where('name', Integration.GITHUB)
@@ -320,14 +319,19 @@ const getGitHubCustomDomain = async (): Promise<string | null> => {
320319
}
321320
};
322321

322+
const normalizeSlashes = (url: string) =>
323+
url.replace(/(?<!:)\/{2,}/g, '/');
323324

324-
const getGitHubRestApiUrl = async (path: string): Promise<string> => {
325+
export const getGitHubRestApiUrl = async (path: string) => {
325326
const customDomain = await getGitHubCustomDomain();
326-
const baseUrl = customDomain ? `https://${customDomain}/api/v3` : GITHUB_API_URL;
327-
return `${baseUrl}/${path}`.replace(/\/+/g, '/');
327+
const base = customDomain
328+
? `https://${customDomain}/api/v3`
329+
: DEFAULT_GH_URL;
330+
return normalizeSlashes(`${base}/${path}`);
328331
};
329332

330-
const getGitHubGraphQLUrl = async (): Promise<string> => {
333+
334+
export const getGitHubGraphQLUrl = async (): Promise<string> => {
331335
const customDomain = await getGitHubCustomDomain();
332-
return customDomain ? `https://${customDomain}/api/graphql` : GITHUB_API_URL;
336+
return customDomain ? `https://${customDomain}/api/graphql` : `${DEFAULT_GH_URL}/graphql`;
333337
};

web-server/src/constants/urls.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const DEFAULT_GH_URL = 'https://api.githb.com';

web-server/src/content/Dashboards/ConfigureGithubModalBody.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
linkProvider,
1818
getMissingPATScopes
1919
} from '@/utils/auth';
20+
import { checkDomainWithRegex } from '@/utils/domainCheck';
2021
import { depFn } from '@/utils/fn';
2122

2223
export const ConfigureGithubModalBody: FC<{
@@ -54,11 +55,6 @@ export const ConfigureGithubModalBody: FC<{
5455
customDomain.set(e);
5556
showDomainError.set('');
5657
};
57-
const checkDomainWithRegex = (domain: string) => {
58-
const regex =
59-
/^(https?:\/\/)[a-zA-Z0-9]+([-.][a-zA-Z0-9]+)*\.[a-zA-Z]{2,}(:[0-9]{1,5})?(\/.*)?$/;
60-
return regex.test(domain);
61-
};
6258

6359
const handleSubmission = useCallback(async () => {
6460
if (!token.value) {

web-server/src/content/Dashboards/ConfigureGitlabModalBody.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
checkGitLabValidity,
1818
getMissingGitLabScopes
1919
} from '@/utils/auth';
20+
import { checkDomainWithRegex } from '@/utils/domainCheck';
2021
import { depFn } from '@/utils/fn';
2122

2223
export const ConfigureGitlabModalBody: FC<{
@@ -46,11 +47,6 @@ export const ConfigureGitlabModalBody: FC<{
4647
[showDomainError.set]
4748
);
4849

49-
const checkDomainWithRegex = (domain: string) => {
50-
const regex =
51-
/^(https?:\/\/)[a-zA-Z0-9]+([-.][a-zA-Z0-9]+)*\.[a-zA-Z]{2,}(:[0-9]{1,5})?(\/.*)?$/;
52-
return regex.test(domain);
53-
};
5450
const handleTokenChange = (e: string) => {
5551
token.set(e);
5652
showScopeError.set('');
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { checkDomainWithRegex } from '../domainCheck';
2+
3+
describe('checkDomainWithRegex', () => {
4+
const validDomains = [
5+
'http://example.com',
6+
'https://example.com',
7+
'https://sub.example.co.uk',
8+
'http://example.io:8080',
9+
'https://example.io:8080',
10+
'https://example.com/',
11+
'https://123domain.net',
12+
'http://my-domain.org'
13+
];
14+
15+
test.each(validDomains)('returns true for %s', (domain) => {
16+
expect(checkDomainWithRegex(domain)).toBe(true);
17+
});
18+
19+
const invalidDomains = [
20+
'example.com',
21+
'ftp://example.com',
22+
'http:/example.com',
23+
'https//example.com',
24+
'https://-example.com',
25+
'https://example-.com',
26+
'https://example',
27+
'https://.com',
28+
'https://example:toolongtsadasds',
29+
'https://example.com:999999',
30+
'https://example .com',
31+
'https://example.com/ path',
32+
'',
33+
'https://',
34+
'https:///'
35+
];
36+
37+
test.each(invalidDomains)('returns false for %s', (domain) => {
38+
expect(checkDomainWithRegex(domain)).toBe(false);
39+
});
40+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const checkDomainWithRegex = (domain: string) => {
2+
const regex =
3+
/^(https?:\/\/)[A-Za-z0-9]+([-.][A-Za-z0-9]+)*\.[A-Za-z]{2,}(:[0-9]{1,5})?(\/\S*)?$/;
4+
return regex.test(domain);
5+
};

0 commit comments

Comments
 (0)