Skip to content

Commit a2480d4

Browse files
committed
refactor: update eventsource dependency and clean up code
1 parent ca85bc9 commit a2480d4

File tree

10 files changed

+218
-422
lines changed

10 files changed

+218
-422
lines changed

package-lock.json

Lines changed: 33 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@
6060
"@aws-sdk/client-lambda": "^3.817.0",
6161
"@inquirer/prompts": "^7.5.3",
6262
"@smartthings/core-sdk": "^8.4.1",
63-
"axios": "1.9.0",
63+
"axios": "1.10.0",
6464
"chalk": "^5.4.1",
6565
"env-paths": "^3.0.0",
66-
"eventsource": "^2.0.2",
66+
"eventsource": "^4.0.0",
6767
"express": "^5.1.0",
6868
"get-port-please": "^3.1.2",
6969
"inquirer": "^9.3.7",
@@ -78,6 +78,7 @@
7878
"qs": "^6.14.0",
7979
"table": "^6.9.0",
8080
"tslib": "^2.8.1",
81+
"undici": "^7.11.0",
8182
"uuid": "^11.1.0",
8283
"yargs": "^18.0.0"
8384
},
@@ -88,7 +89,6 @@
8889
"@commitlint/cli": "^19.8.1",
8990
"@commitlint/config-conventional": "^19.8.1",
9091
"@stylistic/eslint-plugin": "^2.8.0",
91-
"@types/eventsource": "^1.1.15",
9292
"@types/express": "^5.0.2",
9393
"@types/inquirer": "^9.0.7",
9494
"@types/jest": "^29.5.14",

src/__tests__/lib/live-logging.test.ts

Lines changed: 2 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,10 @@ import { jest } from '@jest/globals'
22

33
import type { networkInterfaces } from 'node:os'
44
import type { inspect } from 'node:util'
5-
import type { PeerCertificate, TLSSocket } from 'node:tls'
65

76
import type axios from 'axios'
8-
import { AxiosError } from 'axios'
97
import stripAnsi from 'strip-ansi'
108

11-
import type { Authenticator } from '@smartthings/core-sdk'
12-
13-
import type { HostVerifier, DriverInfo, LiveLogClientConfig } from '../../lib/live-logging.js'
149
import type { LiveLogMessage } from '../../lib/sse-io.js'
1510
import type { fatalError } from '../../lib/util.js'
1611

@@ -27,8 +22,6 @@ jest.unstable_mockModule('node:util', () => ({
2722
inspect: inspectMock,
2823
}))
2924

