-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1486 from actions/bethanyj28/add-twirp-client
Add twirp client
- Loading branch information
Showing
7 changed files
with
422 additions
and
34 deletions.
There are no files selected for viewing
200 changes: 200 additions & 0 deletions
200
packages/artifact/__tests__/artifact-http-client.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import * as http from 'http' | ||
import * as net from 'net' | ||
import {HttpClient} from '@actions/http-client' | ||
import * as config from '../src/internal/shared/config' | ||
import {createArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client' | ||
import * as core from '@actions/core' | ||
|
||
jest.mock('@actions/http-client') | ||
|
||
describe('artifact-http-client', () => { | ||
beforeAll(() => { | ||
// mock all output so that there is less noise when running tests | ||
jest.spyOn(console, 'log').mockImplementation(() => {}) | ||
jest.spyOn(core, 'debug').mockImplementation(() => {}) | ||
jest.spyOn(core, 'info').mockImplementation(() => {}) | ||
jest.spyOn(core, 'warning').mockImplementation(() => {}) | ||
jest | ||
.spyOn(config, 'getResultsServiceUrl') | ||
.mockReturnValue('http://localhost:8080') | ||
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('token') | ||
}) | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
it('should successfully create a client', () => { | ||
const client = createArtifactTwirpClient('upload') | ||
expect(client).toBeDefined() | ||
}) | ||
|
||
it('should make a request', async () => { | ||
const mockPost = jest.fn(() => { | ||
const msg = new http.IncomingMessage(new net.Socket()) | ||
msg.statusCode = 200 | ||
return { | ||
message: msg, | ||
readBody: async () => { | ||
return Promise.resolve( | ||
`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}` | ||
) | ||
} | ||
} | ||
}) | ||
const mockHttpClient = ( | ||
HttpClient as unknown as jest.Mock | ||
).mockImplementation(() => { | ||
return { | ||
post: mockPost | ||
} | ||
}) | ||
|
||
const client = createArtifactTwirpClient('upload') | ||
const artifact = await client.CreateArtifact({ | ||
workflowRunBackendId: '1234', | ||
workflowJobRunBackendId: '5678', | ||
name: 'artifact', | ||
version: 4 | ||
}) | ||
|
||
expect(mockHttpClient).toHaveBeenCalledTimes(1) | ||
expect(mockPost).toHaveBeenCalledTimes(1) | ||
expect(artifact).toBeDefined() | ||
expect(artifact.ok).toBe(true) | ||
expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload') | ||
}) | ||
|
||
it('should retry if the request fails', async () => { | ||
const mockPost = jest | ||
.fn(() => { | ||
const msgSucceeded = new http.IncomingMessage(new net.Socket()) | ||
msgSucceeded.statusCode = 200 | ||
return { | ||
message: msgSucceeded, | ||
readBody: async () => { | ||
return Promise.resolve( | ||
`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}` | ||
) | ||
} | ||
} | ||
}) | ||
.mockImplementationOnce(() => { | ||
const msgFailed = new http.IncomingMessage(new net.Socket()) | ||
msgFailed.statusCode = 500 | ||
msgFailed.statusMessage = 'Internal Server Error' | ||
return { | ||
message: msgFailed, | ||
readBody: async () => { | ||
return Promise.resolve(`{"ok": false}`) | ||
} | ||
} | ||
}) | ||
const mockHttpClient = ( | ||
HttpClient as unknown as jest.Mock | ||
).mockImplementation(() => { | ||
return { | ||
post: mockPost | ||
} | ||
}) | ||
|
||
const client = createArtifactTwirpClient( | ||
'upload', | ||
5, // retry 5 times | ||
1, // wait 1 ms | ||
1.5 // backoff factor | ||
) | ||
const artifact = await client.CreateArtifact({ | ||
workflowRunBackendId: '1234', | ||
workflowJobRunBackendId: '5678', | ||
name: 'artifact', | ||
version: 4 | ||
}) | ||
|
||
expect(mockHttpClient).toHaveBeenCalledTimes(1) | ||
expect(artifact).toBeDefined() | ||
expect(artifact.ok).toBe(true) | ||
expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload') | ||
expect(mockPost).toHaveBeenCalledTimes(2) | ||
}) | ||
|
||
it('should fail if the request fails 5 times', async () => { | ||
const mockPost = jest.fn(() => { | ||
const msgFailed = new http.IncomingMessage(new net.Socket()) | ||
msgFailed.statusCode = 500 | ||
msgFailed.statusMessage = 'Internal Server Error' | ||
return { | ||
message: msgFailed, | ||
readBody: async () => { | ||
return Promise.resolve(`{"ok": false}`) | ||
} | ||
} | ||
}) | ||
|
||
const mockHttpClient = ( | ||
HttpClient as unknown as jest.Mock | ||
).mockImplementation(() => { | ||
return { | ||
post: mockPost | ||
} | ||
}) | ||
const client = createArtifactTwirpClient( | ||
'upload', | ||
5, // retry 5 times | ||
1, // wait 1 ms | ||
1.5 // backoff factor | ||
) | ||
await expect(async () => { | ||
await client.CreateArtifact({ | ||
workflowRunBackendId: '1234', | ||
workflowJobRunBackendId: '5678', | ||
name: 'artifact', | ||
version: 4 | ||
}) | ||
}).rejects.toThrowError( | ||
'Failed to make request after 5 attempts: Failed request: (500) Internal Server Error' | ||
) | ||
expect(mockHttpClient).toHaveBeenCalledTimes(1) | ||
expect(mockPost).toHaveBeenCalledTimes(5) | ||
}) | ||
|
||
it('should fail immediately if there is a non-retryable error', async () => { | ||
const mockPost = jest.fn(() => { | ||
const msgFailed = new http.IncomingMessage(new net.Socket()) | ||
msgFailed.statusCode = 401 | ||
msgFailed.statusMessage = 'Unauthorized' | ||
return { | ||
message: msgFailed, | ||
readBody: async () => { | ||
return Promise.resolve(`{"ok": false}`) | ||
} | ||
} | ||
}) | ||
|
||
const mockHttpClient = ( | ||
HttpClient as unknown as jest.Mock | ||
).mockImplementation(() => { | ||
return { | ||
post: mockPost | ||
} | ||
}) | ||
const client = createArtifactTwirpClient( | ||
'upload', | ||
5, // retry 5 times | ||
1, // wait 1 ms | ||
1.5 // backoff factor | ||
) | ||
await expect(async () => { | ||
await client.CreateArtifact({ | ||
workflowRunBackendId: '1234', | ||
workflowJobRunBackendId: '5678', | ||
name: 'artifact', | ||
version: 4 | ||
}) | ||
}).rejects.toThrowError( | ||
'Received non-retryable error: Failed request: (401) Unauthorized' | ||
) | ||
expect(mockHttpClient).toHaveBeenCalledTimes(1) | ||
expect(mockPost).toHaveBeenCalledTimes(1) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.