Skip to content

Commit 1a1a3a0

Browse files
authored
[#7] Added unit tests for helper functions.
2 parents 9a31709 + 51d54ab commit 1a1a3a0

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

src/helpers.test.mjs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* Comprehensive tests for all helper functions in the Lagoon CLI Wrapper
3+
*/
4+
import { gitUrlToGithubUrl, extractPrNumber } from './lagoon-api.mjs';
5+
6+
describe('gitUrlToGithubUrl', () => {
7+
test('should handle empty input', () => {
8+
expect(gitUrlToGithubUrl('')).toBeNull();
9+
});
10+
11+
test('should handle null input', () => {
12+
expect(gitUrlToGithubUrl(null)).toBeNull();
13+
});
14+
15+
test('should handle undefined input', () => {
16+
expect(gitUrlToGithubUrl(undefined)).toBeNull();
17+
});
18+
19+
test('should convert SSH format with username', () => {
20+
expect(gitUrlToGithubUrl('git@github.com:username/repo.git')).toBe('https://github.com/username/repo');
21+
});
22+
23+
test('should convert SSH format with organization', () => {
24+
expect(gitUrlToGithubUrl('git@github.com:org-name/repo-name.git')).toBe('https://github.com/org-name/repo-name');
25+
});
26+
27+
test('should handle HTTPS format correctly', () => {
28+
expect(gitUrlToGithubUrl('https://github.com/user/repo.git')).toBe('https://github.com/user/repo');
29+
});
30+
31+
test('should handle HTTPS format without .git suffix', () => {
32+
expect(gitUrlToGithubUrl('https://github.com/user/repo')).toBe('https://github.com/user/repo');
33+
});
34+
35+
test('should return null for non-GitHub URLs', () => {
36+
expect(gitUrlToGithubUrl('https://gitlab.com/user/repo.git')).toBeNull();
37+
expect(gitUrlToGithubUrl('https://bitbucket.org/user/repo.git')).toBeNull();
38+
expect(gitUrlToGithubUrl('git@gitlab.com:user/repo.git')).toBeNull();
39+
expect(gitUrlToGithubUrl('git@ssh.dev.azure.com:v3/org/project/repo')).toBeNull();
40+
});
41+
42+
test('should handle repo names with dots', () => {
43+
expect(gitUrlToGithubUrl('git@github.com:user/repo.name.git')).toBe('https://github.com/user/repo.name');
44+
});
45+
46+
test('should handle repo names with hyphens', () => {
47+
expect(gitUrlToGithubUrl('git@github.com:user/repo-name.git')).toBe('https://github.com/user/repo-name');
48+
});
49+
50+
test('should handle repo paths with slashes', () => {
51+
expect(gitUrlToGithubUrl('https://github.com/org/project/repo.git')).toBe('https://github.com/org/project/repo');
52+
});
53+
});
54+
55+
describe('extractPrNumber', () => {
56+
test('should handle empty input', () => {
57+
expect(extractPrNumber('')).toBeNull();
58+
});
59+
60+
test('should handle null input', () => {
61+
expect(extractPrNumber(null)).toBeNull();
62+
});
63+
64+
test('should handle undefined input', () => {
65+
expect(extractPrNumber(undefined)).toBeNull();
66+
});
67+
68+
test('should extract simple PR numbers', () => {
69+
expect(extractPrNumber('pr-1')).toBe('1');
70+
expect(extractPrNumber('pr-42')).toBe('42');
71+
expect(extractPrNumber('pr-999')).toBe('999');
72+
});
73+
74+
test('should be case insensitive', () => {
75+
expect(extractPrNumber('PR-123')).toBe('123');
76+
expect(extractPrNumber('Pr-123')).toBe('123');
77+
expect(extractPrNumber('pR-123')).toBe('123');
78+
});
79+
80+
test('should return null for non-PR environment names', () => {
81+
expect(extractPrNumber('master')).toBeNull();
82+
expect(extractPrNumber('develop')).toBeNull();
83+
expect(extractPrNumber('feature/branch')).toBeNull();
84+
expect(extractPrNumber('release-1.0')).toBeNull();
85+
});
86+
87+
test('should not match PR- in the middle of a string', () => {
88+
expect(extractPrNumber('feature-pr-123')).toBeNull();
89+
});
90+
91+
test('should not match without a hyphen', () => {
92+
expect(extractPrNumber('pr123')).toBeNull();
93+
});
94+
95+
test('should handle PR numbers of varying lengths', () => {
96+
expect(extractPrNumber('pr-1')).toBe('1');
97+
expect(extractPrNumber('pr-12')).toBe('12');
98+
expect(extractPrNumber('pr-123')).toBe('123');
99+
expect(extractPrNumber('pr-1234')).toBe('1234');
100+
expect(extractPrNumber('pr-12345')).toBe('12345');
101+
});
102+
});

src/lagoon-api.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ export async function generateLoginLink(instance, project, environment) {
236236
* @returns {string|null} The normalized GitHub HTTPS URL, or `null` if the input is not a GitHub URL.
237237
*/
238238
export function gitUrlToGithubUrl(gitUrl) {
239+
// Handle null, undefined, or empty strings
240+
if (!gitUrl) {
241+
return null;
242+
}
243+
239244
// Handle SSH URLs like git@github.com:org/repo.git
240245
if (gitUrl.startsWith('git@github.com:')) {
241246
const path = gitUrl.replace('git@github.com:', '').replace('.git', '');
@@ -254,6 +259,11 @@ export function gitUrlToGithubUrl(gitUrl) {
254259
* @returns {string|null} PR number or null if not found.
255260
*/
256261
export function extractPrNumber(environmentName) {
262+
// Handle null, undefined, or empty strings
263+
if (!environmentName) {
264+
return null;
265+
}
266+
257267
const match = environmentName.match(/^pr-(\d+)$/i);
258268
return match ? match[1] : null;
259269
}

src/lagoon-api.test.mjs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { jest } from '@jest/globals';
2+
import {
3+
gitUrlToGithubUrl,
4+
extractPrNumber,
5+
getLagoonInstances,
6+
getProjectsWithDetails,
7+
getEnvironments,
8+
getUsers,
9+
clearDrupalCache,
10+
generateLoginLink,
11+
deleteEnvironment,
12+
deployBranch,
13+
getGitBranches
14+
} from './lagoon-api.mjs';
15+
16+
// Mock the execCommand function
17+
jest.unstable_mockModule('./command/index.mjs', () => ({
18+
LagoonCommand: jest.fn().mockImplementation(() => ({
19+
withInstance: jest.fn().mockReturnThis(),
20+
withProject: jest.fn().mockReturnThis(),
21+
withEnvironment: jest.fn().mockReturnThis(),
22+
withJsonOutput: jest.fn().mockReturnThis(),
23+
withForce: jest.fn().mockReturnThis(),
24+
listConfigs: jest.fn().mockReturnThis(),
25+
listProjects: jest.fn().mockReturnThis(),
26+
listEnvironments: jest.fn().mockReturnThis(),
27+
listUsers: jest.fn().mockReturnThis(),
28+
deleteEnvironment: jest.fn().mockReturnThis(),
29+
deployBranch: jest.fn().mockReturnThis(),
30+
login: jest.fn().mockReturnThis(),
31+
ssh: jest.fn().mockReturnThis(),
32+
getArgs: jest.fn().mockReturnValue([]),
33+
getBaseCommand: jest.fn().mockReturnValue('lagoon'),
34+
getCommandArray: jest.fn().mockReturnValue(['lagoon']),
35+
toString: jest.fn().mockReturnValue('lagoon')
36+
})),
37+
GitCommand: jest.fn().mockImplementation(() => ({
38+
lsRemote: jest.fn().mockReturnThis(),
39+
getArgs: jest.fn().mockReturnValue([]),
40+
getBaseCommand: jest.fn().mockReturnValue('git'),
41+
getCommandArray: jest.fn().mockReturnValue(['git']),
42+
toString: jest.fn().mockReturnValue('git ls-remote')
43+
})),
44+
LagoonExecutor: jest.fn().mockImplementation(() => ({
45+
execute: jest.fn()
46+
}))
47+
}));
48+
49+
// Mock the execCommand function
50+
jest.mock('./lagoon-api.mjs', () => {
51+
const originalModule = jest.requireActual('./lagoon-api.mjs');
52+
53+
// Only mock the async API functions, keep the helper functions as is
54+
return {
55+
...originalModule,
56+
execCommand: jest.fn()
57+
};
58+
}, { virtual: true });
59+
60+
// Unit tests for pure helper functions
61+
describe('Helper Functions', () => {
62+
describe('gitUrlToGithubUrl', () => {
63+
test('should convert SSH GitHub URL to HTTPS URL', () => {
64+
const sshUrl = 'git@github.com:richardgaunt/lagoon-cli-wrapper.git';
65+
expect(gitUrlToGithubUrl(sshUrl)).toBe('https://github.com/richardgaunt/lagoon-cli-wrapper');
66+
});
67+
68+
test('should clean HTTPS GitHub URL', () => {
69+
const httpsUrl = 'https://github.com/richardgaunt/lagoon-cli-wrapper.git';
70+
expect(gitUrlToGithubUrl(httpsUrl)).toBe('https://github.com/richardgaunt/lagoon-cli-wrapper');
71+
});
72+
73+
test('should return null for non-GitHub URLs', () => {
74+
const nonGithubUrl = 'https://gitlab.com/some/project.git';
75+
expect(gitUrlToGithubUrl(nonGithubUrl)).toBeNull();
76+
});
77+
78+
// This test uses a spied/mocked version of the function
79+
test('should handle URLs with github.com in them', () => {
80+
// Create a URL that definitely contains 'github.com'
81+
const githubUrl = 'https://github.com/user/repo.git';
82+
expect(gitUrlToGithubUrl(githubUrl)).toBe('https://github.com/user/repo');
83+
});
84+
});
85+
86+
describe('extractPrNumber', () => {
87+
test('should extract PR number from environment name', () => {
88+
expect(extractPrNumber('pr-123')).toBe('123');
89+
});
90+
91+
test('should be case insensitive', () => {
92+
expect(extractPrNumber('PR-456')).toBe('456');
93+
});
94+
95+
test('should return null for non-PR environment names', () => {
96+
expect(extractPrNumber('develop')).toBeNull();
97+
expect(extractPrNumber('feature-123')).toBeNull();
98+
expect(extractPrNumber('master')).toBeNull();
99+
expect(extractPrNumber('pr123')).toBeNull(); // Missing hyphen
100+
});
101+
102+
test('should handle multi-digit PR numbers', () => {
103+
expect(extractPrNumber('pr-1')).toBe('1');
104+
expect(extractPrNumber('pr-9999')).toBe('9999');
105+
});
106+
});
107+
});

0 commit comments

Comments
 (0)