Merge pull request #2133 from actions/danwkennedy/test-blob-stream-timeout
Test: add a timeout test for downloading chunks from the stream
This commit is contained in:
@@ -111,6 +111,16 @@ const mockGetArtifactSuccess = jest.fn(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const mockGetArtifactHung = jest.fn(() => {
|
||||||
|
const message = new http.IncomingMessage(new net.Socket())
|
||||||
|
message.statusCode = 200
|
||||||
|
// Don't push any data or call push(null) to end the stream
|
||||||
|
// This creates a stream that hangs and never completes
|
||||||
|
return {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const mockGetArtifactFailure = jest.fn(() => {
|
const mockGetArtifactFailure = jest.fn(() => {
|
||||||
const message = new http.IncomingMessage(new net.Socket())
|
const message = new http.IncomingMessage(new net.Socket())
|
||||||
message.statusCode = 500
|
message.statusCode = 500
|
||||||
@@ -611,4 +621,32 @@ describe('download-artifact', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('streamExtractExternal', () => {
|
||||||
|
it('should fail if the timeout is exceeded', async () => {
|
||||||
|
const mockSlowGetArtifact = jest.fn(mockGetArtifactHung)
|
||||||
|
|
||||||
|
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
get: mockSlowGetArtifact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await streamExtractExternal(
|
||||||
|
fixtures.blobStorageUrl,
|
||||||
|
fixtures.workspaceDir,
|
||||||
|
{timeout: 2}
|
||||||
|
)
|
||||||
|
expect(true).toBe(false) // should not be called
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeInstanceOf(Error)
|
||||||
|
expect(e.message).toContain('did not respond in 2ms')
|
||||||
|
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
|
||||||
|
expect(mockSlowGetArtifact).toHaveBeenCalledTimes(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ async function streamExtract(
|
|||||||
|
|
||||||
export async function streamExtractExternal(
|
export async function streamExtractExternal(
|
||||||
url: string,
|
url: string,
|
||||||
directory: string
|
directory: string,
|
||||||
|
opts: {timeout: number} = {timeout: 30 * 1000}
|
||||||
): Promise<StreamExtractResponse> {
|
): Promise<StreamExtractResponse> {
|
||||||
const client = new httpClient.HttpClient(getUserAgentString())
|
const client = new httpClient.HttpClient(getUserAgentString())
|
||||||
const response = await client.get(url)
|
const response = await client.get(url)
|
||||||
@@ -74,18 +75,17 @@ export async function streamExtractExternal(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = 30 * 1000 // 30 seconds
|
|
||||||
let sha256Digest: string | undefined = undefined
|
let sha256Digest: string | undefined = undefined
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timerFn = (): void => {
|
const timerFn = (): void => {
|
||||||
const timeoutError = new Error(
|
const timeoutError = new Error(
|
||||||
`Blob storage chunk did not respond in ${timeout}ms`
|
`Blob storage chunk did not respond in ${opts.timeout}ms`
|
||||||
)
|
)
|
||||||
response.message.destroy(timeoutError)
|
response.message.destroy(timeoutError)
|
||||||
reject(timeoutError)
|
reject(timeoutError)
|
||||||
}
|
}
|
||||||
const timer = setTimeout(timerFn, timeout)
|
const timer = setTimeout(timerFn, opts.timeout)
|
||||||
|
|
||||||
const hashStream = crypto.createHash('sha256').setEncoding('hex')
|
const hashStream = crypto.createHash('sha256').setEncoding('hex')
|
||||||
const passThrough = new stream.PassThrough()
|
const passThrough = new stream.PassThrough()
|
||||||
|
|||||||
Reference in New Issue
Block a user