Skip to content

Commit 22860e6

Browse files
committed
test: add more test for credential
1 parent 70db0ab commit 22860e6

5 files changed

Lines changed: 96 additions & 12 deletions

File tree

src/commitAndSync.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export async function commitAndSync(options: ICommitAndSyncOptions): Promise<voi
185185
}
186186
} finally {
187187
// always restore original remoteUrl without token
188-
await credentialOff(dir, remoteUrl);
188+
await credentialOff(dir, remoteName, remoteUrl);
189189
}
190190
}
191191

src/credential.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,36 @@ import { GitProcess } from 'dugite';
22
import { trim } from 'lodash';
33
import { getRemoteUrl } from './inspect';
44

5+
export enum ServiceType {
6+
Github = 'github',
7+
}
8+
59
// TODO: support folderLocation as rawUrl like `/Users/linonetwo/Desktop/repo/git-sync-js/test/mockUpstreamRepo/credential` for test, or gitlab url.
6-
export const getGitUrlWithCredential = (rawUrl: string, username: string, accessToken: string): string =>
10+
export const getGitHubUrlWithCredential = (rawUrl: string, username: string, accessToken: string): string =>
711
trim(rawUrl.replaceAll('\n', '').replace('https://github.com/', `https://${username}:${accessToken}@github.com/`));
8-
const getGitUrlWithOutCredential = (urlWithCredential: string): string => trim(urlWithCredential.replace(/.+@/, 'https://'));
12+
const getGitHubUrlWithOutCredential = (urlWithCredential: string): string => trim(urlWithCredential.replace(/.+@/, 'https://'));
913