30-
const { debugMock, getLoggerMock, isDebugEnabledMock } = await import('../test-lib/logger-mock.js')
31-
3225
const requestMock = jest.fn<typeof axios.request>()
3326
jest.unstable_mockModule('axios', () => ({
3427
default: {
@@ -46,20 +39,20 @@ const {
4639
handleConnectionErrors,
4740
liveLogMessageFormatter,
4841
logLevels,
49-
newLiveLogClient,
5042
parseIpAndPort,
5143
} = await import('../../lib/live-logging.js')
5244

5345

54-
/* eslint-disable @typescript-eslint/naming-convention */
5546
describe('liveLogMessageFormatter', () => {
47+
/* eslint-disable @typescript-eslint/naming-convention */
5648
const errorEvent: LiveLogMessage = {
5749
timestamp: 'event timestamp',
5850
driver_id: 'driver-id',
5951
driver_name: 'Driver',
6052
log_level: logLevels.error.value,
6153
message: 'Something bad happened.',
6254
}
55+
/* eslint-enable @typescript-eslint/naming-convention */
6356

6457
it('returns timestamp in event format', () => {
6558
const format = liveLogMessageFormatter(errorEvent)
@@ -138,160 +131,3 @@ describe('handleConnectionErrors', () => {
138131
expect(() => handleConnectionErrors('192.168.0.1', error)).not.toThrow()
139132
})
140133
})
141-
142-
describe('newLiveLogClient', () => {
143-
const authority = '192.168.222.1:9495'
144-
const authHeaders = { 'Auth-Header': 'header-value' }
145-
const authenticateMock = jest.fn<Authenticator['authenticate']>().mockResolvedValue(authHeaders)
146-
const authenticatorMock = { authenticate: authenticateMock } as unknown as Authenticator
147-
const baseConfig: LiveLogClientConfig = {
148-
authority,
149-
authenticator: authenticatorMock,
150-
timeout: 1000,
151-
userAgent: 'user-agent',
152-
}
153-
154-
const driver1: DriverInfo = { driver_id: 'driver-id-1', driver_name: 'Driver 1', status: 'some-status' }
155-
const driver2: DriverInfo = { driver_id: 'driver-id-2', driver_name: 'Driver 2', status: 'other-status' }
156-
const hubDriverList = [driver1, driver2]
157-
const certificate = { valid_from: 'yesterday about 3 a.m.' } as PeerCertificate
158-
159-
it('generates populated LiveLogClient', () => {
160-
expect(newLiveLogClient(baseConfig)).toStrictEqual({
161-
getDrivers: expect.any(Function),
162-
getLogSource: expect.any(Function),
163-
})
164-
165-
expect(getLoggerMock).toHaveBeenCalledExactlyOnceWith('cli')
166-
})
167-
168-
describe('getDrivers', () => {
169-
it('returns driver list', async () => {
170-
const client = newLiveLogClient(baseConfig)
171-
requestMock.mockResolvedValueOnce({ data: hubDriverList })
172-
173-
expect(await client.getDrivers()).toBe(hubDriverList)
174-
175-
expect(authenticateMock).toHaveBeenCalledExactlyOnceWith()
176-
177-
expect(requestMock).toHaveBeenCalledExactlyOnceWith({
178-
url: 'https://192.168.222.1:9495/drivers',
179-
method: 'GET',
180-
httpsAgent: expect.anything(), // TODO
181-
timeout: 1000,
182-
headers: { 'User-Agent': 'user-agent', ...authHeaders },
183-
transitional: {
184-
silentJSONParsing: true,
185-
forcedJSONParsing: true,
186-
clarifyTimeoutError: true,
187-
},
188-
})
189-
190-
expect(isDebugEnabledMock).not.toHaveBeenCalled()
191-
expect(debugMock).not.toHaveBeenCalled()
192-
})
193-
194-
it('verifies once an only once', async () => {
195-
const verifierMock = jest.fn<HostVerifier>()
196-
const config = { ...baseConfig, verifier: verifierMock }
197-
const client = newLiveLogClient(config)
198-
const getPeerCertificateMock = jest.fn<typeof TLSSocket.prototype.getPeerCertificate>().mockReturnValue(certificate)
199-
requestMock.mockResolvedValue({ data: hubDriverList, request: { socket: { getPeerCertificate: getPeerCertificateMock } } })
200-
201-
expect(await client.getDrivers()).toBe(hubDriverList)
202-
expect(verifierMock).toHaveBeenCalledExactlyOnceWith(certificate)
203-
expect(getPeerCertificateMock).toHaveBeenCalledExactlyOnceWith()
204-
expect(requestMock).toHaveBeenCalledTimes(1)
205-
206-
// Still once even though another request has been made!
207-
expect(await client.getDrivers()).toBe(hubDriverList)
208-
expect(verifierMock).toHaveBeenCalledExactlyOnceWith(certificate)
209-
expect(getPeerCertificateMock).toHaveBeenCalledExactlyOnceWith()
210-
expect(requestMock).toHaveBeenCalledTimes(2)
211-
})
212-
213-
it.each([
214-
{ code: 'ECONNREFUSED', message: 'Unable to connect to 192.168.222.1:9495' },
215-
{ code: 'EHOSTUNREACH', message: 'Unable to connect to 192.168.222.1:9495' },
216-
{ code: 'ETIMEDOUT', message: 'Connection to 192.168.222.1:9495 timed out' },
217-
{ code: 'EHOSTDOWN', message: 'The host at 192.168.222.1:9495 is down' },
218-
])('handles %s axios error with user facing message', async ({ code, message }) => {
219-
const axiosError = { code, isAxiosError: true } as AxiosError
220-
const client = newLiveLogClient(baseConfig)
221-
requestMock.mockRejectedValueOnce(axiosError)
222-
223-
await expect(client.getDrivers()).rejects.toThrow(`${message}. Ensure hub address is correct and try again`)
224-
})
225-
226-
const jsonError = {
227-
request: {
228-
headers: {
229-
Authorization: 'Bearer 8a25775d-0e67-4dce-bbc9-0601ba6589ca',
230-
},
231-
},
232-
}
233-
it('logs axios error at debug level, scrubbing auth token', async () => {
234-
isDebugEnabledMock.mockReturnValueOnce(true)
235-
const toJSONMock = jest.fn<typeof AxiosError.prototype.toJSON>().mockReturnValue(jsonError)
236-
const axiosError = { code: 'ECONNREFUSED', isAxiosError: true } as AxiosError
237-
axiosError.toJSON = toJSONMock
238-
const client = newLiveLogClient(baseConfig)
239-
requestMock.mockRejectedValueOnce(axiosError)
240-
inspectMock.mockReturnValueOnce('inspected axios.toJSON Bearer 8a25775d-0e67-4dce-bbc9-0601ba6589ca')
241-
inspectMock.mockReturnValueOnce('inspected network interfaces')
242-
243-
await expect(client.getDrivers()).rejects.toThrow()
244-
245-
expect(isDebugEnabledMock).toHaveBeenCalledExactlyOnceWith()
246-
expect(inspectMock).toHaveBeenCalledTimes(2)
247-
expect(inspectMock).toHaveBeenCalledWith(jsonError)
248-
expect(inspectMock).toHaveBeenCalledWith(interfaces)
249-
expect(debugMock).toHaveBeenCalledWith('Error connecting to live-logging: inspected axios.toJSON' +
250-
' Bearer 8a25775d-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\n' +
251-
'Local network interfaces: inspected network interfaces')
252-
})
253-
254-
it('scrubs alternate auth token format', async () => {
255-
isDebugEnabledMock.mockReturnValueOnce(true)
256-
const toJSONMock = jest.fn<typeof AxiosError.prototype.toJSON>().mockReturnValue(jsonError)
257-
const axiosError = { code: 'ECONNREFUSED', isAxiosError: true } as AxiosError
258-
axiosError.toJSON = toJSONMock
259-
const client = newLiveLogClient(baseConfig)
260-
requestMock.mockRejectedValueOnce(axiosError)
261-
inspectMock.mockReturnValueOnce('inspected axios.toJSON Authorization: some-other-token-format')
262-
inspectMock.mockReturnValueOnce('inspected network interfaces')
263-
264-
await expect(client.getDrivers()).rejects.toThrow()
265-
266-
expect(isDebugEnabledMock).toHaveBeenCalledExactlyOnceWith()
267-
expect(inspectMock).toHaveBeenCalledTimes(2)
268-
expect(inspectMock).toHaveBeenCalledWith(jsonError)
269-
expect(inspectMock).toHaveBeenCalledWith(interfaces)
270-
expect(debugMock).toHaveBeenCalledWith('Error connecting to live-logging: inspected axios.toJSON' +
271-
' Authorization: (redacted)\n\n' +
272-
'Local network interfaces: inspected network interfaces')
273-
})
274-
275-
it('rethrows non-axios errors unchanged', async () => {
276-
const error = Error('other error')
277-
const client = newLiveLogClient(baseConfig)
278-
requestMock.mockRejectedValueOnce(error)
279-
280-
await expect(client.getDrivers()).rejects.toThrow(error)
281-
})
282-
})
283-
284-
describe('getLogSource', () => {
285-
const client = newLiveLogClient(baseConfig)
286-
287-
it('returns URL with no query parameters for all drivers', () => {
288-
expect(client.getLogSource()).toBe('https://192.168.222.1:9495/drivers/logs')
289-
})
290-
291-
it('includes query parameter for a specific driver', () => {
292-
expect(client.getLogSource('my-driver-id'))
293-
.toBe('https://192.168.222.1:9495/drivers/logs?driver_id=my-driver-id')
294-
})
295-
})
296-
})
297-
/* eslint-enable @typescript-eslint/naming-convention */

src/__tests__/lib/sse-util.test.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/__tests__/lib/util.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
clipToMaximum,
77
delay,
88
fatalError,
9+
handleSignals,
910
sanitize,
11+
terminationSignals,
1012
stringFromUnknown,
1113
} from '../../lib/util.js'
1214

@@ -113,3 +115,13 @@ describe('asTextBulletedList', () => {
113115
expect(asTextBulletedList(['one', 'two', 'three'])).toBe('\n - one\n - two\n - three')
114116
})
115117
})
118+
119+
test('handleSignals adds handler for all required Signals', () => {
120+
const handler = jest.fn()
121+
122+
handleSignals(handler)
123+
124+
terminationSignals.forEach(signal => {
125+
expect(process.listeners(signal)).toContain(handler)
126+
})
127+
})

0 commit comments

Comments
 (0)