Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add TcpCheck construct [sc-22430] #1012

2 changes: 2 additions & 0 deletions packages/cli/e2e/__tests__/deploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ Update and Unchanged:
ApiCheck: api-check-high-freq
HeartbeatCheck: heartbeat-check-1
BrowserCheck: homepage-browser-check
TcpCheck: tcp-check
CheckGroup: my-group-1
Dashboard: dashboard-1
MaintenanceWindow: maintenance-window-1
Expand All @@ -252,6 +253,7 @@ Update and Unchanged:
HeartbeatCheck: heartbeat-check-1
BrowserCheck: homepage-browser-check
BrowserCheck: snapshot-test.test.ts
TcpCheck: tcp-check
CheckGroup: my-group-1
Dashboard: dashboard-1
MaintenanceWindow: maintenance-window-1
Expand Down
13 changes: 13 additions & 0 deletions packages/cli/e2e/__tests__/fixtures/deploy-project/tcp.check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable no-new */
import { TcpCheck } from 'checkly/constructs'

new TcpCheck('tcp-check', {
name: 'TCP Check',
activated: false,
request: {
hostname: 'api.checklyhq.com',
port: 443,
},
degradedResponseTime: 5000,
maxResponseTime: 20000,
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { expect, test } from '@playwright/test'

test.use({ actionTimeout: 10000 })

test('WWW Snapshot Test', async ({ page }) => {
await page.goto('https://info.cern.ch/hypertext/WWW/TheProject.html')
test('Welcome Snapshot Test', async ({ page }) => {
await page.goto('https://welcome.checklyhq.com')
await expect(page).toHaveScreenshot({ maxDiffPixels: 10000 })
console.log(process.env.SECRET_ENV)
})
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 103 additions & 0 deletions packages/cli/src/constructs/__tests__/tcp-check.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { TcpCheck, CheckGroup, TcpRequest } from '../index'
import { Project, Session } from '../project'

const runtimes = {
'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' } },
}

const request: TcpRequest = {
hostname: 'acme.com',
port: 443,
}

describe('TcpCheck', () => {
it('should not synthesize runtime if not specified even if default runtime is set', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
Session.availableRuntimes = runtimes
Session.defaultRuntimeId = '2022.02'
const check = new TcpCheck('test-check', {
name: 'Test Check',
request,
})
const payload = check.synthesize()
expect(payload.runtimeId).toBeUndefined()
delete Session.defaultRuntimeId
})

it('should synthesize runtime if specified', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
Session.availableRuntimes = runtimes
Session.defaultRuntimeId = '2022.02'
const check = new TcpCheck('test-check', {
name: 'Test Check',
runtimeId: '2022.02',
request,
})
const payload = check.synthesize()
expect(payload.runtimeId).toEqual('2022.02')
delete Session.defaultRuntimeId
})

it('should apply default check settings', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
Session.checkDefaults = { tags: ['default tags'] }
const check = new TcpCheck('test-check', {
name: 'Test Check',
request,
})
delete Session.checkDefaults
expect(check).toMatchObject({ tags: ['default tags'] })
})

it('should overwrite default check settings with check-specific config', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
Session.checkDefaults = { tags: ['default tags'] }
const check = new TcpCheck('test-check', {
name: 'Test Check',
tags: ['test check'],
request,
})
delete Session.checkDefaults
expect(check).toMatchObject({ tags: ['test check'] })
})

it('should support setting groups with `groupId`', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
const group = new CheckGroup('main-group', { name: 'Main Group', locations: [] })
const check = new TcpCheck('main-check', {
name: 'Main Check',
request,
groupId: group.ref(),
})
expect(check.synthesize()).toMatchObject({ groupId: { ref: 'main-group' } })
})

it('should support setting groups with `group`', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
const group = new CheckGroup('main-group', { name: 'Main Group', locations: [] })
const check = new TcpCheck('main-check', {
name: 'Main Check',
request,
group,
})
expect(check.synthesize()).toMatchObject({ groupId: { ref: 'main-group' } })
})
})
159 changes: 14 additions & 145 deletions packages/cli/src/constructs/api-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,172 +6,41 @@ import { QueryParam } from './query-param'
import { pathToPosix } from '../services/util'
import { printDeprecationWarning } from '../reporters/util'
import { Content, Entrypoint } from './construct'
import { Assertion as CoreAssertion, NumericAssertionBuilder, GeneralAssertionBuilder } from './internal/assertion'

// eslint-disable-next-line no-restricted-syntax
enum AssertionSource {
STATUS_CODE = 'STATUS_CODE',
JSON_BODY = 'JSON_BODY',
HEADERS = 'HEADERS',
TEXT_BODY = 'TEXT_BODY',
RESPONSE_TIME = 'RESPONSE_TIME',
}