1014
/**
1115
* Add remote with credential
1216
* @param {string} directory
1317
* @param {string} remoteUrl
1418
* @param {{ login: string, email: string, accessToken: string }} userInfo
1519
*/
16-
export async function credentialOn(directory: string, remoteUrl: string, userName: string, accessToken: string, remoteName: string): Promise<void> {
17-
const gitUrlWithCredential = getGitUrlWithCredential(remoteUrl, userName, accessToken);
20+
export async function credentialOn(
21+
directory: string,
22+
remoteUrl: string,
23+
userName: string,
24+
accessToken: string,
25+
remoteName: string,
26+
serviceType = ServiceType.Github,
27+
): Promise<void> {
28+
let gitUrlWithCredential;
29+
switch (serviceType) {
30+
case ServiceType.Github: {
31+
gitUrlWithCredential = getGitHubUrlWithCredential(remoteUrl, userName, accessToken);
32+
break;
33+
}
34+
}
1835
await GitProcess.exec(['remote', 'add', remoteName, gitUrlWithCredential], directory);
1936
await GitProcess.exec(['remote', 'set-url', remoteName, gitUrlWithCredential], directory);
2037
}
@@ -24,8 +41,14 @@ export async function credentialOn(directory: string, remoteUrl: string, userNam
2441
* @param {string} githubRepoUrl
2542
* @param {{ login: string, email: string, accessToken: string }} userInfo
2643
*/
27-
export async function credentialOff(directory: string, remoteName: string, remoteUrl?: string): Promise<void> {
28-
const githubRepoUrl = remoteUrl ?? (await getRemoteUrl(directory, remoteName));
29-
const gitUrlWithOutCredential = getGitUrlWithOutCredential(githubRepoUrl);
44+
export async function credentialOff(directory: string, remoteName: string, remoteUrl?: string, serviceType = ServiceType.Github): Promise<void> {
45+
const gitRepoUrl = remoteUrl ?? (await getRemoteUrl(directory, remoteName));
46+
let gitUrlWithOutCredential;
47+
switch (serviceType) {
48+
case ServiceType.Github: {
49+
gitUrlWithOutCredential = getGitHubUrlWithOutCredential(gitRepoUrl);
50+
break;
51+
}
52+
}
3053
await GitProcess.exec(['remote', 'set-url', remoteName, gitUrlWithOutCredential], directory);
3154
}

test/commitAndSync.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/* eslint-disable security/detect-non-literal-fs-filename */
2+
import { omit } from 'lodash';
23
import { commitAndSync, ICommitAndSyncOptions } from '../src/commitAndSync';
34
import { defaultGitInfo } from '../src/defaultGitInfo';
4-
import { getSyncState, SyncState } from '../src/inspect';
5+
import { GitPullPushError } from '../src/errors';
6+
import { getRemoteUrl, getSyncState, SyncState } from '../src/inspect';
57
import {
8+
creatorGitInfo,
69
// eslint-disable-next-line unicorn/prevent-abbreviations
710
dir,
811
exampleToken,
@@ -24,4 +27,26 @@ describe('commitAndSync', () => {
2427
await commitAndSync(getCommitAndSyncOptions());
2528
expect(await getSyncState(dir, defaultGitInfo.branch, defaultGitInfo.remote)).toBe<SyncState>('equal');
2629
});
30+
test('restore Github credential after failed', async () => {
31+
// can't push to github during test, so we use a fake token and only test failed situation
32+
const creatorRepoUrl = `https://github.com/${creatorGitInfo.gitUserName}/wiki`;
33+
expect(await getSyncState(dir, defaultGitInfo.branch, defaultGitInfo.remote)).toBe<SyncState>('noUpstreamOrBareUpstream');
34+
await addSomeFiles();
35+
const options = {
36+
dir,
37+
remoteUrl: creatorRepoUrl,
38+
userInfo: { ...creatorGitInfo, branch: 'main' },
39+
};
40+
await expect(async () => await commitAndSync(options)).rejects.toThrow(
41+
new GitPullPushError(
42+
// print the same error message as Error...
43+
{ ...omit(options, ['remoteUrl', 'userInfo']), branch: 'main', remote: 'origin', userInfo: options.userInfo },
44+
`remote: Invalid username or password.
45+
fatal: Authentication failed for 'https://github.com/linonetwo/wiki/'
46+
`,
47+
),
48+
);
49+
const restoredRemoteUrl = await getRemoteUrl(dir, defaultGitInfo.remote);
50+
expect(restoredRemoteUrl).toBe(creatorRepoUrl);
51+
});
2752
});

test/constants.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/* eslint-disable unicorn/prevent-abbreviations */
33
/* eslint-disable unicorn/prefer-module */
44
import path from 'path';
5+
import { IGitUserInfos } from '../src/interface';
56

67
/**
78
* Random dir name to prevent parallel test execution collision
@@ -23,6 +24,14 @@ export let upstreamDirGitDirectory: string;
2324
export let gitSyncRepoDirectory: string;
2425
export let gitSyncRepoDirectoryGitDirectory: string;
2526

27+
export const creatorGitInfo: IGitUserInfos & { remote: string } = {
28+
email: 'gitsync@gmail.com',
29+
gitUserName: 'linonetwo',
30+
branch: 'master',
31+
remote: 'origin',
32+
accessToken: 'ghp_zA8Xet3mupV6kWj2sFsKUpTv45hJA6ZJyzY6',
33+
};
34+
2635
/**
2736
* use currentTestName to get better constants, should call in jest functions as early as possible
2837
*/

test/credential.test.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { credentialOff, credentialOn, getGitUrlWithCredential, getRemoteUrl } from '../src';
1+
import { credentialOff, credentialOn, getGitHubUrlWithCredential, getRemoteUrl } from '../src';
22
import { defaultGitInfo } from '../src/defaultGitInfo';
33
import { getGitUrlWithGitSuffix, getGitUrlWithOutGitSuffix } from '../src/utils';
4-
import { dir, exampleRemoteUrl, exampleToken } from './constants';
4+
import { creatorGitInfo, dir, exampleRemoteUrl, exampleToken } from './constants';
55
import { addHTTPRemote } from './utils';
66

77
describe('credential', () => {
@@ -15,7 +15,7 @@ describe('credential', () => {
1515
// make sure we are working on a https remote, our method only handle this case
1616
expect(remoteUrl).toStartWith('https://');
1717
expect(remoteUrl.length).toBeGreaterThan(0);
18-
expect(remoteUrl).toBe(getGitUrlWithCredential(exampleRemoteUrl, defaultGitInfo.gitUserName, exampleToken));
18+
expect(remoteUrl).toBe(getGitHubUrlWithCredential(exampleRemoteUrl, defaultGitInfo.gitUserName, exampleToken));
1919
// github use https://${username}:${accessToken}@github.com/ format
2020
expect(remoteUrl.includes('@')).toBe(true);
2121
expect(remoteUrl.includes(exampleToken)).toBe(true);
@@ -58,6 +58,33 @@ describe('credential', () => {
5858
expect(restoredRemoteUrl.includes(exampleToken)).toBe(false);
5959
});
6060

61+
test('it remove Github token after off (specific case of creator)', async () => {
62+
const creatorRepoUrl = `https://github.com/${creatorGitInfo.gitUserName}/wiki`;
63+
const creatorRepoUrlWithToken = `https://${creatorGitInfo.gitUserName}:${creatorGitInfo.accessToken}@github.com/${creatorGitInfo.gitUserName}/wiki`;
64+
await credentialOn(dir, creatorRepoUrl, creatorGitInfo.gitUserName, creatorGitInfo.accessToken, defaultGitInfo.remote);
65+
const newRemoteUrl = await getRemoteUrl(dir, defaultGitInfo.remote);
66+
expect(newRemoteUrl.includes(creatorGitInfo.accessToken)).toBe(true);
67+
expect(newRemoteUrl).toBe(creatorRepoUrlWithToken);
68+
await credentialOff(dir, defaultGitInfo.remote);
69+
const restoredRemoteUrl = await getRemoteUrl(dir, defaultGitInfo.remote);
70+
expect(restoredRemoteUrl).toBe(creatorRepoUrl);
71+
expect(restoredRemoteUrl.includes(exampleToken)).toBe(false);
72+
});
73+
74+
test('it remove Github token from url with token (specific case of creator)', async () => {
75+
const creatorRepoUrl = `https://github.com/${creatorGitInfo.gitUserName}/wiki`;
76+
const creatorRepoUrlWithToken = `https://${creatorGitInfo.gitUserName}:${creatorGitInfo.accessToken}@github.com/${creatorGitInfo.gitUserName}/wiki`;
77+
// sometimes, original url has token (forget to remove) due to bugs in previous versions.
78+
await credentialOn(dir, creatorRepoUrlWithToken, creatorGitInfo.gitUserName, creatorGitInfo.accessToken, defaultGitInfo.remote);
79+
const newRemoteUrl = await getRemoteUrl(dir, defaultGitInfo.remote);
80+
expect(newRemoteUrl.includes(creatorGitInfo.accessToken)).toBe(true);
81+
expect(newRemoteUrl).toBe(creatorRepoUrlWithToken);
82+
await credentialOff(dir, defaultGitInfo.remote);
83+
const restoredRemoteUrl = await getRemoteUrl(dir, defaultGitInfo.remote);
84+
expect(restoredRemoteUrl).toBe(creatorRepoUrl);
85+
expect(restoredRemoteUrl.includes(exampleToken)).toBe(false);
86+
});
87+
6188
test('methods are idempotent', async () => {
6289
const originalRemoteUrl = await getRemoteUrl(dir, defaultGitInfo.remote);
6390
await credentialOn(dir, originalRemoteUrl, defaultGitInfo.gitUserName, exampleToken, defaultGitInfo.remote);

0 commit comments

Comments
 (0)