diff --git a/package-lock.json b/package-lock.json index 79c3ae90..e8367a9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "flow-bin": "^0.115.0", "jest": "^27.2.5", "lerna": "^7.1.4", + "node-mocks-http": "^1.12.2", "nx": "16.6.0", "prettier": "^3.0.0", "ts-jest": "^27.0.5", @@ -2564,6 +2565,19 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -3752,6 +3766,18 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/conventional-changelog-angular": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", @@ -5600,6 +5626,15 @@ "node": ">= 6" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -8697,6 +8732,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -8863,6 +8907,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -8878,6 +8928,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -8891,6 +8950,18 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -9354,6 +9425,36 @@ "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", "dev": true }, + "node_modules/node-mocks-http": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.12.2.tgz", + "integrity": "sha512-xhWwC0dh35R9rf0j3bRZXuISXdHxxtMx0ywZQBwjrg3yl7KpRETzogfeCamUIjltpn0Fxvs/ZhGJul1vPLrdJQ==", + "dev": true, + "dependencies": { + "accepts": "^1.3.7", + "content-disposition": "^0.5.3", + "depd": "^1.1.0", + "fresh": "^0.5.2", + "merge-descriptors": "^1.0.1", + "methods": "^1.1.2", + "mime": "^1.3.4", + "parseurl": "^1.3.3", + "range-parser": "^1.2.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/node-mocks-http/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -10500,6 +10601,15 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -10853,6 +10963,15 @@ "node": ">=8" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -12527,6 +12646,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", diff --git a/package.json b/package.json index d64d2841..1b3e02e8 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,10 @@ "flow-bin": "^0.115.0", "jest": "^27.2.5", "lerna": "^7.1.4", + "node-mocks-http": "^1.12.2", "nx": "16.6.0", "prettier": "^3.0.0", "ts-jest": "^27.0.5", "typescript": "^3.9.9" } -} \ No newline at end of file +} diff --git a/packages/artifact/__tests__/artifact-http-client.test.ts b/packages/artifact/__tests__/artifact-http-client.test.ts index 14c0a9cf..44469ee6 100644 --- a/packages/artifact/__tests__/artifact-http-client.test.ts +++ b/packages/artifact/__tests__/artifact-http-client.test.ts @@ -1,8 +1,26 @@ +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") + +const mockPost = jest.fn((statusCode: number, body: string) => { + const msg = new http.IncomingMessage(new net.Socket()) + msg.statusCode = statusCode + return { + message: msg, + readBody: () => {return Promise.resolve(body)} + } +}) +jest.mock("@actions/http-client", () => { + return jest.fn().mockImplementation(() => { + return { + post: mockPost + } + }) +}) describe("artifact-http-client", () => { beforeAll(() => { @@ -15,14 +33,33 @@ describe("artifact-http-client", () => { jest.spyOn(config, "getRuntimeToken").mockReturnValue("token") }) + beforeEach(() => { + mockPost.mockClear(); + (HttpClient as unknown as jest.Mock).mockClear() + }) + it("should successfully create a client", () => { const client = createArtifactTwirpClient("upload") expect(client).toBeDefined() }) it("should make a request", async () => { - const client = createArtifactTwirpClient("upload") - const artifact = client.CreateArtifact( + /* + const mockHttpClient = (HttpClient as unknown as jest.Mock).mockImplementation(() => { + return { + post: () => { + const msg = new http.IncomingMessage(new net.Socket()) + msg.statusCode = 200 + return { + message: msg, + readBody: () => {return Promise.resolve(`{"ok": true, "signedUploadUrl": "http://localhost:8080/lol/upload"}`)} + } + } + } + }) + */ + const client = createArtifactTwirpClient("upload", new HttpClient()) + const artifact = await client.CreateArtifact( { workflowRunBackendId: "1234", workflowJobRunBackendId: "5678", @@ -30,6 +67,105 @@ describe("artifact-http-client", () => { version: 4 } ) + + expect(mockHttpClient).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 msg = new http.IncomingMessage(new net.Socket()) + if (mockPost.mock.calls.length > 1) { + msg.statusCode = 200 + return { + message: msg, + readBody: () => {return Promise.resolve(`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`)} + } + } + msg.statusCode = 500 + return { + message: msg, + readBody: () => {return Promise.resolve(`{"ok": false}`)} + } + }) + */ + const msgFailed = new http.IncomingMessage(new net.Socket()) + msgFailed.statusCode = 500 + const msgSucceeded = new http.IncomingMessage(new net.Socket()) + msgSucceeded.statusCode = 200 + + const mockPost = jest.fn() + mockPost.mockReturnValueOnce({ + message: msgFailed, + readBody: () => {return Promise.resolve(`{"ok": false}`)} + }).mockReturnValue({ + message: msgSucceeded, + readBody: () => {return Promise.resolve(`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`)} + }) + + /* + mockPost.mockImplementationOnce(() => { + const msg = new http.IncomingMessage(new net.Socket()) + msg.statusCode = 500 + return { + message: msg, + readBody: () => {return Promise.resolve(`{"ok": false}`)} + } + }) + + mockPost.mockImplementation(() => { + const msg = new http.IncomingMessage(new net.Socket()) + msg.statusCode = 200 + return { + message: msg, + readBody: () => {return Promise.resolve(`{"ok": true, "signedUploadUrl": "http://localhost:8080/lol/upload"}`)} + } + }) + */ + + jest.mock("@actions/http-client", () => { + return jest.fn().mockImplementation(() => { + return { + post: mockPost + } + }) + }) + /* + jest.mock("@actions/http-client", () => { + return { + HttpClient: jest.fn().mockImplementation(() => { + return { + post: mockPost + } + }) + } + }) + jest.mock("@actions/http-client", () => { + return jest.fn().mockImplementation(() => { + return { + post: mockPost + } + }) + }) + */ + + const client = createArtifactTwirpClient("upload", new HttpClient()) + const artifact = await client.CreateArtifact( + { + workflowRunBackendId: "1234", + workflowJobRunBackendId: "5678", + name: "artifact", + version: 4 + } + ) + + expect(artifact).toBeDefined() + expect(artifact.ok).toBe(true) + expect(artifact.signedUploadUrl).toBe("http://localhost:8080/upload") + expect(mockPost).toHaveBeenCalledTimes(2) }) }) + diff --git a/tsconfig.json b/tsconfig.json index fb610c6e..f706470a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "commonjs", "strict": true, "declaration": true, - "target": "es6", + "target": "es2020", "sourceMap": true, "noImplicitAny": false, "baseUrl": "./", @@ -22,4 +22,4 @@ "**/*.test.ts", "**/__mocks__/*" ] -} \ No newline at end of file +}