-
Notifications
You must be signed in to change notification settings - Fork 348
Expand file tree
/
Copy pathclean.test.js
More file actions
175 lines (165 loc) · 5.65 KB
/
clean.test.js
File metadata and controls
175 lines (165 loc) · 5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'use strict';
const { Cleaner, exec_git } = require('../clean.js');
const fs = require('fs').promises;
const nock = require('nock');
const os = require('os');
const rmfr = require('rmfr');
const tmp = `${os.tmpdir()}/preview_cleaner`;
async function prepareCleaner(extra_branches = []) {
await fs.mkdir(tmp, {recursive: true});
// Create an empty repo
const repo = await fs.mkdtemp(`${tmp}/input`);
const opts = {
cwd: repo,
env: {
'GIT_AUTHOR_NAME': 'Test',
'GIT_AUTHOR_EMAIL': 'test@example.com',
'GIT_COMMITTER_NAME': 'Test',
'GIT_COMMITTER_EMAIL': 'test@example.com',
},
};
await exec_git(['init'], opts);
await exec_git(['commit', '--allow-empty', '-m', 'init'], opts);
for (const branch of extra_branches) {
await exec_git(["checkout", "-b", branch.name], opts);
let date = Date.now();
if (branch.age) {
date = date - 1000 * 60 * 60 * 24 * branch.age;
}
await exec_git(
[
"commit", "--allow-empty", "-m", "mark time",
"--date", new Date(date).toISOString(),
], opts);
}
await exec_git(["checkout", "master"], opts);
// Create the local cache which is required by the cleaner scripit
const cache_dir = await fs.mkdtemp(`${tmp}/cache`);
opts.cwd = cache_dir;
await exec_git(['clone', '--mirror', repo], opts);
// Setup the cleaner
const tmp_dir = await fs.mkdtemp(`${tmp}/tmp`);
return new Cleaner(null, repo, cache_dir, tmp_dir);
}
function mock_github(request_body) {
return nock('https://api.github.com').post('/graphql', request_body);
}
function github_result(is_closed) {
return JSON.stringify({
data: {
repository: {
pullRequest: {
closed: is_closed
}
}
}
});
}
afterAll(() => rmfr(tmp));
describe('Cleaner.clone', () => {
let out;
beforeAll(async () => {
const cleaner = await prepareCleaner();
await cleaner.clone();
out = cleaner.local_path;
});
test('performs a bare clone', async () => {
// We want a bare clone because it is a lot faster. We don't need to check
// out all of the files in the master branch to do our job.
await fs.access(out);
await expect(fs.access(`${out}/.git`)).rejects.toMatchObject({
code: 'ENOENT'
});
});
});
describe('Cleaner.is_pr_closed', () => {
let cleaner;
let token;
beforeEach(() => {
token = Math.random().toString();
cleaner = new Cleaner(token, 'repo', null, null);
});
test('returns true if github returns true', async () => {
mock_github().reply(200, github_result(true));
await expect(cleaner.is_pr_closed({repo: 'r', number: 1})).resolves.toBe(true);
});
test('returns false if github returns false', async () => {
mock_github().reply(200, github_result(false));
await expect(cleaner.is_pr_closed({repo: 'r', number: 1})).resolves.toBe(false);
});
test("returns false if github doesn't return repo", async () => {
mock_github().reply(200, JSON.stringify({
data: {}
}));
await expect(cleaner.is_pr_closed({branch: 'r_1', repo: 'r', number: 1})).resolves.toBe(false);
});
test('complains if github returns invalid json', async () => {
mock_github().reply(200, 'pure garbage');
await expect(cleaner.is_pr_closed({repo: 'r', number: 1})).rejects
.toThrow(/Unexpected token p/);
});
test('complains if github returns unexpect json', async () => {
mock_github().reply(200, JSON.stringify({
data: {
repository: 'asdf'
}
}));
await expect(cleaner.is_pr_closed({repo: 'r', number: 1})).rejects
.toThrow(/Cannot read properties of undefined \(reading 'closed'\)/);
});
test("backs off if there aren't many requests remaining", async () => {
// Mock setTimeout to immediately run. We don't use jest.useFakeTimers
// because that needs to be manually advanced which is complex for this
// test and not worth it.
global.setTimeout = jest.fn((callback) => callback());
const oldNow = Date.now;
Date.now = jest.fn(() => new Date(0));
try {
mock_github().reply(200, github_result(true), {
'x-ratelimit-remaining': 50,
'x-ratelimit-reset': 1,
});
const is_closed = cleaner.is_pr_closed({repo: 'r', number: 1});
await expect(is_closed).resolves.toBe(true);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
} finally {
Date.now = oldNow;
}
});
});
describe('Cleaner.run', () => {
let repo;
beforeAll(async () => {
const cleaner = await prepareCleaner([
{name: "staging"},
{name: "foo"},
{name: "r_1"},
{name: "r_2"},
{name: "r_3", age: 50}]);
mock_github(/{"repo":"r","number":1}/).reply(200, github_result(false));
mock_github(/{"repo":"r","number":2}/).reply(200, github_result(true));
mock_github(/{"repo":"r","number":3}/).reply(200, github_result(true));
await cleaner.run();
repo = cleaner.repo;
});
function show_branch(branch) {
return exec_git(
['show-ref', '--verify', `refs/heads/${branch}`],
{cwd: repo}
);
}
test("branches that don't look like PRs are left alone", async () => {
await expect(show_branch('master')).resolves.toMatch(/master/);
await expect(show_branch('staging')).resolves.toMatch(/staging/);
});
test('branches for open prs are left alone', async () => {
await expect(show_branch('r_1')).resolves.toMatch(/r_1/);
});
test('branches for closed prs are removed', async () => {
await expect(show_branch('r_2')).rejects.toMatch(/not a valid ref/);
});
test('old branches are removed', async () => {
await expect(show_branch('r_2')).rejects.toMatch(/not a valid ref/);
});
});