Skip to content

Commit 611733f

Browse files
authored
feat: check constructs against account default runtime if not given (#1007)
The CLI had a hardcoded default runtime value of 2024.02 which became the effective value if no runtime was set at check or project level. Now, the account's default runtime is used instead. Additionally, if the account's default runtime is used, then we leave `runtimeId` undefined when synthesizing resources. This allows the user to have their checks always use the current account default runtime if they wish. If they do not wish to have such behavior, they should set a default runtime at project level as usual.
1 parent 1fe533f commit 611733f

13 files changed

+263
-18
lines changed

packages/cli/e2e/__tests__/switch.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('switch', () => {
1111
promptsInjection: [{
1212
id: config.get('accountId') as string,
1313
name: accountName,
14+
runtimeId: '2024.02', // Not important for this command.
1415
}],
1516
timeout: 5000,
1617
})

packages/cli/src/commands/deploy.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Flags, ux } from '@oclif/core'
66
import { AuthCommand } from './authCommand'
77
import { parseProject } from '../services/project-parser'
88
import { loadChecklyConfig } from '../services/checkly-config-loader'
9-
import { runtimes } from '../rest/api'
109
import type { Runtime } from '../rest/runtimes'
1110
import {
1211
Check, AlertChannelSubscription, AlertChannel, CheckGroup, Dashboard,
@@ -92,7 +91,8 @@ export default class Deploy extends AuthCommand {
9291
config: checklyConfig,
9392
constructs: checklyConfigConstructs,
9493
} = await loadChecklyConfig(configDirectory, configFilenames)
95-
const { data: avilableRuntimes } = await runtimes.getAll()
94+
const { data: account } = await api.accounts.get(config.getAccountId())
95+
const { data: avilableRuntimes } = await api.runtimes.getAll()
9696
const project = await parseProject({
9797
directory: configDirectory,
9898
projectLogicalId: checklyConfig.logicalId,
@@ -108,6 +108,7 @@ export default class Deploy extends AuthCommand {
108108
acc[runtime.name] = runtime
109109
return acc
110110
}, <Record<string, Runtime>> {}),
111+
defaultRuntimeId: account.runtimeId,
111112
verifyRuntimeDependencies,
112113
checklyConfigConstructs,
113114
})
@@ -140,8 +141,6 @@ export default class Deploy extends AuthCommand {
140141
return
141142
}
142143

143-
const { data: account } = await api.accounts.get(config.getAccountId())
144-
145144
if (!force && !preview) {
146145
const { confirm } = await prompts({
147146
name: 'confirm',

packages/cli/src/commands/test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export default class Test extends AuthCommand {
159159
})
160160
const verbose = this.prepareVerboseFlag(verboseFlag, checklyConfig.cli?.verbose)
161161
const reporterTypes = this.prepareReportersTypes(reporterFlag as ReporterType, checklyConfig.cli?.reporters)
162+
const { data: account } = await api.accounts.get(config.getAccountId())
162163
const { data: availableRuntimes } = await api.runtimes.getAll()
163164

164165
const project = await parseProject({
@@ -176,6 +177,7 @@ export default class Test extends AuthCommand {
176177
acc[runtime.name] = runtime
177178
return acc
178179
}, <Record<string, Runtime>> {}),
180+
defaultRuntimeId: account.runtimeId,
179181
verifyRuntimeDependencies,
180182
checklyConfigConstructs,
181183
})

packages/cli/src/constructs/__tests__/api-check.spec.ts

+78
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,84 @@ describe('ApiCheck', () => {
3636
})
3737
})
3838

