Skip to content

Commit 6b505c4

Browse files
committed
improve test coverage
1 parent ee24f16 commit 6b505c4

7 files changed

Lines changed: 292 additions & 32 deletions

File tree

jest.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ module.exports = {
88
coverageReporters: ['lcovonly', 'text'],
99
collectCoverage: true,
1010
coverageDirectory: 'coverage',
11+
restoreMocks: true,
12+
1113
collectCoverageFrom: [
1214
'**/*.ts',
1315
'**/*.js',

modules.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ declare namespace NodeJS {
88
GITHUB_INSTALLATION_ID: string
99
GITHUB_APP_ID: string
1010
GITHUB_PRIVATE_KEY: string
11-
IGNORED_USERS: string
11+
IGNORED_USERS?: string
1212
}
1313
}

src/github.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { createAppAuth } from '@octokit/auth-app'
22
import { Octokit } from '@octokit/rest'
3-
const ignoredUsers = process.env.IGNORED_USERS.toLowerCase().split(',')
3+
import * as mod from './github'
44

5-
const octokit = getAuthenticatedOctokit()
5+
export function getIgnoredUsers(): string[] {
6+
return process.env.IGNORED_USERS?.toLowerCase().split(',') ?? []
7+
}
68

7-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
8-
export function getAuthenticatedOctokit() {
9+
export function getAuthenticatedOctokit(): Octokit {
910
return new Octokit({
1011
authStrategy: createAppAuth,
1112
auth: {
@@ -17,6 +18,7 @@ export function getAuthenticatedOctokit() {
1718
}
1819

1920
export async function getGithubUsersFromGithub(): Promise<Set<string>> {
21+
const octokit = mod.getAuthenticatedOctokit()
2022
const members = await octokit.paginate(octokit.orgs.listMembers, {
2123
org: process.env.GITHUB_ORG,
2224
})
@@ -36,10 +38,11 @@ export async function getGithubUsersFromGithub(): Promise<Set<string>> {
3638

3739
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
3840
export function formatUserList(users): Set<string> {
39-
return new Set(users.map((user) => user.login.toLowerCase()))
41+
return new Set(users.map((user) => user?.login?.toLowerCase()))
4042
}
4143

4244
export async function getUserIdFromUsername(username: string): Promise<number> {
45+
const octokit = mod.getAuthenticatedOctokit()
4346
console.log(`Looking up user ${username}`)
4447
let user
4548
try {
@@ -53,33 +56,36 @@ export async function getUserIdFromUsername(username: string): Promise<number> {
5356

5457
export async function addUsersToGitHubOrg(users: Set<string>): Promise<void> {
5558
for (const user of users) {
56-
await addUserToGitHubOrg(user)
59+
await mod.addUserToGitHubOrg(user)
5760
}
5861
}
5962

60-
export async function addUserToGitHubOrg(user: string): Promise<void | boolean> {
61-
if (ignoredUsers.includes(user.toLowerCase())) {
63+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
64+
export async function addUserToGitHubOrg(user: string) {
65+
const octokit = mod.getAuthenticatedOctokit()
66+
console.log(user, mod.getIgnoredUsers(), mod.getIgnoredUsers().includes(user.toLowerCase()))
67+
if (mod.getIgnoredUsers().includes(user.toLowerCase())) {
6268
console.log(`Ignoring add for ${user}`)
6369
return false
6470
}
65-
const userId = await getUserIdFromUsername(user)
71+
const userId = await mod.getUserIdFromUsername(user)
6672
console.log(`Inviting ${user} (${userId} to ${process.env.GITHUB_ORG})`)
67-
await octokit.orgs.createInvitation({
73+
return await octokit.orgs.createInvitation({
6874
org: process.env.GITHUB_ORG,
6975
invitee_id: userId,
7076
})
71-
console.log(`Invitation sent to ${user} (${userId} to ${process.env.GITHUB_ORG})`)
7277
}
7378

7479
export async function removeUsersToGitHubOrg(users: Set<string>): Promise<void> {
7580
for (const user of users) {
76-
await removeUserToGitHubOrg(user)
81+
await mod.removeUserToGitHubOrg(user)
7782
}
7883
}
7984

8085
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
8186
export async function removeUserToGitHubOrg(user: string) {
82-
if (ignoredUsers.includes(user.toLowerCase())) {
87+
const octokit = mod.getAuthenticatedOctokit()
88+
if (mod.getIgnoredUsers().includes(user.toLowerCase())) {
8389
console.log(`Ignoring remove for ${user}`)
8490
return false
8591
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`github integration addUserToGitHubOrg 1`] = `
4+
[MockFunction] {
5+
"calls": Array [
6+
Array [
7+
Object {
8+
"invitee_id": 123,
9+
"org": "myorg",
10+
},
11+
],
12+
],
13+
"results": Array [
14+
Object {
15+
"type": "return",
16+
"value": Promise {},
17+
},
18+
],
19+
}
20+
`;
21+
22+
exports[`github integration addUsersToGitHubOrg 1`] = `
23+
[MockFunction] {
24+
"calls": Array [
25+
Array [
26+
"foo",
27+
],
28+
Array [
29+
"bar",
30+
],
31+
],
32+
"results": Array [
33+
Object {
34+
"type": "return",
35+
"value": Promise {},
36+
},
37+
Object {
38+
"type": "return",
39+
"value": Promise {},
40+
},
41+
],
42+
}
43+
`;
44+
45+
exports[`github integration getAuthenticatedOctokit 1`] = `
46+
[MockFunction] {
47+
"calls": Array [
48+
Array [
49+
Object {
50+
"auth": Object {
51+
"appId": "123",
52+
"installationId": "123",
53+
"privateKey": "helloworld",
54+
},
55+
"authStrategy": [Function],
56+
},
57+
],
58+
],
59+
"results": Array [
60+
Object {
61+
"type": "return",
62+
"value": undefined,
63+
},
64+
],
65+
}
66+
`;
67+
68+
exports[`github integration getGithubUsersFromGithub 1`] = `
69+
Set {
70+
"chrisns",
71+
"bar",
72+
"foo",
73+
"pending",
74+
"anotherpending",
75+
}
76+
`;
77+
78+
exports[`github integration getIgnoredUsers empty 1`] = `Array []`;
79+
80+
exports[`github integration getIgnoredUsers many 1`] = `
81+
Array [
82+
"user1",
83+
"user2",
84+
"user3",
85+
"user4",
86+
]
87+
`;
88+
89+
exports[`github integration getIgnoredUsers single 1`] = `
90+
Array [
91+
"user1",
92+
]
93+
`;
94+
95+
exports[`github integration getUserIdFromUsername found 1`] = `123`;
96+
97+
exports[`github integration getUserIdFromUsername notfound 1`] = `"Unable to find user id for foo"`;
98+
99+
exports[`github integration removeUserToGitHubOrg 1`] = `
100+
[MockFunction] {
101+
"calls": Array [
102+
Array [
103+
Object {
104+
"org": "myorg",
105+
"username": "foo",
106+
},
107+
],
108+
],
109+
"results": Array [
110+
Object {
111+
"type": "return",
112+
"value": Promise {},
113+
},
114+
],
115+
}
116+
`;
117+
118+
exports[`github integration removeUsersToGitHubOrg 1`] = `
119+
[MockFunction] {
120+
"calls": Array [
121+
Array [
122+
"foo",
123+
],
124+
Array [
125+
"bar",
126+
],
127+
],
128+
"results": Array [
129+
Object {
130+
"type": "return",
131+
"value": Promise {},
132+
},
133+
Object {
134+
"type": "return",
135+
"value": Promise {},
136+
},
137+
],
138+
}
139+
`;

tests/__snapshots__/google.spec.ts.snap

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@ Set {
1010
`;
1111

1212
exports[`google integration googleAuth 1`] = `
13-
Array [
14-
Array [
15-
"foo",
16-
null,
17-
"bar",
13+
[MockFunction] {
14+
"calls": Array [
1815
Array [
19-
"https://www.googleapis.com/auth/admin.directory.user.readonly",
16+
"foo",
17+
null,
18+
"bar",
19+
Array [
20+
"https://www.googleapis.com/auth/admin.directory.user.readonly",
21+
],
22+
"hello@example.com",
2023
],
21-
"hello@example.com",
2224
],
23-
]
25+
"results": Array [
26+
Object {
27+
"type": "return",
28+
"value": undefined,
29+
},
30+
],
31+
}
2432
`;

tests/github.spec.ts

Lines changed: 113 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,118 @@
1+
jest.mock('@octokit/rest')
2+
import { Octokit } from '@octokit/rest'
3+
14
import * as mod from '../src/github'
25
describe('github integration', () => {
3-
it.todo('getAuthenticatedOctokit')
4-
it.todo('getGithubUsersFromGithub')
5-
it.todo('getUserIdFromUsername')
6-
it.todo('addUsersToGitHubOrg')
7-
it.todo('addUserToGitHubOrg')
8-
it.todo('removeUsersToGitHubOrg')
9-
it.todo('removeUserToGitHubOrg')
6+
beforeEach(() => {
7+
process.env.GITHUB_PRIVATE_KEY = Buffer.from('helloworld').toString('base64')
8+
process.env.GITHUB_APP_ID = '123'
9+
process.env.GITHUB_INSTALLATION_ID = '123'
10+
jest.restoreAllMocks()
11+
})
12+
13+
it('getAuthenticatedOctokit', () => {
14+
mod.getAuthenticatedOctokit()
15+
return expect(Octokit).toMatchSnapshot()
16+
})
17+
it('getGithubUsersFromGithub', () => {
18+
const fakeOctokit = {
19+
paginate: jest
20+
.fn()
21+
.mockResolvedValueOnce([{ login: 'chrisns' }, { login: 'bar' }, { login: 'foo' }])
22+
.mockResolvedValueOnce([{ login: 'pending' }, { login: 'chrisns' }, { login: 'anotherpending' }]),
23+
orgs: { listMembers: jest.fn(), listPendingInvitations: jest.fn() },
24+
}
25+
// @ts-expect-error mock service isn't a complete implementation, so being lazy and just doing the bare minimum
26+
jest.spyOn(mod, 'getAuthenticatedOctokit').mockReturnValue(fakeOctokit)
27+
expect(mod.getGithubUsersFromGithub()).resolves.toMatchSnapshot()
28+
})
29+
it('getUserIdFromUsername found', () => {
30+
const fakeOctokit = {
31+
users: {
32+
getByUsername: jest.fn().mockResolvedValue({ data: { id: 123 } }),
33+
},
34+
}
35+
// @ts-expect-error mock service isn't a complete implementation, so being lazy and just doing the bare minimum
36+
jest.spyOn(mod, 'getAuthenticatedOctokit').mockReturnValue(fakeOctokit)
37+
return expect(mod.getUserIdFromUsername('foo')).resolves.toMatchSnapshot()
38+
})
39+
40+
it('getUserIdFromUsername notfound', () => {
41+
const fakeOctokit = {
42+
users: {
43+
getByUsername: jest.fn().mockRejectedValue(new Error('not found')),
44+
},
45+
}
46+
// @ts-expect-error mock service isn't a complete implementation, so being lazy and just doing the bare minimum
47+
jest.spyOn(mod, 'getAuthenticatedOctokit').mockReturnValue(fakeOctokit)
48+
return expect(mod.getUserIdFromUsername('foo')).rejects.toMatchSnapshot()
49+
})
50+
51+
it('addUsersToGitHubOrg', async () => {
52+
const users = new Set(['foo', 'bar'])
53+
jest.spyOn(mod, 'addUserToGitHubOrg').mockResolvedValue(false)
54+
await mod.addUsersToGitHubOrg(users)
55+
return expect(mod.addUserToGitHubOrg).toMatchSnapshot()
56+
})
57+
58+
it('removeUsersToGitHubOrg', async () => {
59+
const users = new Set(['foo', 'bar'])
60+
jest.spyOn(mod, 'removeUserToGitHubOrg').mockResolvedValue(false)
61+
await mod.removeUsersToGitHubOrg(users)
62+
return expect(mod.removeUserToGitHubOrg).toMatchSnapshot()
63+
})
64+
65+
it('removeUserToGitHubOrg skip ignore', () => {
66+
jest.spyOn(mod, 'getIgnoredUsers').mockReturnValue(['foo'])
67+
expect(mod.removeUserToGitHubOrg('foo')).resolves.toBe(false)
68+
})
69+
70+
it('addUserToGitHubOrg skip ignore', () => {
71+
jest.spyOn(mod, 'getIgnoredUsers').mockReturnValue(['foo'])
72+
expect(mod.addUserToGitHubOrg('foo')).resolves.toBe(false)
73+
})
74+
75+
it('addUserToGitHubOrg', async () => {
76+
const fakeOctokit = {
77+
orgs: {
78+
createInvitation: jest.fn().mockResolvedValue(true),
79+
},
80+
}
81+
process.env.GITHUB_ORG = 'myorg'
82+
jest.spyOn(mod, 'getUserIdFromUsername').mockResolvedValue(123)
83+
// @ts-expect-error mock service isn't a complete implementation, so being lazy and just doing the bare minimum
84+
jest.spyOn(mod, 'getAuthenticatedOctokit').mockReturnValue(fakeOctokit)
85+
await mod.addUserToGitHubOrg('foo')
86+
return expect(fakeOctokit.orgs.createInvitation).toMatchSnapshot()
87+
})
88+
89+
it('removeUserToGitHubOrg', async () => {
90+
const fakeOctokit = {
91+
orgs: {
92+
removeMembershipForUser: jest.fn().mockResolvedValue(true),
93+
},
94+
}
95+
process.env.GITHUB_ORG = 'myorg'
96+
jest.spyOn(mod, 'getUserIdFromUsername').mockResolvedValue(123)
97+
// @ts-expect-error mock service isn't a complete implementation, so being lazy and just doing the bare minimum
98+
jest.spyOn(mod, 'getAuthenticatedOctokit').mockReturnValue(fakeOctokit)
99+
await mod.removeUserToGitHubOrg('foo')
100+
return expect(fakeOctokit.orgs.removeMembershipForUser).toMatchSnapshot()
101+
})
102+
103+
it('getIgnoredUsers empty', () => {
104+
delete process.env.IGNORED_USERS
105+
expect(mod.getIgnoredUsers()).toMatchSnapshot()
106+
})
107+
108+
it('getIgnoredUsers single', () => {
109+
process.env.IGNORED_USERS = 'user1'
110+
expect(mod.getIgnoredUsers()).toMatchSnapshot()
111+
})
112+
it('getIgnoredUsers many', () => {
113+
process.env.IGNORED_USERS = 'user1,user2,user3,USER4'
114+
expect(mod.getIgnoredUsers()).toMatchSnapshot()
115+
})
10116

11117
it('formatUserList', () => {
12118
const response = [{ login: 'chrisns' }, { login: 'chrisns' }, { login: 'foo' }]

0 commit comments

Comments
 (0)