2023-08-07 21:24:58 +00:00
|
|
|
import * as http from "http"
|
|
|
|
import * as net from "net"
|
|
|
|
import { HttpClient } from "@actions/http-client"
|
2023-08-04 20:00:58 +00:00
|
|
|
import * as config from "../src/internal/shared/config"
|
|
|
|
import { createArtifactTwirpClient } from "../src/internal/shared/artifact-twirp-client"
|
|
|
|
import * as core from "@actions/core"
|
|
|
|
|
2023-08-08 19:49:05 +00:00
|
|
|
jest.mock("@actions/http-client")
|
2023-08-04 20:00:58 +00:00
|
|
|
|
|
|
|
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(() => {})
|
2023-08-07 15:43:39 +00:00
|
|
|
jest.spyOn(config, "getResultsServiceUrl").mockReturnValue("http://localhost:8080")
|
|
|
|
jest.spyOn(config, "getRuntimeToken").mockReturnValue("token")
|
2023-08-04 20:00:58 +00:00
|
|
|
})
|
|
|
|
|
2023-08-07 21:24:58 +00:00
|
|
|
beforeEach(() => {
|
2023-08-08 19:49:05 +00:00
|
|
|
jest.clearAllMocks()
|
2023-08-07 21:24:58 +00:00
|
|
|
})
|
|
|
|
|
2023-08-04 20:00:58 +00:00
|
|
|
it("should successfully create a client", () => {
|
|
|
|
const client = createArtifactTwirpClient("upload")
|
|
|
|
expect(client).toBeDefined()
|
|
|
|
})
|
|
|
|
|
|
|
|
it("should make a request", async () => {
|
2023-08-08 19:49:05 +00:00
|
|
|
const mockPost = jest.fn(() => {
|
2023-08-07 23:26:07 +00:00
|
|
|
const msg = new http.IncomingMessage(new net.Socket())
|
|
|
|
msg.statusCode = 200
|
|
|
|
return {
|
|
|
|
message: msg,
|
|
|
|
readBody: () => {return Promise.resolve(`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`)}
|
|
|
|
}
|
|
|
|
})
|
2023-08-08 19:49:05 +00:00
|
|
|
const mockHttpClient = (HttpClient as unknown as jest.Mock).mockImplementation(() => {
|
|
|
|
return {
|
|
|
|
post: mockPost
|
|
|
|
}
|
|
|
|
})
|
2023-08-07 23:26:07 +00:00
|
|
|
|
|
|
|
const client = createArtifactTwirpClient("upload")
|
2023-08-07 21:24:58 +00:00
|
|
|
const artifact = await client.CreateArtifact(
|
2023-08-04 20:00:58 +00:00
|
|
|
{
|
|
|
|
workflowRunBackendId: "1234",
|
|
|
|
workflowJobRunBackendId: "5678",
|
|
|
|
name: "artifact",
|
2023-08-07 15:43:39 +00:00
|
|
|
version: 4
|
|
|
|
}
|
2023-08-04 20:00:58 +00:00
|
|
|
)
|
2023-08-07 21:24:58 +00:00
|
|
|
|
2023-08-08 19:49:05 +00:00
|
|
|
expect(mockHttpClient).toHaveBeenCalledTimes(1)
|
2023-08-07 23:26:07 +00:00
|
|
|
expect(mockPost).toHaveBeenCalledTimes(1)
|
2023-08-04 20:00:58 +00:00
|
|
|
expect(artifact).toBeDefined()
|
2023-08-07 21:24:58 +00:00
|
|
|
expect(artifact.ok).toBe(true)
|
|
|
|
expect(artifact.signedUploadUrl).toBe("http://localhost:8080/upload")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("should retry if the request fails", async () => {
|
2023-08-08 19:49:05 +00:00
|
|
|
const mockPost = jest
|
|
|
|
.fn(() => {
|
2023-08-07 23:26:07 +00:00
|
|
|
const msgSucceeded = new http.IncomingMessage(new net.Socket())
|
|
|
|
msgSucceeded.statusCode = 200
|
|
|
|
return {
|
|
|
|
message: msgSucceeded,
|
|
|
|
readBody: () => {return Promise.resolve(`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`)}
|
|
|
|
}
|
2023-08-07 21:24:58 +00:00
|
|
|
})
|
2023-08-08 19:49:05 +00:00
|
|
|
.mockImplementationOnce(() => {
|
|
|
|
const msgFailed = new http.IncomingMessage(new net.Socket())
|
|
|
|
msgFailed.statusCode = 500
|
|
|
|
msgFailed.statusMessage = "Internal Server Error"
|
2023-08-07 21:24:58 +00:00
|
|
|
return {
|
2023-08-08 19:49:05 +00:00
|
|
|
message: msgFailed,
|
2023-08-07 21:24:58 +00:00
|
|
|
readBody: () => {return Promise.resolve(`{"ok": false}`)}
|
|
|
|
}
|
|
|
|
})
|
2023-08-08 19:49:05 +00:00
|
|
|
const mockHttpClient = (HttpClient as unknown as jest.Mock).mockImplementation(() => {
|
2023-08-07 21:24:58 +00:00
|
|
|
return {
|
2023-08-08 19:49:05 +00:00
|
|
|
post: mockPost
|
2023-08-07 21:24:58 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-08-08 19:49:05 +00:00
|
|
|
const client = createArtifactTwirpClient(
|
|
|
|
"upload",
|
|
|
|
5, // retry 5 times
|
|
|
|
1, // wait 1 ms
|
|
|
|
1.5 // backoff factor
|
|
|
|
)
|
2023-08-07 21:24:58 +00:00
|
|
|
const artifact = await client.CreateArtifact(
|
|
|
|
{
|
|
|
|
workflowRunBackendId: "1234",
|
|
|
|
workflowJobRunBackendId: "5678",
|
|
|
|
name: "artifact",
|
|
|
|
version: 4
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2023-08-08 19:49:05 +00:00
|
|
|
expect(mockHttpClient).toHaveBeenCalledTimes(1)
|
2023-08-07 21:24:58 +00:00
|
|
|
expect(artifact).toBeDefined()
|
|
|
|
expect(artifact.ok).toBe(true)
|
|
|
|
expect(artifact.signedUploadUrl).toBe("http://localhost:8080/upload")
|
|
|
|
expect(mockPost).toHaveBeenCalledTimes(2)
|
2023-08-04 20:00:58 +00:00
|
|
|
})
|
2023-08-08 19:49:05 +00:00
|
|
|
|
|
|
|
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: () => {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: () => {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)
|
|
|
|
})
|
2023-08-04 20:00:58 +00:00
|
|
|
})
|
2023-08-07 21:24:58 +00:00
|
|
|
|