1
1
import { Octokit } from '@octokit/rest' ;
2
2
import { mocked } from 'jest-mock' ;
3
+ import moment from 'moment-timezone' ;
3
4
import nock from 'nock' ;
4
5
5
6
import { listEC2Runners } from '../aws/runners' ;
6
7
import * as ghAuth from '../gh-auth/gh-auth' ;
7
- import * as scale from '../scale-runners/scale-up' ;
8
+ import { createRunners } from '../scale-runners/scale-up' ;
8
9
import { adjust } from './pool' ;
9
10
10
11
const mockOctokit = {
@@ -24,6 +25,7 @@ jest.mock('@octokit/rest', () => ({
24
25
25
26
jest . mock ( './../aws/runners' ) ;
26
27
jest . mock ( './../gh-auth/gh-auth' ) ;
28
+ jest . mock ( './../scale-runners/scale-up' ) ;
27
29
28
30
const mocktokit = Octokit as jest . MockedClass < typeof Octokit > ;
29
31
const mockedAppAuth = mocked ( ghAuth . createGithubAppAuth , {
@@ -36,6 +38,36 @@ const mockListRunners = mocked(listEC2Runners);
36
38
const cleanEnv = process . env ;
37
39
38
40
const ORG = 'my-org' ;
41
+ const MINIMUM_TIME_RUNNING = 15 ;
42
+
43
+ const ec2InstancesRegistered = [
44
+ {
45
+ instanceId : 'i-1-idle' ,
46
+ launchTime : new Date ( ) ,
47
+ type : 'Org' ,
48
+ owner : ORG ,
49
+ } ,
50
+ {
51
+ instanceId : 'i-2-busy' ,
52
+ launchTime : new Date ( ) ,
53
+ type : 'Org' ,
54
+ owner : ORG ,
55
+ } ,
56
+ {
57
+ instanceId : 'i-3-offline' ,
58
+ launchTime : new Date ( ) ,
59
+ type : 'Org' ,
60
+ owner : ORG ,
61
+ } ,
62
+ {
63
+ instanceId : 'i-4-idle-older-than-minimum-time-running' ,
64
+ launchTime : moment ( new Date ( ) )
65
+ . subtract ( MINIMUM_TIME_RUNNING + 3 , 'minutes' )
66
+ . toDate ( ) ,
67
+ type : 'Org' ,
68
+ owner : ORG ,
69
+ } ,
70
+ ] ;
39
71
40
72
beforeEach ( ( ) => {
41
73
nock . disableNetConnect ( ) ;
@@ -55,6 +87,7 @@ beforeEach(() => {
55
87
process . env . INSTANCE_TYPES = 'm5.large' ;
56
88
process . env . INSTANCE_TARGET_CAPACITY_TYPE = 'spot' ;
57
89
process . env . RUNNER_OWNER = ORG ;
90
+ process . env . RUNNER_BOOT_TIME_IN_MINUTES = MINIMUM_TIME_RUNNING . toString ( ) ;
58
91
59
92
const mockTokenReturnValue = {
60
93
data : {
@@ -66,66 +99,39 @@ beforeEach(() => {
66
99
mockOctokit . paginate . mockImplementation ( ( ) => [
67
100
{
68
101
id : 1 ,
69
- name : 'i-1' ,
102
+ name : 'i-1-idle ' ,
70
103
os : 'linux' ,
71
104
status : 'online' ,
72
105
busy : false ,
73
106
labels : [ ] ,
74
107
} ,
75
108
{
76
109
id : 2 ,
77
- name : 'i-2' ,
110
+ name : 'i-2-busy ' ,
78
111
os : 'linux' ,
79
112
status : 'online' ,
80
113
busy : true ,
81
114
labels : [ ] ,
82
115
} ,
83
116
{
84
117
id : 3 ,
85
- name : 'i-3' ,
118
+ name : 'i-3-offline ' ,
86
119
os : 'linux' ,
87
120
status : 'offline' ,
88
121
busy : false ,
89
122
labels : [ ] ,
90
123
} ,
91
124
{
92
- id : 11 ,
93
- name : 'j-1' , // some runner of another env
125
+ id : 3 ,
126
+ name : 'i-4-idle-older-than-minimum-time-running' ,
94
127
os : 'linux' ,
95
128
status : 'online' ,
96
129
busy : false ,
97
130
labels : [ ] ,
98
131
} ,
99
- {
100
- id : 12 ,
101
- name : 'j-2' , // some runner of another env
102
- os : 'linux' ,
103
- status : 'online' ,
104
- busy : true ,
105
- labels : [ ] ,
106
- } ,
107
132
] ) ;
108
133
109
- mockListRunners . mockImplementation ( async ( ) => [
110
- {
111
- instanceId : 'i-1' ,
112
- launchTime : new Date ( ) ,
113
- type : 'Org' ,
114
- owner : ORG ,
115
- } ,
116
- {
117
- instanceId : 'i-2' ,
118
- launchTime : new Date ( ) ,
119
- type : 'Org' ,
120
- owner : ORG ,
121
- } ,
122
- {
123
- instanceId : 'i-3' ,
124
- launchTime : new Date ( ) ,
125
- type : 'Org' ,
126
- owner : ORG ,
127
- } ,
128
- ] ) ;
134
+ mockListRunners . mockImplementation ( async ( ) => ec2InstancesRegistered ) ;
129
135
130
136
const mockInstallationIdReturnValueOrgs = {
131
137
data : {
@@ -156,16 +162,74 @@ beforeEach(() => {
156
162
157
163
describe ( 'Test simple pool.' , ( ) => {
158
164
describe ( 'With GitHub Cloud' , ( ) => {
159
- it ( 'Top up pool with pool size 2.' , async ( ) => {
160
- const spy = jest . spyOn ( scale , 'createRunners' ) ;
161
- await expect ( adjust ( { poolSize : 2 } ) ) . resolves ;
162
- expect ( spy ) . toBeCalled ;
165
+ it ( 'Top up pool with pool size 2 registered.' , async ( ) => {
166
+ await expect ( await adjust ( { poolSize : 3 } ) ) . resolves ;
167
+ expect ( createRunners ) . toHaveBeenCalledTimes ( 1 ) ;
168
+ expect ( createRunners ) . toHaveBeenCalledWith (
169
+ expect . anything ( ) ,
170
+ expect . objectContaining ( { numberOfRunners : 1 } ) ,
171
+ expect . anything ( ) ,
172
+ ) ;
163
173
} ) ;
164
174
165
175
it ( 'Should not top up if pool size is reached.' , async ( ) => {
166
- const spy = jest . spyOn ( scale , 'createRunners' ) ;
167
- await expect ( adjust ( { poolSize : 1 } ) ) . resolves ;
168
- expect ( spy ) . not . toHaveBeenCalled ;
176
+ await expect ( await adjust ( { poolSize : 1 } ) ) . resolves ;
177
+ expect ( createRunners ) . not . toHaveBeenCalled ( ) ;
178
+ } ) ;
179
+
180
+ it ( 'Should top up if pool size is not reached including a booting instance.' , async ( ) => {
181
+ mockListRunners . mockImplementation ( async ( ) => [
182
+ ...ec2InstancesRegistered ,
183
+ {
184
+ instanceId : 'i-4-still-booting' ,
185
+ launchTime : moment ( new Date ( ) )
186
+ . subtract ( MINIMUM_TIME_RUNNING - 3 , 'minutes' )
187
+ . toDate ( ) ,
188
+ type : 'Org' ,
189
+ owner : ORG ,
190
+ } ,
191
+ {
192
+ instanceId : 'i-5-orphan' ,
193
+ launchTime : moment ( new Date ( ) )
194
+ . subtract ( MINIMUM_TIME_RUNNING + 3 , 'minutes' )
195
+ . toDate ( ) ,
196
+ type : 'Org' ,
197
+ owner : ORG ,
198
+ } ,
199
+ ] ) ;
200
+
201
+ // 2 idle + 1 booting = 3, top up with 2 to match a pool of 5
202
+ await expect ( await adjust ( { poolSize : 5 } ) ) . resolves ;
203
+ expect ( createRunners ) . toHaveBeenCalledWith (
204
+ expect . anything ( ) ,
205
+ expect . objectContaining ( { numberOfRunners : 2 } ) ,
206
+ expect . anything ( ) ,
207
+ ) ;
208
+ } ) ;
209
+
210
+ it ( 'Should not top up if pool size is reached including a booting instance.' , async ( ) => {
211
+ mockListRunners . mockImplementation ( async ( ) => [
212
+ ...ec2InstancesRegistered ,
213
+ {
214
+ instanceId : 'i-4-still-booting' ,
215
+ launchTime : moment ( new Date ( ) )
216
+ . subtract ( MINIMUM_TIME_RUNNING - 3 , 'minutes' )
217
+ . toDate ( ) ,
218
+ type : 'Org' ,
219
+ owner : ORG ,
220
+ } ,
221
+ {
222
+ instanceId : 'i-5-orphan' ,
223
+ launchTime : moment ( new Date ( ) )
224
+ . subtract ( MINIMUM_TIME_RUNNING + 3 , 'minutes' )
225
+ . toDate ( ) ,
226
+ type : 'Org' ,
227
+ owner : ORG ,
228
+ } ,
229
+ ] ) ;
230
+
231
+ await expect ( await adjust ( { poolSize : 2 } ) ) . resolves ;
232
+ expect ( createRunners ) . not . toHaveBeenCalled ( ) ;
169
233
} ) ;
170
234
} ) ;
171
235
@@ -175,9 +239,13 @@ describe('Test simple pool.', () => {
175
239
} ) ;
176
240
177
241
it ( 'Top up if the pool size is set to 5' , async ( ) => {
178
- const spy = jest . spyOn ( scale , 'createRunners' ) ;
179
- await expect ( adjust ( { poolSize : 5 } ) ) . resolves ;
180
- expect ( spy ) . toBeCalled ;
242
+ await expect ( await adjust ( { poolSize : 5 } ) ) . resolves ;
243
+ // 2 idle, top up with 3 to match a pool of 5
244
+ expect ( createRunners ) . toHaveBeenCalledWith (
245
+ expect . anything ( ) ,
246
+ expect . objectContaining ( { numberOfRunners : 3 } ) ,
247
+ expect . anything ( ) ,
248
+ ) ;
181
249
} ) ;
182
250
} ) ;
183
251
} ) ;
0 commit comments