Skip to content

Commit b5ae382

Browse files
committed
test: Improve tests
1 parent 8395f42 commit b5ae382

File tree

5 files changed

+294
-419
lines changed

5 files changed

+294
-419
lines changed

packages/cli/src/environments.ee/source-control/__tests__/source-control-git.service.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ describe('SourceControlGitService', () => {
124124

125125
// Mock the getPrivateKeyPath to return a Windows path
126126
mockPreferencesService.getPrivateKeyPath.mockResolvedValue(windowsPath);
127+
// Mock getPreferences to return SSH connection type (required for new functionality)
128+
mockPreferencesService.getPreferences.mockReturnValue({
129+
connectionType: 'ssh',
130+
connected: true,
131+
repositoryUrl: 'git@github.com:user/repo.git',
132+
branchName: 'main',
133+
branchReadOnly: false,
134+
branchColor: '#5296D6',
135+
initRepo: false,
136+
keyGeneratorType: 'ed25519',
137+
});
127138

128139
const gitService = new SourceControlGitService(mock(), mock(), mockPreferencesService);
129140

@@ -154,6 +165,17 @@ describe('SourceControlGitService', () => {
154165

155166
// Mock the getPrivateKeyPath to return a path with spaces
156167
mockPreferencesService.getPrivateKeyPath.mockResolvedValue(privateKeyPath);
168+
// Mock getPreferences to return SSH connection type
169+
mockPreferencesService.getPreferences.mockReturnValue({
170+
connectionType: 'ssh',
171+
connected: true,
172+
repositoryUrl: 'git@github.com:user/repo.git',
173+
branchName: 'main',
174+
branchReadOnly: false,
175+
branchColor: '#5296D6',
176+
initRepo: false,
177+
keyGeneratorType: 'ed25519',
178+
});
157179

158180
const gitService = new SourceControlGitService(mock(), mock(), mockPreferencesService);
159181

@@ -187,6 +209,17 @@ describe('SourceControlGitService', () => {
187209

188210
// Mock the getPrivateKeyPath to return a path with quotes
189211
mockPreferencesService.getPrivateKeyPath.mockResolvedValue(pathWithQuotes);
212+
// Mock getPreferences to return SSH connection type
213+
mockPreferencesService.getPreferences.mockReturnValue({
214+
connectionType: 'ssh',
215+
connected: true,
216+
repositoryUrl: 'git@github.com:user/repo.git',
217+
branchName: 'main',
218+
branchReadOnly: false,
219+
branchColor: '#5296D6',
220+
initRepo: false,
221+
keyGeneratorType: 'ed25519',
222+
});
190223

191224
const gitService = new SourceControlGitService(mock(), mock(), mockPreferencesService);
192225

packages/cli/src/environments.ee/source-control/__tests__/source-control-https.service.ee.test.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ import type { SourceControlPreferences } from '../types/source-control-preferenc
66

77
// Mock simple-git
88
const mockSimpleGit = {
9-
env: jest.fn().mockReturnThis(),
9+
env: jest.fn(),
1010
init: jest.fn().mockResolvedValue(undefined),
1111
addRemote: jest.fn().mockResolvedValue(undefined),
1212
getRemotes: jest.fn().mockResolvedValue([]),
1313
};
1414

15+
// Set up chaining for env method
16+
mockSimpleGit.env.mockReturnValue(mockSimpleGit);
17+
1518
jest.mock('simple-git', () => ({
16-
simpleGit: jest.fn().mockImplementation(() => mockSimpleGit),
19+
simpleGit: jest.fn().mockReturnValue(mockSimpleGit),
1720
}));
1821

1922
describe('SourceControlGitService - HTTPS functionality', () => {
@@ -37,10 +40,17 @@ describe('SourceControlGitService - HTTPS functionality', () => {
3740

3841
jest.spyOn(sourceControlPreferencesService, 'getPreferences').mockReturnValue(mockPreferences);
3942
jest.clearAllMocks();
43+
44+
// Reset the mock chaining after clearAllMocks
45+
mockSimpleGit.env.mockReturnValue(mockSimpleGit);
4046
});
4147

4248
afterEach(() => {
43-
jest.resetAllMocks();
49+
// Don't reset all mocks to preserve simple-git module mock
50+
mockSimpleGit.env.mockClear();
51+
mockSimpleGit.init.mockClear();
52+
mockSimpleGit.addRemote.mockClear();
53+
mockSimpleGit.getRemotes.mockClear();
4454
});
4555

4656
describe('setGitSshCommand', () => {
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { mock } from 'jest-mock-extended';
2+
3+
import { SourceControlService } from '../source-control.service.ee';
4+
import type { SourceControlGitService } from '../source-control-git.service.ee';
5+
import type { SourceControlPreferencesService } from '../source-control-preferences.service.ee';
6+
import type { SourceControlPreferences } from '../types/source-control-preferences';
7+
8+
describe('SourceControl Integration Tests', () => {
9+
let sourceControlService: SourceControlService;
10+
let mockGitService: SourceControlGitService;
11+
let mockPreferencesService: SourceControlPreferencesService;
12+
13+
beforeEach(() => {
14+
mockGitService = mock<SourceControlGitService>();
15+
mockPreferencesService = mock<SourceControlPreferencesService>();
16+
17+
sourceControlService = new SourceControlService(
18+
mock(),
19+
mockGitService,
20+
mockPreferencesService,
21+
mock(),
22+
mock(),
23+
mock(),
24+
mock(),
25+
mock(),
26+
mock(),
27+
);
28+
});
29+
30+
describe('HTTPS vs SSH Integration', () => {
31+
it('should handle HTTPS connection flow', async () => {
32+
// Arrange
33+
const httpsPrefs: SourceControlPreferences = {
34+
connected: true,
35+
repositoryUrl: 'https://github.com/user/repo.git',
36+
branchName: 'main',
37+
branchReadOnly: false,
38+
branchColor: '#5296D6',
39+
connectionType: 'https',
40+
initRepo: false,
41+
keyGeneratorType: 'ed25519',
42+
};
43+
44+
jest.spyOn(mockPreferencesService, 'getPreferences').mockReturnValue(httpsPrefs);
45+
jest.spyOn(mockPreferencesService, 'getDecryptedHttpsCredentials').mockResolvedValue({
46+
username: 'testuser',
47+
password: 'testtoken',
48+
});
49+
50+
// Act & Assert
51+
expect(httpsPrefs.connectionType).toBe('https');
52+
expect(mockPreferencesService.getPreferences().connectionType).toBe('https');
53+
});
54+
55+
it('should handle SSH connection flow', async () => {
56+
// Arrange
57+
const sshPrefs: SourceControlPreferences = {
58+
connected: true,
59+
repositoryUrl: 'git@github.com:user/repo.git',
60+
branchName: 'main',
61+
branchReadOnly: false,
62+
branchColor: '#5296D6',
63+
connectionType: 'ssh',
64+
initRepo: false,
65+
keyGeneratorType: 'ed25519',
66+
publicKey: 'ssh-ed25519 AAAAC3NzaC1...',
67+
};
68+
69+
jest.spyOn(mockPreferencesService, 'getPreferences').mockReturnValue(sshPrefs);
70+
71+
// Act & Assert
72+
expect(sshPrefs.connectionType).toBe('ssh');
73+
expect(mockPreferencesService.getPreferences().connectionType).toBe('ssh');
74+
expect(sshPrefs.publicKey).toBeDefined();
75+
});
76+
});
77+
78+
describe('Connection Type Switching', () => {
79+
it('should support switching from SSH to HTTPS', () => {
80+
// Arrange
81+
const initialSSHPrefs: SourceControlPreferences = {
82+
connected: false,
83+
repositoryUrl: 'git@github.com:user/repo.git',
84+
branchName: '',
85+
branchReadOnly: false,
86+
branchColor: '#5296D6',
87+
connectionType: 'ssh',
88+
initRepo: false,
89+
keyGeneratorType: 'ed25519',
90+
};
91+
92+
const updatedHTTPSPrefs: SourceControlPreferences = {
93+
...initialSSHPrefs,
94+
repositoryUrl: 'https://github.com/user/repo.git',
95+
connectionType: 'https',
96+
};
97+
98+
// Act
99+
const isValidTransition =
100+
initialSSHPrefs.connectionType === 'ssh' && updatedHTTPSPrefs.connectionType === 'https';
101+
102+
// Assert
103+
expect(isValidTransition).toBe(true);
104+
expect(updatedHTTPSPrefs.repositoryUrl.startsWith('https://')).toBe(true);
105+
});
106+
107+
it('should validate repository URL format matches connection type', () => {
108+
// Test cases for URL validation
109+
const testCases = [
110+
{ url: 'https://github.com/user/repo.git', connectionType: 'https', expected: true },
111+
{ url: 'git@github.com:user/repo.git', connectionType: 'ssh', expected: true },
112+
{ url: 'https://github.com/user/repo.git', connectionType: 'ssh', expected: false },
113+
{ url: 'git@github.com:user/repo.git', connectionType: 'https', expected: false },
114+
];
115+
116+
testCases.forEach(({ url, connectionType, expected }) => {
117+
const isHTTPSUrl = url.startsWith('https://');
118+
const isSSHUrl = url.startsWith('git@') || url.startsWith('ssh://');
119+
120+
const isValid =
121+
(connectionType === 'https' && isHTTPSUrl) || (connectionType === 'ssh' && isSSHUrl);
122+
123+
expect(isValid).toBe(expected);
124+
});
125+
});
126+
});
127+
128+
describe('Credential Management', () => {
129+
it('should handle HTTPS credentials securely', async () => {
130+
// Arrange
131+
const credentials = { username: 'testuser', password: 'secret' };
132+
133+
jest
134+
.spyOn(mockPreferencesService, 'getDecryptedHttpsCredentials')
135+
.mockResolvedValue(credentials);
136+
137+
// Act
138+
const retrievedCredentials = await mockPreferencesService.getDecryptedHttpsCredentials();
139+
140+
// Assert
141+
expect(retrievedCredentials).toEqual(credentials);
142+
expect(mockPreferencesService.getDecryptedHttpsCredentials).toHaveBeenCalled();
143+
});
144+
145+
it('should clean up credentials on disconnect', async () => {
146+
// Arrange
147+
jest.spyOn(mockPreferencesService, 'deleteHttpsCredentials').mockResolvedValue();
148+
149+
// Act
150+
await mockPreferencesService.deleteHttpsCredentials();
151+
152+
// Assert
153+
expect(mockPreferencesService.deleteHttpsCredentials).toHaveBeenCalled();
154+
});
155+
});
156+
157+
describe('URL Encoding and Security', () => {
158+
it('should properly encode URLs with credentials', () => {
159+
// Arrange
160+
const baseUrl = 'https://github.com/user/repo.git';
161+
const username = 'user@domain.com';
162+
const password = 'p@ssw0rd!';
163+
164+
// Act
165+
const urlWithCredentials = new URL(baseUrl);
166+
urlWithCredentials.username = encodeURIComponent(username);
167+
urlWithCredentials.password = encodeURIComponent(password);
168+
const encodedUrl = urlWithCredentials.toString();
169+
170+
// Assert
171+
expect(encodedUrl).toContain(encodeURIComponent(username));
172+
expect(encodedUrl).toContain('github.com/user/repo.git');
173+
expect(encodedUrl.startsWith('https://')).toBe(true);
174+
});
175+
176+
it('should normalize URLs for comparison', () => {
177+
// Arrange
178+
const urlWithCredentials = 'https://user:token@github.com/user/repo.git';
179+
const urlWithoutCredentials = 'https://github.com/user/repo.git';
180+
181+
// Act
182+
const normalize = (url: string) => {
183+
try {
184+
const urlObj = new URL(url);
185+
urlObj.username = '';
186+
urlObj.password = '';
187+
return urlObj.toString();
188+
} catch {
189+
return url;
190+
}
191+
};
192+
193+
const normalized1 = normalize(urlWithCredentials);
194+
const normalized2 = normalize(urlWithoutCredentials);
195+
196+
// Assert
197+
expect(normalized1).toBe(normalized2);
198+
});
199+
});
200+
201+
describe('Error Handling', () => {
202+
it('should handle connection errors gracefully', async () => {
203+
// Arrange
204+
const error = new Error('Connection failed');
205+
jest.spyOn(mockGitService, 'hasRemoteConfigured').mockRejectedValue(error);
206+
207+
// Act & Assert
208+
await expect(mockGitService.hasRemoteConfigured('test-url')).rejects.toThrow(
209+
'Connection failed',
210+
);
211+
});
212+
213+
it('should handle invalid preferences', () => {
214+
// Arrange
215+
const invalidPrefs = {
216+
connectionType: 'https' as const,
217+
repositoryUrl: 'invalid-url',
218+
};
219+
220+
// Act
221+
const isValidUrl = (url: string) => {
222+
try {
223+
new URL(url);
224+
return true;
225+
} catch {
226+
return false;
227+
}
228+
};
229+
230+
// Assert
231+
expect(isValidUrl(invalidPrefs.repositoryUrl)).toBe(false);
232+
});
233+
});
234+
});

0 commit comments

Comments
 (0)