39+
it('should fail to bundle if runtime is not specified and default runtime is not set', () => {
40+
const getFilePath = (filename: string) => path.join(__dirname, 'fixtures', 'api-check', filename)
41+
const bundle = () => {
42+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
43+
const _bundle = ApiCheck.bundle(getFilePath('entrypoint.js'), undefined)
44+
}
45+
46+
Session.basePath = __dirname
47+
Session.availableRuntimes = runtimes
48+
Session.defaultRuntimeId = undefined
49+
expect(bundle).toThrowError('runtime is not set')
50+
delete Session.basePath
51+
delete Session.defaultRuntimeId
52+
})
53+
54+
it('should successfully bundle if runtime is not specified but default runtime is set', () => {
55+
const getFilePath = (filename: string) => path.join(__dirname, 'fixtures', 'api-check', filename)
56+
const bundle = () => {
57+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
58+
const _bundle = ApiCheck.bundle(getFilePath('entrypoint.js'), undefined)
59+
}
60+
61+
Session.basePath = __dirname
62+
Session.availableRuntimes = runtimes
63+
Session.defaultRuntimeId = '2022.10'
64+
expect(bundle).not.toThrowError('is not supported')
65+
delete Session.basePath
66+
delete Session.defaultRuntimeId
67+
})
68+
69+
it('should fail to bundle if runtime is not supported even if default runtime is set', () => {
70+
const getFilePath = (filename: string) => path.join(__dirname, 'fixtures', 'api-check', filename)
71+
const bundle = () => {
72+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
73+
const _bundle = ApiCheck.bundle(getFilePath('entrypoint.js'), '9999.99')
74+
}
75+
76+
Session.basePath = __dirname
77+
Session.availableRuntimes = runtimes
78+
Session.defaultRuntimeId = '2022.02'
79+
expect(bundle).toThrowError('9999.99 is not supported')
80+
delete Session.basePath
81+
delete Session.defaultRuntimeId
82+
})
83+
84+
it('should not synthesize runtime if not specified even if default runtime is set', () => {
85+
Session.project = new Project('project-id', {
86+
name: 'Test Project',
87+
repoUrl: 'https://github.com/checkly/checkly-cli',
88+
})
89+
Session.availableRuntimes = runtimes
90+
Session.defaultRuntimeId = '2022.02'
91+
const apiCheck = new ApiCheck('test-check', {
92+
name: 'Test Check',
93+
request,
94+
})
95+
const payload = apiCheck.synthesize()
96+
expect(payload.runtimeId).toBeUndefined()
97+
delete Session.defaultRuntimeId
98+
})
99+
100+
it('should synthesize runtime if specified', () => {
101+
Session.project = new Project('project-id', {
102+
name: 'Test Project',
103+
repoUrl: 'https://github.com/checkly/checkly-cli',
104+
})
105+
Session.availableRuntimes = runtimes
106+
Session.defaultRuntimeId = '2022.02'
107+
const apiCheck = new ApiCheck('test-check', {
108+
name: 'Test Check',
109+
runtimeId: '2022.02',
110+
request,
111+
})
112+
const payload = apiCheck.synthesize()
113+
expect(payload.runtimeId).toEqual('2022.02')
114+
delete Session.defaultRuntimeId
115+
})
116+
39117
it('should apply default check settings', () => {
40118
Session.project = new Project('project-id', {
41119
name: 'Test Project',

packages/cli/src/constructs/__tests__/browser-check.spec.ts

+78
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,84 @@ describe('BrowserCheck', () => {
3131
})
3232
})
3333

34+
it('should fail to bundle if runtime is not specified and default runtime is not set', () => {
35+
const getFilePath = (filename: string) => path.join(__dirname, 'fixtures', 'api-check', filename)
36+
const bundle = () => {
37+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
38+
const _bundle = BrowserCheck.bundle(getFilePath('entrypoint.js'), undefined)
39+
}
40+
41+
Session.basePath = __dirname
42+
Session.availableRuntimes = runtimes
43+
Session.defaultRuntimeId = undefined
44+
expect(bundle).toThrowError('runtime is not set')
45+
delete Session.basePath
46+
delete Session.defaultRuntimeId
47+
})
48+
49+
it('should successfully bundle if runtime is not specified but default runtime is set', () => {
50+
const getFilePath = (filename: string) => path.join(__dirname, 'fixtures', 'api-check', filename)
51+
const bundle = () => {
52+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
53+
const _bundle = BrowserCheck.bundle(getFilePath('entrypoint.js'), undefined)
54+
}
55+
56+
Session.basePath = __dirname
57+
Session.availableRuntimes = runtimes
58+
Session.defaultRuntimeId = '2022.10'
59+
expect(bundle).not.toThrowError('is not supported')
60+
delete Session.basePath
61+
delete Session.defaultRuntimeId
62+
})
63+
64+
it('should fail to bundle if runtime is not supported even if default runtime is set', () => {
65+
const getFilePath = (filename: string) => path.join(__dirname, 'fixtures', 'api-check', filename)
66+
const bundle = () => {
67+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
68+
const _bundle = BrowserCheck.bundle(getFilePath('entrypoint.js'), '9999.99')
69+
}
70+
71+
Session.basePath = __dirname
72+
Session.availableRuntimes = runtimes
73+
Session.defaultRuntimeId = '2022.02'
74+
expect(bundle).toThrowError('9999.99 is not supported')
75+
delete Session.basePath
76+
delete Session.defaultRuntimeId
77+
})
78+
79+
it('should not synthesize runtime if not specified even if default runtime is set', () => {
80+
Session.project = new Project('project-id', {
81+
name: 'Test Project',
82+
repoUrl: 'https://github.com/checkly/checkly-cli',
83+
})
84+
Session.availableRuntimes = runtimes
85+
Session.defaultRuntimeId = '2022.02'
86+
const browserCheck = new BrowserCheck('test-check', {
87+
name: 'Test Check',
88+
code: { content: 'console.log("test check")' },
89+
})
90+
const payload = browserCheck.synthesize()
91+
expect(payload.runtimeId).toBeUndefined()
92+
delete Session.defaultRuntimeId
93+
})
94+
95+
it('should synthesize runtime if specified', () => {
96+
Session.project = new Project('project-id', {
97+
name: 'Test Project',
98+
repoUrl: 'https://github.com/checkly/checkly-cli',
99+
})
100+
Session.availableRuntimes = runtimes
101+
Session.defaultRuntimeId = '2022.02'
102+
const browserCheck = new BrowserCheck('test-check', {
103+
name: 'Test Check',
104+
runtimeId: '2022.02',
105+
code: { content: 'console.log("test check")' },
106+
})
107+
const payload = browserCheck.synthesize()
108+
expect(payload.runtimeId).toEqual('2022.02')
109+
delete Session.defaultRuntimeId
110+
})
111+
34112
it('should apply default check settings', () => {
35113
Session.project = new Project('project-id', {
36114
name: 'Test Project',

packages/cli/src/constructs/__tests__/multi-step-check.spec.ts

+69
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('MultistepCheck', () => {
1919
})
2020
expect(check.synthesize()).toMatchObject({ checkType: 'MULTI_STEP' })
2121
})
22+
2223
it('should throw if runtime does not support multi step check type', () => {
2324
Session.project = new Project('project-id', {
2425
name: 'Test Project',
@@ -33,7 +34,75 @@ describe('MultistepCheck', () => {
3334
}
3435
expect(() => new MultiStepCheck('main-check', {
3536
name: 'Main Check',
37+
runtimeId: '2023.09',
3638
code: { content: '' },
3739
})).toThrowError('This runtime does not support multi step checks.')
3840
})
41+
42+
it('should throw if runtime is not set and default runtime does not support multi step check type', () => {
43+
Session.project = new Project('project-id', {
44+
name: 'Test Project',
45+
repoUrl: 'https://github.com/checkly/checkly-cli',
46+
})
47+
Session.availableRuntimes = {
48+
...runtimesWithMultiStepSupport,
49+
9999.99: {
50+
...runtimesWithMultiStepSupport['2023.09'],
51+
multiStepSupport: false,
52+
},
53+
}
54+
Session.defaultRuntimeId = '9999.99'
55+
expect(() => new MultiStepCheck('main-check', {
56+
name: 'Main Check',
57+
code: { content: '' },
58+
})).toThrowError('This runtime does not support multi step checks.')
59+
delete Session.defaultRuntimeId
60+
})
61+
62+
it('should succeed if runtime is not set but default runtime supports multi step check type', () => {
63+
Session.project = new Project('project-id', {
64+
name: 'Test Project',
65+
repoUrl: 'https://github.com/checkly/checkly-cli',
66+
})
67+
Session.availableRuntimes = runtimesWithMultiStepSupport
68+
Session.defaultRuntimeId = '2023.09'
69+
expect(() => new MultiStepCheck('main-check', {
70+
name: 'Main Check',
71+
code: { content: '' },
72+
})).not.toThrowError()
73+
delete Session.defaultRuntimeId
74+
})
75+
76+
it('should not synthesize runtime if not specified even if default runtime is set', () => {
77+
Session.project = new Project('project-id', {
78+
name: 'Test Project',
79+
repoUrl: 'https://github.com/checkly/checkly-cli',
80+
})
81+
Session.availableRuntimes = runtimesWithMultiStepSupport
82+
Session.defaultRuntimeId = '2023.09'
83+
const multiCheck = new MultiStepCheck('main-check', {
84+
name: 'Main Check',
85+
code: { content: '' },
86+
})
87+
const payload = multiCheck.synthesize()
88+
expect(payload.runtimeId).toBeUndefined()
89+
delete Session.defaultRuntimeId
90+
})
91+
92+
it('should synthesize runtime if specified', () => {
93+
Session.project = new Project('project-id', {
94+
name: 'Test Project',
95+
repoUrl: 'https://github.com/checkly/checkly-cli',
96+
})
97+
Session.availableRuntimes = runtimesWithMultiStepSupport
98+
Session.defaultRuntimeId = '2023.09'
99+
const multiCheck = new MultiStepCheck('main-check', {
100+
name: 'Main Check',
101+
runtimeId: '2023.09',
102+
code: { content: '' },
103+
})
104+
const payload = multiCheck.synthesize()
105+
expect(payload.runtimeId).toEqual('2023.09')
106+
delete Session.defaultRuntimeId
107+
})
39108
})

packages/cli/src/constructs/api-check.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ export class ApiCheck extends Check {
295295

296296
if (props.setupScript) {
297297
if ('entrypoint' in props.setupScript) {
298-
const { script, scriptPath, dependencies } = ApiCheck.bundle(props.setupScript.entrypoint, this.runtimeId!)
298+
const { script, scriptPath, dependencies } = ApiCheck.bundle(props.setupScript.entrypoint, this.runtimeId)
299299
this.localSetupScript = script
300300
this.setupScriptPath = scriptPath
301301
this.setupScriptDependencies = dependencies
@@ -314,7 +314,7 @@ export class ApiCheck extends Check {
314314

315315
if (props.tearDownScript) {
316316
if ('entrypoint' in props.tearDownScript) {
317-
const { script, scriptPath, dependencies } = ApiCheck.bundle(props.tearDownScript.entrypoint, this.runtimeId!)
317+
const { script, scriptPath, dependencies } = ApiCheck.bundle(props.tearDownScript.entrypoint, this.runtimeId)
318318
this.localTearDownScript = script
319319
this.tearDownScriptPath = scriptPath
320320
this.tearDownScriptDependencies = dependencies
@@ -337,7 +337,7 @@ export class ApiCheck extends Check {
337337
this.addPrivateLocationCheckAssignments()
338338
}
339339

340-
static bundle (entrypoint: string, runtimeId: string) {
340+
static bundle (entrypoint: string, runtimeId?: string) {
341341
let absoluteEntrypoint = null
342342
if (path.isAbsolute(entrypoint)) {
343343
absoluteEntrypoint = entrypoint
@@ -348,7 +348,7 @@ export class ApiCheck extends Check {
348348
absoluteEntrypoint = path.join(path.dirname(Session.checkFileAbsolutePath), entrypoint)
349349
}
350350

351-
const runtime = Session.availableRuntimes[runtimeId]
351+
const runtime = Session.getRuntime(runtimeId)
352352
if (!runtime) {
353353
throw new Error(`${runtimeId} is not supported`)
354354
}

packages/cli/src/constructs/browser-check.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ export class BrowserCheck extends Check {
7979
}
8080
absoluteEntrypoint = path.join(path.dirname(Session.checkFileAbsolutePath), entrypoint)
8181
}
82-
// runtimeId will always be set by check or browser check defaults so it is safe to use ! operator
83-
const bundle = BrowserCheck.bundle(absoluteEntrypoint, this.runtimeId!)
82+
const bundle = BrowserCheck.bundle(absoluteEntrypoint, this.runtimeId)
8483
if (!bundle.script) {
8584
throw new Error(`Browser check "${logicalId}" is not allowed to be empty`)
8685
}
@@ -115,8 +114,8 @@ export class BrowserCheck extends Check {
115114
}
116115
}
117116

118-
static bundle (entry: string, runtimeId: string) {
119-
const runtime = Session.availableRuntimes[runtimeId]
117+
static bundle (entry: string, runtimeId?: string) {
118+
const runtime = Session.getRuntime(runtimeId)
120119
if (!runtime) {
121120
throw new Error(`${runtimeId} is not supported`)
122121
}

packages/cli/src/constructs/multi-step-check.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class MultiStepCheck extends Check {
4747

4848
this.playwrightConfig = props.playwrightConfig
4949

50-
if (!Session.availableRuntimes[this.runtimeId!]?.multiStepSupport) {
50+
if (!Session.getRuntime(this.runtimeId)?.multiStepSupport) {
5151
throw new Error('This runtime does not support multi step checks.')
5252
}
5353
if ('content' in props.code) {
@@ -65,7 +65,7 @@ export class MultiStepCheck extends Check {
6565
absoluteEntrypoint = path.join(path.dirname(Session.checkFileAbsolutePath), entrypoint)
6666
}
6767
// runtimeId will always be set by check or multi-step check defaults so it is safe to use ! operator
68-
const bundle = MultiStepCheck.bundle(absoluteEntrypoint, this.runtimeId!)
68+
const bundle = MultiStepCheck.bundle(absoluteEntrypoint, this.runtimeId)
6969
if (!bundle.script) {
7070
throw new Error(`Multi-Step check "${logicalId}" is not allowed to be empty`)
7171
}
@@ -99,8 +99,8 @@ export class MultiStepCheck extends Check {
9999
}
100100
}
101101

102-
static bundle (entry: string, runtimeId: string) {
103-
const runtime = Session.availableRuntimes[runtimeId]
102+
static bundle (entry: string, runtimeId?: string) {
103+
const runtime = Session.getRuntime(runtimeId)
104104
if (!runtime) {
105105
throw new Error(`${runtimeId} is not supported`)
106106
}

0 commit comments

Comments
 (0)