Skip to content

Commit 9331762

Browse files
feat: add TcpCheck construct [sc-22430] (#1012)
* feat: added tcp check construct [sc-22430] * feat: hide misleading url property from the user, use hostname instead * chore: add unit tests for TcpCheck (copied from ApiCheck) * chore: add a TcpCheck to e2e tests * feat: support all current TCP check features * fix: remove bodyType from TcpCheck, it has no purpose currently * refactor: update to new API schema * fix: responseData assertion does not have properties * Revert "fix: responseData assertion does not have properties" The property could be a regex, like with API checks and TEXT_BODY, so expose it after all. This reverts commit 2c7b8b8. * chore: fix variable name in tests * fix(tests): use our site for snapshots instead of an external page that's down --------- Co-authored-by: ejanusevicius <[email protected]>
1 parent 0173d7b commit 9331762

File tree

10 files changed

+377
-147
lines changed

10 files changed

+377
-147
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ Update and Unchanged:
240240
ApiCheck: api-check-high-freq
241241
HeartbeatCheck: heartbeat-check-1
242242
BrowserCheck: homepage-browser-check
243+
TcpCheck: tcp-check
243244
CheckGroup: my-group-1
244245
Dashboard: dashboard-1
245246
MaintenanceWindow: maintenance-window-1
@@ -252,6 +253,7 @@ Update and Unchanged:
252253
HeartbeatCheck: heartbeat-check-1
253254
BrowserCheck: homepage-browser-check
254255
BrowserCheck: snapshot-test.test.ts
256+
TcpCheck: tcp-check
255257
CheckGroup: my-group-1
256258
Dashboard: dashboard-1
257259
MaintenanceWindow: maintenance-window-1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* eslint-disable no-new */
2+
import { TcpCheck } from 'checkly/constructs'
3+
4+
new TcpCheck('tcp-check', {
5+
name: 'TCP Check',
6+
activated: false,
7+
request: {
8+
hostname: 'api.checklyhq.com',
9+
port: 443,
10+
},
11+
degradedResponseTime: 5000,
12+
maxResponseTime: 20000,
13+
})

packages/cli/e2e/__tests__/fixtures/snapshot-project/snapshot-test.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { expect, test } from '@playwright/test'
33

44
test.use({ actionTimeout: 10000 })
55

6-
test('WWW Snapshot Test', async ({ page }) => {
7-
await page.goto('https://info.cern.ch/hypertext/WWW/TheProject.html')
6+
test('Welcome Snapshot Test', async ({ page }) => {
7+
await page.goto('https://welcome.checklyhq.com')
88
await expect(page).toHaveScreenshot({ maxDiffPixels: 10000 })
99
console.log(process.env.SECRET_ENV)
1010
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { TcpCheck, CheckGroup, TcpRequest } from '../index'
2+
import { Project, Session } from '../project'
3+
4+
const runtimes = {
5+
'2022.10': { name: '2022.10', default: false, stage: 'CURRENT', description: 'Main updates are Playwright 1.28.0, Node.js 16.x and Typescript support. We are also dropping support for Puppeteer', dependencies: { '@playwright/test': '1.28.0', '@opentelemetry/api': '1.0.4', '@opentelemetry/sdk-trace-base': '1.0.1', '@faker-js/faker': '5.5.3', aws4: '1.11.0', axios: '0.27.2', btoa: '1.2.1', chai: '4.3.7', 'chai-string': '1.5.0', 'crypto-js': '4.1.1', expect: '29.3.1', 'form-data': '4.0.0', jsonwebtoken: '8.5.1', lodash: '4.17.21', mocha: '10.1.0', moment: '2.29.2', node: '16.x', otpauth: '9.0.2', playwright: '1.28.0', typescript: '4.8.4', uuid: '9.0.0' } },
6+
}
7+
8+
const request: TcpRequest = {
9+
hostname: 'acme.com',
10+
port: 443,
11+
}
12+
13+
describe('TcpCheck', () => {
14+
it('should not synthesize runtime if not specified even if default runtime is set', () => {
15+
Session.project = new Project('project-id', {
16+
name: 'Test Project',
17+
repoUrl: 'https://github.com/checkly/checkly-cli',
18+
})
19+
Session.availableRuntimes = runtimes
20+
Session.defaultRuntimeId = '2022.02'
21+
const check = new TcpCheck('test-check', {
22+
name: 'Test Check',
23+
request,
24+
})
25+
const payload = check.synthesize()
26+
expect(payload.runtimeId).toBeUndefined()
27+
delete Session.defaultRuntimeId
28+
})
29+
30+
it('should synthesize runtime if specified', () => {
31+
Session.project = new Project('project-id', {
32+
name: 'Test Project',
33+
repoUrl: 'https://github.com/checkly/checkly-cli',
34+
})
35+
Session.availableRuntimes = runtimes
36+
Session.defaultRuntimeId = '2022.02'
37+
const check = new TcpCheck('test-check', {
38+
name: 'Test Check',
39+
runtimeId: '2022.02',
40+
request,
41+
})
42+
const payload = check.synthesize()
43+
expect(payload.runtimeId).toEqual('2022.02')
44+
delete Session.defaultRuntimeId
45+
})
46+
47+
it('should apply default check settings', () => {
48+
Session.project = new Project('project-id', {
49+
name: 'Test Project',
50+
repoUrl: 'https://github.com/checkly/checkly-cli',
51+
})
52+
Session.checkDefaults = { tags: ['default tags'] }
53+
const check = new TcpCheck('test-check', {
54+
name: 'Test Check',
55+
request,
56+
})
57+
delete Session.checkDefaults
58+
expect(check).toMatchObject({ tags: ['default tags'] })
59+
})
60+
61+
it('should overwrite default check settings with check-specific config', () => {
62+
Session.project = new Project('project-id', {
63+
name: 'Test Project',
64+
repoUrl: 'https://github.com/checkly/checkly-cli',
65+
})
66+
Session.checkDefaults = { tags: ['default tags'] }
67+
const check = new TcpCheck('test-check', {
68+
name: 'Test Check',
69+
tags: ['test check'],
70+
request,
71+
})
72+
delete Session.checkDefaults
73+
expect(check).toMatchObject({ tags: ['test check'] })
74+
})
75+
76+
it('should support setting groups with `groupId`', () => {
77+
Session.project = new Project('project-id', {
78+
name: 'Test Project',
79+
repoUrl: 'https://github.com/checkly/checkly-cli',
80+
})
81+
const group = new CheckGroup('main-group', { name: 'Main Group', locations: [] })
82+
const check = new TcpCheck('main-check', {
83+
name: 'Main Check',
84+
request,
85+
groupId: group.ref(),
86+
})
87+
expect(check.synthesize()).toMatchObject({ groupId: { ref: 'main-group' } })
88+
})
89+
90+
it('should support setting groups with `group`', () => {
91+
Session.project = new Project('project-id', {
92+
name: 'Test Project',
93+
repoUrl: 'https://github.com/checkly/checkly-cli',
94+
})
95+
const group = new CheckGroup('main-group', { name: 'Main Group', locations: [] })
96+
const check = new TcpCheck('main-check', {
97+
name: 'Main Check',
98+
request,
99+
group,
100+
})
101+
expect(check.synthesize()).toMatchObject({ groupId: { ref: 'main-group' } })
102+
})
103+
})

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

+14-145
Original file line numberDiff line numberDiff line change
@@ -6,172 +6,41 @@ import { QueryParam } from './query-param'
66
import { pathToPosix } from '../services/util'
77
import { printDeprecationWarning } from '../reporters/util'
88
import { Content, Entrypoint } from './construct'
9+
import { Assertion as CoreAssertion, NumericAssertionBuilder, GeneralAssertionBuilder } from './internal/assertion'
910

10-
// eslint-disable-next-line no-restricted-syntax
11-
enum AssertionSource {
12-
STATUS_CODE = 'STATUS_CODE',
13-
JSON_BODY = 'JSON_BODY',
14-
HEADERS = 'HEADERS',
15-
TEXT_BODY = 'TEXT_BODY',
16-
RESPONSE_TIME = 'RESPONSE_TIME',
17-
}
18-
19-
// eslint-disable-next-line no-restricted-syntax
20-
enum AssertionComparison {
21-
EQUALS = 'EQUALS',
22-
NOT_EQUALS = 'NOT_EQUALS',
23-
HAS_KEY = 'HAS_KEY',
24-
NOT_HAS_KEY = 'NOT_HAS_KEY',
25-
HAS_VALUE = 'HAS_VALUE',
26-
NOT_HAS_VALUE = 'NOT_HAS_VALUE',
27-
IS_EMPTY = 'IS_EMPTY',
28-
NOT_EMPTY = 'NOT_EMPTY',
29-
GREATER_THAN = 'GREATER_THAN',
30-
LESS_THAN = 'LESS_THAN',
31-
CONTAINS = 'CONTAINS',
32-
NOT_CONTAINS = 'NOT_CONTAINS',
33-
IS_NULL = 'IS_NULL',
34-
NOT_NULL = 'NOT_NULL',
35-
}
11+
type AssertionSource =
12+
| 'STATUS_CODE'
13+
| 'JSON_BODY'
14+
| 'HEADERS'
15+
| 'TEXT_BODY'
16+
| 'RESPONSE_TIME'
3617

37-
export interface Assertion {
38-
source: string,
39-
property: string,
40-
comparison: string,
41-
target: string,
42-
regex: string|null,
43-
}
18+
export type Assertion = CoreAssertion<AssertionSource>
4419

4520
export class AssertionBuilder {
4621
static statusCode () {
47-
return new NumericAssertionBuilder(AssertionSource.STATUS_CODE)
22+
return new NumericAssertionBuilder<AssertionSource>('STATUS_CODE')
4823
}
4924

5025
static jsonBody (property?: string) {
51-
return new GeneralAssertionBuilder(AssertionSource.JSON_BODY, property)
26+
return new GeneralAssertionBuilder<AssertionSource>('JSON_BODY', property)
5227
}
5328

5429
static headers (property?: string, regex?: string) {
55-
return new GeneralAssertionBuilder(AssertionSource.HEADERS, property, regex)
30+
return new GeneralAssertionBuilder<AssertionSource>('HEADERS', property, regex)
5631
}
5732

5833
static textBody (property?: string) {
59-
return new GeneralAssertionBuilder(AssertionSource.TEXT_BODY, property)
34+
return new GeneralAssertionBuilder<AssertionSource>('TEXT_BODY', property)
6035
}
6136

6237
/** @deprecated Use responseTime() instead */
6338
static responseTme () {
64-
return new NumericAssertionBuilder(AssertionSource.RESPONSE_TIME)
39+
return new NumericAssertionBuilder<AssertionSource>('RESPONSE_TIME')
6540
}
6641

6742
static responseTime () {
68-
return new NumericAssertionBuilder(AssertionSource.RESPONSE_TIME)
69-
}
70-
}
71-
72-
class NumericAssertionBuilder {
73-
source: AssertionSource
74-
constructor (source: AssertionSource) {
75-
this.source = source
76-
}
77-
78-
equals (target: number): Assertion {
79-
return this._toAssertion(AssertionComparison.EQUALS, target)
80-
}
81-
82-
notEquals (target: number): Assertion {
83-
return this._toAssertion(AssertionComparison.NOT_EQUALS, target)
84-
}
85-
86-
lessThan (target: number): Assertion {
87-
return this._toAssertion(AssertionComparison.LESS_THAN, target)
88-
}
89-
90-
greaterThan (target: number): Assertion {
91-
return this._toAssertion(AssertionComparison.GREATER_THAN, target)
92-
}
93-
94-
/** @private */
95-
private _toAssertion (comparison: AssertionComparison, target: number): Assertion {
96-
return { source: this.source, comparison, property: '', target: target.toString(), regex: null }
97-
}
98-
}
99-
100-
class GeneralAssertionBuilder {
101-
source: AssertionSource
102-
property?: string
103-
regex?: string
104-
constructor (source: AssertionSource, property?: string, regex?: string) {
105-
this.source = source
106-
this.property = property
107-
this.regex = regex
108-
}
109-
110-
equals (target: string|number|boolean): Assertion {
111-
return this._toAssertion(AssertionComparison.EQUALS, target)
112-
}
113-
114-
notEquals (target: string|number|boolean): Assertion {
115-
return this._toAssertion(AssertionComparison.NOT_EQUALS, target)
116-
}
117-
118-
hasKey (target: string): Assertion {
119-
return this._toAssertion(AssertionComparison.HAS_KEY, target)
120-
}
121-
122-
notHasKey (target: string): Assertion {
123-
return this._toAssertion(AssertionComparison.NOT_HAS_KEY, target)
124-
}
125-
126-
hasValue (target: string|number|boolean): Assertion {
127-
return this._toAssertion(AssertionComparison.HAS_VALUE, target)
128-
}
129-
130-
notHasValue (target: string|number|boolean): Assertion {
131-
return this._toAssertion(AssertionComparison.NOT_HAS_VALUE, target)
132-
}
133-
134-
isEmpty () {
135-
return this._toAssertion(AssertionComparison.IS_EMPTY)
136-
}
137-
138-
notEmpty () {
139-
return this._toAssertion(AssertionComparison.NOT_EMPTY)
140-
}
141-
142-
lessThan (target: string|number|boolean): Assertion {
143-
return this._toAssertion(AssertionComparison.LESS_THAN, target)
144-
}
145-
146-
greaterThan (target: string|number|boolean): Assertion {
147-
return this._toAssertion(AssertionComparison.GREATER_THAN, target)
148-
}
149-
150-
contains (target: string): Assertion {
151-
return this._toAssertion(AssertionComparison.CONTAINS, target)
152-
}
153-
154-
notContains (target: string): Assertion {
155-
return this._toAssertion(AssertionComparison.NOT_CONTAINS, target)
156-
}
157-
158-
isNull () {
159-
return this._toAssertion(AssertionComparison.IS_NULL)
160-
}
161-
162-
isNotNull () {
163-
return this._toAssertion(AssertionComparison.NOT_NULL)
164-
}
165-
166-
/** @private */
167-
private _toAssertion (comparison: AssertionComparison, target?: string|number|boolean): Assertion {
168-
return {
169-
source: this.source,
170-
comparison,
171-
property: this.property ?? '',
172-
target: target?.toString() ?? '',
173-
regex: this.regex ?? null,
174-
}
43+
return new NumericAssertionBuilder<AssertionSource>('RESPONSE_TIME')
17544
}
17645
}
17746

packages/cli/src/constructs/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ export * from './phone-call-alert-channel'
2525
export * from './retry-strategy'
2626
export * from './multi-step-check'
2727
export * from './alert-escalation-policy'
28+
export * from './tcp-check'

0 commit comments

Comments
 (0)