// eslint-disable-next-line no-restricted-syntax
enum AssertionComparison {
EQUALS = 'EQUALS',
NOT_EQUALS = 'NOT_EQUALS',
HAS_KEY = 'HAS_KEY',
NOT_HAS_KEY = 'NOT_HAS_KEY',
HAS_VALUE = 'HAS_VALUE',
NOT_HAS_VALUE = 'NOT_HAS_VALUE',
IS_EMPTY = 'IS_EMPTY',
NOT_EMPTY = 'NOT_EMPTY',
GREATER_THAN = 'GREATER_THAN',
LESS_THAN = 'LESS_THAN',
CONTAINS = 'CONTAINS',
NOT_CONTAINS = 'NOT_CONTAINS',
IS_NULL = 'IS_NULL',
NOT_NULL = 'NOT_NULL',
}
type AssertionSource =
| 'STATUS_CODE'
| 'JSON_BODY'
| 'HEADERS'
| 'TEXT_BODY'
| 'RESPONSE_TIME'

export interface Assertion {
source: string,
property: string,
comparison: string,
target: string,
regex: string|null,
}
export type Assertion = CoreAssertion<AssertionSource>

export class AssertionBuilder {
static statusCode () {
return new NumericAssertionBuilder(AssertionSource.STATUS_CODE)
return new NumericAssertionBuilder<AssertionSource>('STATUS_CODE')
}

static jsonBody (property?: string) {
return new GeneralAssertionBuilder(AssertionSource.JSON_BODY, property)
return new GeneralAssertionBuilder<AssertionSource>('JSON_BODY', property)
}

static headers (property?: string, regex?: string) {
return new GeneralAssertionBuilder(AssertionSource.HEADERS, property, regex)
return new GeneralAssertionBuilder<AssertionSource>('HEADERS', property, regex)
}

static textBody (property?: string) {
return new GeneralAssertionBuilder(AssertionSource.TEXT_BODY, property)
return new GeneralAssertionBuilder<AssertionSource>('TEXT_BODY', property)
}

/** @deprecated Use responseTime() instead */
static responseTme () {
return new NumericAssertionBuilder(AssertionSource.RESPONSE_TIME)
return new NumericAssertionBuilder<AssertionSource>('RESPONSE_TIME')
}

static responseTime () {
return new NumericAssertionBuilder(AssertionSource.RESPONSE_TIME)
}
}

class NumericAssertionBuilder {
source: AssertionSource
constructor (source: AssertionSource) {
this.source = source
}

equals (target: number): Assertion {
return this._toAssertion(AssertionComparison.EQUALS, target)
}

notEquals (target: number): Assertion {
return this._toAssertion(AssertionComparison.NOT_EQUALS, target)
}

lessThan (target: number): Assertion {
return this._toAssertion(AssertionComparison.LESS_THAN, target)
}

greaterThan (target: number): Assertion {
return this._toAssertion(AssertionComparison.GREATER_THAN, target)
}

/** @private */
private _toAssertion (comparison: AssertionComparison, target: number): Assertion {
return { source: this.source, comparison, property: '', target: target.toString(), regex: null }
}
}

class GeneralAssertionBuilder {
source: AssertionSource
property?: string
regex?: string
constructor (source: AssertionSource, property?: string, regex?: string) {
this.source = source
this.property = property
this.regex = regex
}

equals (target: string|number|boolean): Assertion {
return this._toAssertion(AssertionComparison.EQUALS, target)
}

notEquals (target: string|number|boolean): Assertion {
return this._toAssertion(AssertionComparison.NOT_EQUALS, target)
}

hasKey (target: string): Assertion {
return this._toAssertion(AssertionComparison.HAS_KEY, target)
}

notHasKey (target: string): Assertion {
return this._toAssertion(AssertionComparison.NOT_HAS_KEY, target)
}

hasValue (target: string|number|boolean): Assertion {
return this._toAssertion(AssertionComparison.HAS_VALUE, target)
}

notHasValue (target: string|number|boolean): Assertion {
return this._toAssertion(AssertionComparison.NOT_HAS_VALUE, target)
}

isEmpty () {
return this._toAssertion(AssertionComparison.IS_EMPTY)
}

notEmpty () {
return this._toAssertion(AssertionComparison.NOT_EMPTY)
}

lessThan (target: string|number|boolean): Assertion {
return this._toAssertion(AssertionComparison.LESS_THAN, target)
}

greaterThan (target: string|number|boolean): Assertion {
return this._toAssertion(AssertionComparison.GREATER_THAN, target)
}

contains (target: string): Assertion {
return this._toAssertion(AssertionComparison.CONTAINS, target)
}

notContains (target: string): Assertion {
return this._toAssertion(AssertionComparison.NOT_CONTAINS, target)
}

isNull () {
return this._toAssertion(AssertionComparison.IS_NULL)
}

isNotNull () {
return this._toAssertion(AssertionComparison.NOT_NULL)
}

/** @private */
private _toAssertion (comparison: AssertionComparison, target?: string|number|boolean): Assertion {
return {
source: this.source,
comparison,
property: this.property ?? '',
target: target?.toString() ?? '',
regex: this.regex ?? null,
}
return new NumericAssertionBuilder<AssertionSource>('RESPONSE_TIME')
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/constructs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export * from './phone-call-alert-channel'
export * from './retry-strategy'
export * from './multi-step-check'
export * from './alert-escalation-policy'
export * from './tcp-check'
Loading
Loading