From d9c1434c8862d918660fec53e0d2300ee2bfee74 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Sun, 28 Jan 2024 19:31:57 -0800 Subject: [PATCH 1/5] Add new `full` tests, for complete integration testing --- jest.config.js | 12 + package-lock.json | 66 ++++- package.json | 5 +- tests/full/fixtures/a-pulsar-package/match.js | 40 +++ .../fixtures/a-pulsar-package/package.json | 11 + .../full/fixtures/a-pulsar-package/readme.md | 3 + tests/full/fixtures/a-pulsar-package/tags.js | 10 + tests/full/fixtures/b-pulsar-package/match.js | 53 ++++ .../fixtures/b-pulsar-package/package.json | 11 + .../full/fixtures/b-pulsar-package/readme.md | 3 + tests/full/fixtures/b-pulsar-package/tags.js | 18 ++ .../fixtures/c-pulsar-package/package.json | 11 + .../full/fixtures/c-pulsar-package/readme.md | 3 + tests/full/fixtures/c-pulsar-package/tags.js | 13 + tests/full/publish.test.js | 256 ++++++++++++++++++ 15 files changed, 508 insertions(+), 7 deletions(-) create mode 100644 tests/full/fixtures/a-pulsar-package/match.js create mode 100644 tests/full/fixtures/a-pulsar-package/package.json create mode 100644 tests/full/fixtures/a-pulsar-package/readme.md create mode 100644 tests/full/fixtures/a-pulsar-package/tags.js create mode 100644 tests/full/fixtures/b-pulsar-package/match.js create mode 100644 tests/full/fixtures/b-pulsar-package/package.json create mode 100644 tests/full/fixtures/b-pulsar-package/readme.md create mode 100644 tests/full/fixtures/b-pulsar-package/tags.js create mode 100644 tests/full/fixtures/c-pulsar-package/package.json create mode 100644 tests/full/fixtures/c-pulsar-package/readme.md create mode 100644 tests/full/fixtures/c-pulsar-package/tags.js create mode 100644 tests/full/publish.test.js diff --git a/jest.config.js b/jest.config.js index 5b7304fa..a82fa430 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,6 +9,18 @@ const config = { "/node_modules/", ], projects: [ + { + displayName: "Full-Tests", + globalSetup: "/node_modules/@databases/pg-test/jest/globalSetup", + globalTeardown: "/node_modules/@databases/pg-test/jest/globalTeardown", + setupFilesAfterEnv: [ + "/tests/helpers/global.setup.jest.js", + "/tests/helpers/handlers.setup.jest.js" + ], + testMatch: [ + "/tests/full/**.test.js" + ] + }, { displayName: "Integration-Tests", globalSetup: "/node_modules/@databases/pg-test/jest/globalSetup", diff --git a/package-lock.json b/package-lock.json index 62cdcafe..92dfa316 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "js-yaml": "^4.1.0", "parse-github-url": "^1.0.2", "postgres": "^3.3.4", + "semver": "^7.5.4", "superagent": "^8.0.9" }, "devDependencies": { @@ -32,6 +33,7 @@ "jest-joi": "^1.1.17", "joi": "^17.9.1", "jsdoc-to-markdown": "^8.0.0", + "nock": "^13.5.0", "prettier": "^2.8.7", "supertest": "^6.3.3" }, @@ -6257,6 +6259,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6741,6 +6749,20 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/nock": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.0.tgz", + "integrity": "sha512-9hc1eCS2HtOz+sE9W7JQw/tXJktg0zoPSu48s/pYe73e25JW9ywiowbqnUSd7iZPeVawLcVpPZeZS312fwSY+g==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -7467,6 +7489,15 @@ "node": ">= 6" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -7938,9 +7969,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -13799,6 +13830,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -14178,6 +14215,17 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nock": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.0.tgz", + "integrity": "sha512-9hc1eCS2HtOz+sE9W7JQw/tXJktg0zoPSu48s/pYe73e25JW9ywiowbqnUSd7iZPeVawLcVpPZeZS312fwSY+g==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -14680,6 +14728,12 @@ "sisteransi": "^1.0.5" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -14990,9 +15044,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } diff --git a/package.json b/package.json index abf098ca..a463031d 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,10 @@ }, "scripts": { "start": "node ./src/server.js", - "test": "cross-env NODE_ENV=test PULSAR_STATUS=dev jest --selectProjects Integration-Tests Unit-Tests --runInBand", + "test": "cross-env NODE_ENV=test PULSAR_STATUS=dev jest --selectProjects Integration-Tests Unit-Tests Full-Tests --runInBand", "test:unit": "cross-env NODE_ENV=test PULSAR_STATUS=dev jest --selectProjects Unit-Tests", "test:integration": "cross-env NODE_ENV=test PULSAR_STATUS=dev jest --selectProjects Integration-Tests --runInBand", + "test:full": "cross-env NODE_ENV=test PULSAR_STATUS=dev jest --selectProjects Full-Tests --runInBand", "start:dev": "cross-env PULSAR_STATUS=dev node ./src/dev_server.js", "lint": "prettier --check -u -w .", "complex": "cr --newmi --config .complexrc .", @@ -49,6 +50,7 @@ "js-yaml": "^4.1.0", "parse-github-url": "^1.0.2", "postgres": "^3.3.4", + "semver": "^7.5.4", "superagent": "^8.0.9" }, "devDependencies": { @@ -64,6 +66,7 @@ "jest-joi": "^1.1.17", "joi": "^17.9.1", "jsdoc-to-markdown": "^8.0.0", + "nock": "^13.5.0", "prettier": "^2.8.7", "supertest": "^6.3.3" } diff --git a/tests/full/fixtures/a-pulsar-package/match.js b/tests/full/fixtures/a-pulsar-package/match.js new file mode 100644 index 00000000..bdb2697f --- /dev/null +++ b/tests/full/fixtures/a-pulsar-package/match.js @@ -0,0 +1,40 @@ +module.exports = { + name: "a-pulsar-package", + owner: "confused-Techie", + readme: expect.stringContaining("I'm a readme!"), + metadata: { + dist: { + sha: "09f", + tarball: "https://api.github.com/repos/confused-Techie/a-pulsar-package/tarball/refs/tags/v1.0.0" + }, + main: "./lib/main.js", + name: "a-pulsar-package", + engines: { atom: "*" }, + license: "MIT", + version: "1.0.0", + repository: "https://github.com/confused-Techie/a-pulsar-package", + description: "A package for stuff." + }, + releases: { latest: "1.0.0" }, + versions: { + "1.0.0": { + dist: { + tarball: "https://api.pulsar-edit.dev/api/packages/a-pulsar-package/versions/1.0.0/tarball" + }, + main: "./lib/main.js", + name: "a-pulsar-package", + license: "MIT", + version: "1.0.0", + repository: "https://github.com/confused-Techie/a-pulsar-package", + description: "A package for stuff." + } + }, + repository: { + url: "https://github.com/confused-Techie/a-pulsar-package", + type: "git" + }, + creation_method: "User Made Package", + downloads: "0", + stargazers_count: "0", + badges: [ { title: "Made for Pulsar!", type: "success" } ] +}; diff --git a/tests/full/fixtures/a-pulsar-package/package.json b/tests/full/fixtures/a-pulsar-package/package.json new file mode 100644 index 00000000..9e967b84 --- /dev/null +++ b/tests/full/fixtures/a-pulsar-package/package.json @@ -0,0 +1,11 @@ +{ + "name": "a-pulsar-package", + "version": "1.0.0", + "description": "A package for stuff.", + "main": "./lib/main.js", + "engines": { + "atom": "*" + }, + "repository": "https://github.com/confused-Techie/a-pulsar-package", + "license": "MIT" +} diff --git a/tests/full/fixtures/a-pulsar-package/readme.md b/tests/full/fixtures/a-pulsar-package/readme.md new file mode 100644 index 00000000..aa42e485 --- /dev/null +++ b/tests/full/fixtures/a-pulsar-package/readme.md @@ -0,0 +1,3 @@ +# a-pulsar-package + +I'm a readme! diff --git a/tests/full/fixtures/a-pulsar-package/tags.js b/tests/full/fixtures/a-pulsar-package/tags.js new file mode 100644 index 00000000..812417ed --- /dev/null +++ b/tests/full/fixtures/a-pulsar-package/tags.js @@ -0,0 +1,10 @@ +module.exports = [ + { + name: "v1.0.0", + tarball_url: "https://api.github.com/repos/confused-Techie/a-pulsar-package/tarball/refs/tags/v1.0.0", + commit: { + sha: "09f", + url: "https://api.github.com/repos/confused-Techie/a-pulsar-package/commits/09f" + } + } +]; diff --git a/tests/full/fixtures/b-pulsar-package/match.js b/tests/full/fixtures/b-pulsar-package/match.js new file mode 100644 index 00000000..f6f4b635 --- /dev/null +++ b/tests/full/fixtures/b-pulsar-package/match.js @@ -0,0 +1,53 @@ +module.exports = { + name: "b-pulsar-package", + owner: "confused-Techie", + readme: expect.stringContaining("I'm a readme!"), + metadata: { + dist: { + sha: "09f", + tarball: "https://api.github.com/repos/confused-Techie/b-pulsar-package/tarball/refs/tags/v2.0.0" + }, + main: "./lib/main.js", + name: "b-pulsar-package", + engines: { atom: "*" }, + license: "MIT", + version: "2.0.0", + repository: "https://github.com/confused-Techie/b-pulsar-package", + description: "A package for stuff." + }, + releases: { latest: "2.0.0" }, + versions: { + "2.0.0": { + dist: { + tarball: "https://api.pulsar-edit.dev/api/packages/b-pulsar-package/versions/2.0.0/tarball" + }, + main: "./lib/main.js", + name: "b-pulsar-package", + license: "MIT", + version: "2.0.0", + repository: "https://github.com/confused-Techie/b-pulsar-package", + description: "A package for stuff." + }, + "1.0.0": { + dist: { + tarball: "https://api.pulsar-edit.dev/api/packages/b-pulsar-package/versions/1.0.0/tarball" + }, + //main: "./lib/main.js", + // Since building the package.json of v1.0.0 is an absent build, we + // can't expect it to know what the 'main' entry may have been + name: "b-pulsar-package", + license: "MIT", + version: "1.0.0", + repository: "https://github.com/confused-Techie/b-pulsar-package", + description: "A package for stuff." + } + }, + repository: { + url: "https://github.com/confused-Techie/b-pulsar-package", + type: "git" + }, + creation_method: "User Made Package", + downloads: "0", + stargazers_count: "0", + badges: [ { title: "Made for Pulsar!", type: "success" } ] +}; diff --git a/tests/full/fixtures/b-pulsar-package/package.json b/tests/full/fixtures/b-pulsar-package/package.json new file mode 100644 index 00000000..3f3eac86 --- /dev/null +++ b/tests/full/fixtures/b-pulsar-package/package.json @@ -0,0 +1,11 @@ +{ + "name": "b-pulsar-package", + "version": "2.0.0", + "description": "A package for stuff.", + "main": "./lib/main.js", + "engines": { + "atom": "*" + }, + "repository": "https://github.com/confused-Techie/b-pulsar-package", + "license": "MIT" +} diff --git a/tests/full/fixtures/b-pulsar-package/readme.md b/tests/full/fixtures/b-pulsar-package/readme.md new file mode 100644 index 00000000..be180a46 --- /dev/null +++ b/tests/full/fixtures/b-pulsar-package/readme.md @@ -0,0 +1,3 @@ +# b-pulsar-package + +I'm a readme! diff --git a/tests/full/fixtures/b-pulsar-package/tags.js b/tests/full/fixtures/b-pulsar-package/tags.js new file mode 100644 index 00000000..dbde2dcd --- /dev/null +++ b/tests/full/fixtures/b-pulsar-package/tags.js @@ -0,0 +1,18 @@ +module.exports = [ + { + name: "v1.0.0", + tarball_url: "https://api.github.com/repos/confused-Techie/b-pulsar-package/tarball/refs/tags/v1.0.0", + commit: { + sha: "09f", + url: "https://api.github.com/repos/confused-Techie/b-pulsar-package/commits/09f" + } + }, + { + name: "v2.0.0", + tarball_url: "https://api.github.com/repos/confused-Techie/b-pulsar-package/tarball/refs/tags/v2.0.0", + commit: { + sha: "09f", + url: "https://api.github.com/repos/confused-Techie/b-pulsar-package/commits/09f" + } + } +]; diff --git a/tests/full/fixtures/c-pulsar-package/package.json b/tests/full/fixtures/c-pulsar-package/package.json new file mode 100644 index 00000000..e19b0f53 --- /dev/null +++ b/tests/full/fixtures/c-pulsar-package/package.json @@ -0,0 +1,11 @@ +{ + "name": "c-pulsar-package", + "version": "2.0.0", + "description": "A package for stuff", + "main": "./lib/main.js", + "engines": { + "atom": "*" + }, + "repository": "https://github.com/confused-Techie/c-pulsar-package", + "license": "MIT" +} diff --git a/tests/full/fixtures/c-pulsar-package/readme.md b/tests/full/fixtures/c-pulsar-package/readme.md new file mode 100644 index 00000000..be180a46 --- /dev/null +++ b/tests/full/fixtures/c-pulsar-package/readme.md @@ -0,0 +1,3 @@ +# b-pulsar-package + +I'm a readme! diff --git a/tests/full/fixtures/c-pulsar-package/tags.js b/tests/full/fixtures/c-pulsar-package/tags.js new file mode 100644 index 00000000..73d9608d --- /dev/null +++ b/tests/full/fixtures/c-pulsar-package/tags.js @@ -0,0 +1,13 @@ +module.exports = [ + { + name: "v1.0.0", + tarball_url: "https://api.github.com/repos/confused-Techie/c-pulsar-package/tarball/refs/tags/v1.0.0", + commit: { + sha: "09f", + url: "https://api.github.com/repos/confused-Techie/c-pulsar-package/commits/09f" + } + } + // The `package.json` declares it's version as '2.0.0' but we only provide + // tags for version v1.0.0 in this way we want to make sure that this publication + // fails! +]; diff --git a/tests/full/publish.test.js b/tests/full/publish.test.js new file mode 100644 index 00000000..a163db53 --- /dev/null +++ b/tests/full/publish.test.js @@ -0,0 +1,256 @@ +const fs = require("fs"); +const { Buffer } = require("node:buffer"); +const supertest = require("supertest"); +const nock = require("nock"); +const app = require("../../src/setupEndpoints.js"); +const database = require("../../src/database/_export.js"); + +function getFileEncoded(loc) { + const file = fs.readFileSync(loc, { encoding: "utf8" }); + + const buf = Buffer.from(file).toString("base64"); + + return buf; +} + +describe("can publish packages", () => { + + beforeAll(async () => { + // Create our test user + let addUser = await database.insertNewUser( + "full-publish-test-user", + "full-publish-test-user-node-id", + "https://roadtonowhere.com" + ); + expect(addUser.ok).toBe(true); + + nock.disableNetConnect(); + nock.enableNetConnect("127.0.0.1"); + }); + + afterAll(() => { + nock.cleanAll(); + nock.enableNetConnect(); + }); + + beforeEach(() => { + // Ensure the ownership tests passes for our test user + nock("https://api.github.com/") + .get("/user") + .reply(200, { + node_id: "full-publish-test-user-node-id" + }); + }); + + afterEach(() => { + // Remove any interceptors setup for this test + nock.cleanAll(); + }); + + test("when everything is bog standard", async () => { + + // Ensure user has ownership of repo + nock("https://api.github.com/") + .get("/repos/confused-Techie/a-pulsar-package/collaborators?page=1") + .reply(200, [ + { + node_id: "full-publish-test-user-node-id", + permissions: { + admin: true, + maintain: true, + push: true + }, + role_name: "Admin" + } + ]); + + // Ensure we can report that the package exists + nock("https://api.github.com") + .get("/repos/confused-Techie/a-pulsar-package") + .reply(200, { + full_name: "confused-Techie/a-pulsar-package" + }); + + // Ensure we can get the readme + nock("https://api.github.com") + .get("/repos/confused-Techie/a-pulsar-package/readme") + .reply(200, { + content: getFileEncoded("./tests/full/fixtures/a-pulsar-package/readme.md"), + encoding: "base64" + }); + + // Ensure we can get the tags + nock("https://api.github.com") + .get("/repos/confused-Techie/a-pulsar-package/tags") + .reply(200, require("./fixtures/a-pulsar-package/tags.js")); + + // Ensure we can get the `package.json` + nock("https://api.github.com") + .get("/repos/confused-Techie/a-pulsar-package/contents/package.json") + .reply(200, { + content: getFileEncoded("./tests/full/fixtures/a-pulsar-package/package.json"), + encoding: "base64" + }); + + // Lets fail any feature detection requests for now + nock("https://api.github.com") + .get("/repos/confused-Techie/a-pulsar-package/contents/snippets") + .reply(404); + + nock("https://api.github.com") + .get("/repos/confused-Techie/a-pulsar-package/contents/grammars") + .reply(404); + + const res = await supertest(app) + .post("/api/packages") + .query({ repository: "confused-Techie/a-pulsar-package" }) + .set({ Authorization: "any-token-will-do" }); + + expect(res).toHaveHTTPCode(201); + + const pack = res.body; + + expect(pack.name).toBe("a-pulsar-package"); + expect(pack).toMatchObject(require("./fixtures/a-pulsar-package/match.js")); + + await database.removePackageByName("a-pulsar-package", true); + }); + + test("when multiple tags are available", async () => { + // Ensure user has ownership of repo + nock("https://api.github.com/") + .get("/repos/confused-Techie/b-pulsar-package/collaborators?page=1") + .reply(200, [ + { + node_id: "full-publish-test-user-node-id", + permissions: { + admin: true, + maintain: true, + push: true + }, + role_name: "Admin" + } + ]); + + // Ensure we can report that the package exists + nock("https://api.github.com") + .get("/repos/confused-Techie/b-pulsar-package") + .reply(200, { + full_name: "confused-Techie/b-pulsar-package" + }); + + // Ensure we can get the readme + nock("https://api.github.com") + .get("/repos/confused-Techie/b-pulsar-package/readme") + .reply(200, { + content: getFileEncoded("./tests/full/fixtures/b-pulsar-package/readme.md"), + encoding: "base64" + }); + + // Ensure we can get the tags + nock("https://api.github.com") + .get("/repos/confused-Techie/b-pulsar-package/tags") + .reply(200, require("./fixtures/b-pulsar-package/tags.js")); + + // Ensure we can get the `package.json` + nock("https://api.github.com") + .get("/repos/confused-Techie/b-pulsar-package/contents/package.json") + .reply(200, { + content: getFileEncoded("./tests/full/fixtures/b-pulsar-package/package.json"), + encoding: "base64" + }); + + // Lets fail any feature detection requests for now + nock("https://api.github.com") + .get("/repos/confused-Techie/b-pulsar-package/contents/snippets") + .reply(404); + + nock("https://api.github.com") + .get("/repos/confused-Techie/b-pulsar-package/contents/grammars") + .reply(404); + + const res = await supertest(app) + .post("/api/packages") + .query({ repository: "confused-Techie/b-pulsar-package" }) + .set({ Authorization: "any-token-will-do" }); + + expect(res).toHaveHTTPCode(201); + + const pack = res.body; + + expect(pack.name).toBe("b-pulsar-package"); + expect(pack).toMatchObject(require("./fixtures/b-pulsar-package/match.js")); + + await database.removePackageByName("b-pulsar-package", true); + }); + + test("when a package version is missing", async () => { + // Ensure user has ownership of repo + nock("https://api.github.com/") + .get("/repos/confused-Techie/c-pulsar-package/collaborators?page=1") + .reply(200, [ + { + node_id: "full-publish-test-user-node-id", + permissions: { + admin: true, + maintain: true, + push: true + }, + role_name: "Admin" + } + ]); + + // Ensure we can report that the package exists + nock("https://api.github.com") + .get("/repos/confused-Techie/c-pulsar-package") + .reply(200, { + full_name: "confused-Techie/c-pulsar-package" + }); + + // Ensure we can get the readme + nock("https://api.github.com") + .get("/repos/confused-Techie/c-pulsar-package/readme") + .reply(200, { + content: getFileEncoded("./tests/full/fixtures/c-pulsar-package/readme.md"), + encoding: "base64" + }); + + // Ensure we can get the tags + nock("https://api.github.com") + .get("/repos/confused-Techie/c-pulsar-package/tags") + .reply(200, require("./fixtures/c-pulsar-package/tags.js")); + + // Ensure we can get the `package.json` + nock("https://api.github.com") + .get("/repos/confused-Techie/c-pulsar-package/contents/package.json") + .reply(200, { + content: getFileEncoded("./tests/full/fixtures/c-pulsar-package/package.json"), + encoding: "base64" + }); + + // Lets fail any feature detection requests for now + nock("https://api.github.com") + .get("/repos/confused-Techie/c-pulsar-package/contents/snippets") + .reply(404); + + nock("https://api.github.com") + .get("/repos/confused-Techie/c-pulsar-package/contents/grammars") + .reply(404); + + // This test data has a `package.json` that declares it's version as '2.0.0' + // But the tags file only provides `v1.0.0` + // We expect this to fail because of the undiscoverable tag + + const res = await supertest(app) + .post("/api/packages") + .query({ repository: "confused-Techie/c-pulsar-package" }) + .set({ Authorization: "any-token-will-do" }); + + let errMsg = "Server Error: From Bad Repo: Error: Unable to locate the tag"; + errMsg += " for 'package.json' version '2.0.0'. Are you sure you published a matching tag?"; + + expect(res).toHaveHTTPCode(500); + expect(res.body.message).toBe(errMsg); + + }); +}); From f1aa975ab3ba05cff77a99e8b8e47442bdf33016 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Sun, 28 Jan 2024 19:32:21 -0800 Subject: [PATCH 2/5] Resolve errors discovered via new tests --- src/models/constructNewPackagePublishData.js | 229 +++++++++++++++++++ src/vcs.js | 44 ++-- 2 files changed, 253 insertions(+), 20 deletions(-) create mode 100644 src/models/constructNewPackagePublishData.js diff --git a/src/models/constructNewPackagePublishData.js b/src/models/constructNewPackagePublishData.js new file mode 100644 index 00000000..34aaeea4 --- /dev/null +++ b/src/models/constructNewPackagePublishData.js @@ -0,0 +1,229 @@ +/** + * @function constructNewPackagePublishData + * @desc Takes the multiple different sets of data from VCS.newPackageData() + * and constructs an object that can then be used for publication. + * This superseeds the `PackageObject` object builder, which attempted to be too + * generic for it's own good. + */ +const parseGithubURL = require("parse-github-url"); +const semver = require("semver"); + +module.exports = +function constructNewPackagePublishData(opts = {}) { + // `opts` Contains: + // ownerRepo = OWNER/REPO (string) + // provider = Provider object from VCS + // packageJson = The full `package.json` from the latest version + // tags = The GitHub API return of tag information + // readme = The full text readme + + let out = {}; + + // Lets first apply some defaults, not really needed on new packages, but kept + // for backwards compatibility with tests + out.creation_method = "User Made Package"; + out.downloads = "0"; + out.stargazers_count = "0"; + out.badges = []; + + opts.parsedUrl = parseGithubURL(opts.ownerRepo); + + if (typeof opts.packageJson.repository === "string") { + opts.parsedRepo = parseGithubURL(opts.packageJson.repository); + } else if (typeof opts.packageJson.repository === "object" && typeof opts.packageJson.repository.url === "string") { + // Support repository objects in `package.json` + opts.parsedRepo = parseGithubURL(opts.packageJson.repository.url); + } + + // Now lets setup some constants that we will use over and over + let PACK_NAME = findPackName(opts); + let LATEST_VER = findLatestVer(opts); + + out.name = PACK_NAME; + out.owner = findOwner(opts); + out.readme = (typeof opts.readme === "string" ? opts.readme : "" ); + out.repository = opts.provider; + out.metadata = buildMeta(opts); + out.releases = { + latest: LATEST_VER + }; + + // From here we want to build or version objects, except we don't have the + // `package.json` info for anything except our latest version. Which means + // we can't build it out the same way we do for others. This means we will only + // build out fully the latest version, and make stubbed mandatory-only entries + // for the rest. + + out.versions = {}; + // So lets loop through all versions, we will use the same meta object for the + // latest, while getting mandatory only fields for the rest + out.versions[LATEST_VER] = buildMeta(opts); + + for (let i = 0; i < opts.tags.length; i++) { + let curVer = semver.clean(opts.tags[i].name); + if (curVer !== LATEST_VER) { + out.versions[curVer] = buildAbsentVer(curVer, opts); + } + } + + // Now we should be good to go + + return out; +} + +function throwIfFalse(value, id, func) { + if (typeof value === "boolean" && !false) { + if (typeof func === "function") { + // Support callback to a custom function for generating the error output + func(); + } else { + throw new Error(`The value '${id}' couldn't be found!`); + } + } +} + +function findPackName(opts) { + let name = false; + + // Prefer getting the name from the actual package. + // Since the repo name might not match the package name. + if (typeof opts.packageJson?.name === "string") { + // Find name as normally declared on the `package.json` + name = opts.packageJson.name; + } else if (typeof opts.parsedUrl?.name === "string") { + // Find name via the parsed `ownerRepo` provided for publication + name = opts.parsedUrl.name; + } else if (typeof opts.parsedRepo?.name === "string") { + // Fallback to the repository of the `package.json` which shouldn't be needed + name = opts.parsedRepo.name; + } + + throwIfFalse(name, "package name"); + return name; +} + +function findOwner(opts) { + let owner = false; + + // Like findPackName we will prefer finding this info in the package + if (typeof opts.parsedRepo?.owner === "string") { + // Find owner via the normally declared `repository` entry of the `package.json` + owner = opts.parsedRepo.owner; + } else if (opts.parsedUrl?.owner === "string") { + // Find owner via the parsed URL for publication + owner = opts.parsedUrl.owner; + } else if (typeof opts.packageJson.author === "string") { + // The above shouldn't fail, if it does, we can look at a simple owner entry + // on the `package.json`. This doesn't account for non-string entries + owner = opts.packageJson.author; + } + + throwIfFalse(owner, "owner"); + return owner; +} + +function findLatestVer(opts) { + let latest = false; + + // We will assume the latest version is always whats present and defined + // within the `package.json` + if (typeof opts.packageJson.version === "string") { + latest = semver.clean(opts.packageJson.version); + } + + throwIfFalse(latest, "latest version"); + return latest; +} + +function buildMeta(opts) { + // This function builds the metadata object for a package publication, which + // consists of: + // - the latests `package.json` in full + // - an engines declaration + // - a `dist` object pointing to the latest version within the tags + + let sha = false; + let tarball = false; + let ver = findLatestVer(opts); + let out = {}; + + out = opts.packageJson; + + if (!out.engines) { + out.engines = { atom: "*" }; + } + + let tag = findTagForVer(ver, opts); + + sha = tag.commit.sha ?? false; + tarball = tag.tarball_url ?? false; + + out.dist = { + sha: sha, + tarball: tarball + }; + + // Then lets validate these required fields + throwIfFalse(sha, "", () => { + throw new Error(`Unable to locate the SHA for version: '${ver}'!`); + }); + throwIfFalse(tarball, "", () => { + throw new Error(`Unable to locate the Tarball URL for version: '${ver}'!`); + }); + + return out; +} + +function buildAbsentVer(ver, opts) { + // Here we will build an "absent" version object. Since we don't have the + // `package.json` of this version, it's considered absent, and will only receive + // the mandatory fields we can discover via the current version, and it's tag + let tag = findTagForVer(ver, opts); + + let sha = false; + let tarball = false; + let out = {}; + + sha = tag.commit.sha ?? false; + tarball = tag.tarball_url ?? false; + + out.name = opts.packageJson.name; + out.license = opts.packageJson.license ?? "NONE"; + out.version = ver; + out.description = opts.packageJson.description ?? "NONE"; + out.repository = opts.packageJson.repository; // TODO have more flexibility here + out.dist = { + sha: sha, + tarball: tarball + }; + + if (typeof opts.packageJson.theme === "string") { + out.theme = opts.packageJson.theme; + } + + // Lets validate + throwIfFalse(sha, "", () => { + throw new Error(`Unable to locate the SHA for version: '${ver}'!`); + }); + throwIfFalse(tarball, "", () => { + throw new Error(`Unable to locate the Tarball URL for version: '${ver}'!`); + }); + + return out; +} + +function findTagForVer(wantedTag, opts) { + let tag = semver.clean(wantedTag); + let tagFound = false; + + for (let i = 0; i < opts.tags.length; i++) { + if (semver.clean(opts.tags[i].name) === tag) { + tagFound = opts.tags[i]; + } + } + + throwIfFalse(tagFound, "", () => { + throw new Error(`Unable to locate the tag for 'package.json' version '${tag}'. Are you sure you published a matching tag?`); + }); + return tagFound; +} diff --git a/src/vcs.js b/src/vcs.js index de5309d5..3dcbb77c 100644 --- a/src/vcs.js +++ b/src/vcs.js @@ -8,7 +8,7 @@ const query = require("./query_parameters/index.js").logic; const utils = require("./utils.js"); -const PackageObject = require("./PackageObject.js"); +const constructNewPackagePublishData = require("./models/constructNewPackagePublishData.js"); const GitHub = require("./vcs_providers/github.js"); const ServerStatus = require("./ServerStatusObject.js"); const semVerInitRegex = /^\s*v/i; @@ -110,8 +110,6 @@ async function newPackageData(userObj, ownerRepo, service) { provider = new GitHub(); } - let newPack = new PackageObject().setOwnerRepo(ownerRepo); - let exists = await provider.exists(userObj, ownerRepo); if (!exists.ok) { @@ -135,10 +133,6 @@ async function newPackageData(userObj, ownerRepo, service) { .build(); } - // We need to ensure we add the semver prior to adding it's data - newPack.Version.addSemver(pack.content.version); - newPack.Version.addPackageJSON(pack.content.version, pack.content); - const tags = await provider.tags(userObj, ownerRepo); if (!tags.ok) { @@ -149,12 +143,6 @@ async function newPackageData(userObj, ownerRepo, service) { .build(); } - for (const tag of tags.content) { - newPack.Version.addSemver(tag.name); - newPack.Version.addTarball(tag.name, tag.tarball_url); - newPack.Version.addSha(tag.name, tag.commit.sha); - } - // Now to get our Readme const readme = await provider.readme(userObj, ownerRepo); @@ -190,16 +178,32 @@ async function newPackageData(userObj, ownerRepo, service) { // within the repo field, this would break support for transfering of ownership // or of changing the repo name. So we may have to live with that possibility. - newPack - .setReadme(readme.content) - .setName(pack.content.name.toLowerCase()) - .setCreationMethod("User Made Package") - .setRepositoryType(packRepoObj.type) - .setRepositoryURL(packRepoObj.url); + let newPack; + try { + newPack = constructNewPackagePublishData({ + ownerRepo: ownerRepo, + provider: packRepoObj, + packageJson: pack.content, + tags: tags.content, + readme: readme.content + }); + } catch(err) { + console.error(err); + // We know the errors generated from the constructor + // are intended to provide insight into what went wrong. + // So we will assign the content as the actual error + return new ServerStatus() + .notOk() + .setContent( + err.toString() + ) + .setShort("Bad Repo") + .build(); + } // For this we just use the most recent tag published to the repo. // and now the object is complete, lets return the pack, as a Server Status Object. - return new ServerStatus().isOk().setContent(newPack.buildFull()).build(); + return new ServerStatus().isOk().setContent(newPack).build(); } catch (err) { // An error occured somewhere during package generation return new ServerStatus() From 7170c04e50cecf8f6a4e342c994ffa8d758e2c65 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Sun, 28 Jan 2024 19:32:36 -0800 Subject: [PATCH 3/5] Preform cleanup after resolution --- src/PackageObject.js | 485 ------------------------------- src/controllers/postPackages.js | 7 + tests/unit/PackageObject.test.js | 54 ---- 3 files changed, 7 insertions(+), 539 deletions(-) delete mode 100644 src/PackageObject.js delete mode 100644 tests/unit/PackageObject.test.js diff --git a/src/PackageObject.js b/src/PackageObject.js deleted file mode 100644 index b324fcaa..00000000 --- a/src/PackageObject.js +++ /dev/null @@ -1,485 +0,0 @@ -const parseGithubUrl = require("parse-github-url"); - -/** - * @module PackageObject - * @desc This Module is used to aide in building Package Objects. - * Allowing a singular location for the proper data structure of a package object. - * And allowing an easy interface to add, modify, or retreive data about a - * package object. - */ - -const logger = require("./logger.js"); -const utils = require("./utils.js"); - -/** - * The PackageObject Object Builder - * @class - */ -class PackageObject { - constructor() { - this.Version = new Version(); - this.name = undefined; - this.ownerRepo = undefined; - this.repository = { - type: undefined, - url: undefined, - }; - this.downloads = undefined; - this.stargazers_count = undefined; - this.readme = undefined; - this.creationMethod = undefined; - this.owner = undefined; - } - - /** - * @function setName - * @param {string} packNameString - The Name of the package to set - * @desc Allows setting the name of the package. - */ - setName(packNameString) { - if (typeof packNameString !== "string") { - logger.generic( - 3, - `PackageObject.setName() called wtih ${packNameString}::${typeof packNameString}. Ignoring assignment.` - ); - return this; - } - - this.name = packNameString; - return this; - } - - /** - * @function setOwnerRepo - * @param {string} ownerRepoString - The `owner/repo` string combo to set for the package. - * @desc Allows setting the `owner/repo` of the package. - */ - setOwnerRepo(ownerRepoString) { - if (typeof ownerRepoString !== "string") { - logger.generic( - 3, - `PackageObject.setOwnerRepo() called with ${ownerRepoString}::${typeof ownerRepoString}. Ignoring assignment.` - ); - return this; - } - - let testValidation = ownerRepoString.split("/"); - - if (testValidation.length !== 2) { - // If the name is longer than 2, then it is not owner/repo - logger.generic( - 3, - `PackageObject.setOwnerRepo() called with invalid split length value ${ownerRepoString}. Ignoring assignment.` - ); - return this; - } - - this.ownerRepo = ownerRepoString; - return this; - } - - /** - * @function setDownloads - * @param {number|string} downloadCount - The Download count to add. - * @desc Allows setting the packages download count. - */ - setDownloads(downloadCount) { - if (Number.isNaN(parseInt(downloadCount))) { - logger.generic( - 3, - `PackageObject.setDownloads() called with invalid argument ${downloadCount}. Ignoring assignment.` - ); - return this; - } - - this.downloads = downloadCount; - return this; - } - - /** - * @function setStargazers - * @param {number|string} stargazerCount - The Stargazers count to add. - * @desc Allows setting the packages stargazer count. - */ - setStargazers(stargazerCount) { - if (Number.isNaN(parseInt(stargazerCount))) { - logger.generic( - 3, - `PackageObject.setStargazers() called with invalid argument ${stargazerCount}. Ignoring assignment.` - ); - return this; - } - - this.stargazers_count = stargazerCount; - return this; - } - - /** - * @function setReadme - * @param {string} readmeString - The Full text based readme. - * @desc Allows setting the packages readme data. - */ - setReadme(readmeString) { - if (typeof readmeString !== "string") { - logger.generic( - 3, - `PackageObject.setReadme() called with invalid argument type ${readmeString}::${typeof readmeString}. Ignoring assignment.` - ); - return this; - } - - this.readme = readmeString; - return this; - } - - /** - * @function setRepository - * @param {object} repoObject - The repo object containing `type` and `url` keys. - * @desc Allows setting the repository object of a package. As returned by - * `VCS.determineProvider()`. - */ - setRepository(repoObject) { - if (!repoObject.type || !repoObject.url) { - logger.generic( - 3, - `PackageObject.setRepository() called with invalid object ${repoObject}. Ignoring assignment.` - ); - return this; - } - - this.repository.type = repoObject.type; - this.repository.url = repoObject.url; - return this; - } - - /** - * @function setRepositoryType - * @param {string} repoType - The type of repo. - * @desc Allows setting the repo type of the package. As returned by `VCS.determineProvider().type` - */ - setRepositoryType(repoType) { - this.repository.type = repoType; - return this; - } - - /** - * @function setRepositoryURL - * @param {string} repoURL - The URL of the repo. - * @desc Allows setting the repo URL of the package. As returned - * by `VCS.determineProvider().url` - */ - setRepositoryURL(repoURL) { - this.repository.url = repoURL; - let parsed = parseGithubUrl(repoURL); - if (parsed) { - this.owner = parsed.owner; - } - return this; - } - - /** - * @function setCreationMethod - * @param {string} method - The creation method of the package. - * @desc Allows setting a creation method for the package. - */ - setCreationMethod(method) { - this.creationMethod = method; - return this; - } - - /** - * @function parse - * @param {object} pack - N/A - * @desc Unimplemented function. - * @todo Implement generic parse() - */ - parse(pack) { - // Parse can take a packages data and destructure accordingly - throw new Error("Not Implemented! ~ PackageObject.parse()"); - } - - /** - * @function buildShort - * @desc Returns an object matching the `Package Object Short` format, using provided data. - */ - buildShort() { - // The structure of this object is based off `./docs/resources/package_object_short.json` - // And should be considered our master reference for the Package Object Short Data Structure - let obj = { - name: this.name, - owner: this.owner, - repository: this.repository, - downloads: this.downloads, - stargazers_count: this.stargazers_count, - releases: { - latest: this.Version.getLatestVersionSemver(), - }, - readme: this.readme, - metadata: this.Version.getLatestVersionPackageJSON(), - }; - - // Here we could use Joi to validate the values within our data structure - // prior to returning. In the long term this is very much the method we should - // use, as the API should never be sending out invalid data. But for now TODO - return obj; - } - - /** - * @function buildFull - * @desc Returns an object matching the `Package Object Full` format. - * Using the provided data. - */ - buildFull() { - // This object structure is modeled directly off of `./docs/resources/package_object_full.json` - // Should be considered the master Package Object Full data structure - let obj = { - name: this.name, - owner: this.owner, - repository: this.repository, - downloads: this.downloads, - stargazers_count: this.stargazers_count, - creation_method: this.creationMethod, - releases: { - latest: this.Version.getLatestVersionSemver(), - }, - readme: this.readme, - metadata: this.Version.getLatestVersionPackageJSON(), - versions: this.Version.buildFullVersions(), - }; - - // Again would likely in the future want to use Joi to validate our object - return obj; - } -} - -/** - * A version object to help build package objects `Version` data. Accessible - * from the PackageObject via `PackageObject.Version` - * @class - */ -class Version { - constructor() { - this.latestSemver = undefined; - this.versions = {}; - this.semverInitRegex = /^\s*v/i; - } - - /** - * @function addVersion - * @param {object} value - N/A - * @desc Unimplemented function. - * @todo Implement Agnostic `addVersion()` - */ - addVersion(value) { - throw new Error("Not Implmented! ~ Version.addVersion()"); - } - - /** - * @function addVersions - * @param {object[]} values - N/A - * @desc An array handling variant that relies on the the unimplmented `addVersion` - * @todo Implement Agnostic `addVersion()` - */ - addVersions(values) { - for (const value in values) { - this.addVersion(value); - } - return this; - } - - /** - * @function addSemver - * @param {string} semver - The Semver to add to the package. - * @desc Handles adding a new semver value. - */ - addSemver(semver) { - // Allows adding a standalone semver to the version list. - let cleanSemver = this.cleanSemver(semver); - - if (cleanSemver === "") { - logger.generic( - 3, - `Version.addSemver() called with invalid semver ${semver}! Ignoring assignment` - ); - return this; - } - - if (this.versions[cleanSemver]) { - logger.generic( - 3, - `Version.addSemver() called with semver already within range! ${semver}` - ); - return this; - } - - this.versions[cleanSemver] = {}; - - // Now to determine if our newer semver is larger than the current latestSemver - if (typeof this.latestSemver === "undefined") { - this.latestSemver = cleanSemver; - } else { - console.log( - `Latest semver: ${this.latestSemver} - Typeof: ${typeof this - .latestSemver}` - ); - if ( - utils.semverGt( - utils.semverArray(cleanSemver), - utils.semverArray(this.latestSemver) - ) - ) { - // The provided semver is greater than our current latest - this.latestSemver = cleanSemver; - } - } - - return this; - } - - /** - * @function cleanSemver - * @param {string} semver - The Semver to clean. - * @desc A utility function that will parse and process a `semver` string - * to remove special characters, and remove any leading `v`s - */ - cleanSemver(semver) { - if (typeof semver !== "string") { - return ""; - } - - return semver.replace(this.semverInitRegex, "").trim(); - } - - /** - * @function addTarball - * @param {string} semver - The `semver` to add it to. - * @param {string} tarballURL - The url of the `tarball` to add. - * @desc Adds a `tarball` to the version specified. - */ - addTarball(semver, tarballURL) { - // Takes a valid existing semver to add the url too - if (!this.versions[this.cleanSemver(semver)]) { - logger.generic( - 3, - `Version.addTarball() called with semver outside known range! ${semver}` - ); - return this; - } - - this.versions[this.cleanSemver(semver)].tarball_url = tarballURL; - return this; - } - - /** - * @function addSha - * @param {string} semver - The `semver` to add it to. - * @param {string} sha - The SHA to add. - * @desc Adds a `sha` to the `version` specified. - */ - addSha(semver, sha) { - // Takes a valid existing semver to add the sha too - if (!this.versions[this.cleanSemver(semver)]) { - logger.generic( - 3, - `Version.addSHA() called with semver outside known range! ${semver}` - ); - return this; - } - - this.versions[this.cleanSemver(semver)].sha = sha; - return this; - } - - /** - * @function addPackageJSON - * @param {string} semver - The `semver` to add it to. - * @param {object} pack - The `package.json` to add. - * @desc Adds a `package.json` to a specific version. - */ - addPackageJSON(semver, pack) { - // Takes a valid existing semver to add the package data to - if (!this.versions[this.cleanSemver(semver)]) { - logger.generic( - 3, - `Version.addPackageJSON() called with semver outside known range! ${semver}` - ); - return this; - } - - this.versions[this.cleanSemver(semver)].package = pack; - return this; - } - - /** - * @function getLatestVersion - * @desc Returns the full data of the `latest` version. As stored locally. - * This likely is not suited for using as any kind of package data. - */ - getLatestVersion() { - return this.versions[this.latestSemver]; - } - - /** - * @function getLatestVersionSemver - * @desc Returns the `semver` of the `latest` version. - */ - getLatestVersionSemver() { - return this.latestSemver; - } - - /** - * @function getLatestVersionTarball - * @desc Returns the `tarball` of the `latest` version. - */ - getLatestVersionTarball() { - return this.versions[this.latestSemver].tarball_url; - } - - /** - * @function getLatestVersionSha - * @desc Returns the `sha` of the `latest` version. - */ - getLatestVersionSha() { - return this.versions[this.latestSemver].sha; - } - - /** - * @function getLatestVersionPackageJSON - * @desc Returns the `package.json` data for the `latest` semver. - */ - getLatestVersionPackageJSON() { - return this.versions[this.latestSemver].package; - } - - /** - * @function buildFullVersions - * @desc Returns an object of the full version object for the versions provided. - */ - buildFullVersions() { - let obj = {}; - - // this.versions === this.versions.package | this.versions.sha | this.versions.tarball_url - for (const ver in this.versions) { - if (typeof this.versions[ver].package !== "object") { - // Ensure we are always inserting base package data, even if using - // the current latest as a fallback - obj[ver] = this.versions[this.latestSemver].package; - } else { - obj[ver] = this.versions[ver].package; - } - obj[ver].dist = { - tarball: this.versions[ver].tarball_url, - sha: this.versions[ver].sha, - }; - } - - // This should result in a hashmap of values by the versions of a package - // where each object holds the full versions package.json with an added dist - // key that contains the tarball url and sha - - // This is likely where in the future we want to use Joi to validate our object - return obj; - } -} - -module.exports = PackageObject; diff --git a/src/controllers/postPackages.js b/src/controllers/postPackages.js index b80b43cc..da8a9099 100644 --- a/src/controllers/postPackages.js +++ b/src/controllers/postPackages.js @@ -26,6 +26,12 @@ module.exports = { }, async postReturnHTTP(req, res, context, obj) { // Return to user before wbehook call, so user doesn't wait on it + + if (!obj.webhook || !obj.featureDetection) { + // Seems the `logic` function didn't execute successfully. Lets exit + return; + } + await context.webhook.alertPublishPackage( obj.webhook.pack, obj.webhook.user @@ -141,6 +147,7 @@ module.exports = { return sso .notOk() .addContent(newPack) + .addMessage(newPack.content) // This is where we trust the output .addCalls("auth.verifyAuth", user) .addCalls("vcs.ownership", gitowner) .addCalls("vcs.newPackageData", newPack); diff --git a/tests/unit/PackageObject.test.js b/tests/unit/PackageObject.test.js deleted file mode 100644 index d58ee529..00000000 --- a/tests/unit/PackageObject.test.js +++ /dev/null @@ -1,54 +0,0 @@ -const PackageObject = require("../../src/PackageObject.js"); - -describe("Building Objects with PackageObject Return as Expected", () => { - test("Formal Usage", () => { - const obj = new PackageObject().setName("hello"); - - expect(obj.name).toBe("hello"); - }); - - test("Extracting owner", () => { - let obj = new PackageObject() - .setName("hello") - .setOwnerRepo("pulsar/hello") - .setDownloads(100) - .setStargazers(200) - .setReadme("## Hello this is a readme!") - .setRepositoryURL("https://github.com/pulsar-edit/hello") - .setRepositoryType("git"); - - expect(obj.owner).toBe("pulsar-edit"); - }); - - test("Adding multiple versions", () => { - let obj = new PackageObject() - .setName("hello") - .setOwnerRepo("pulsar/hello") - .setDownloads(100) - .setStargazers(200) - .setReadme("## Hello this is a readme!") - .setRepositoryURL("https://github.com/pulsar-edit/hello") - .setRepositoryType("git"); - - obj.Version.addSemver("v1.101.0-beta") - .addTarball("v1.101.0-beta", "https://nowhere.com") - .addSha("v1.101.0-beta", "123") - .addSemver("v1.101.1-beta") - .addTarball("v1.101.1-beta", "https://nowhere.1.com") - .addSha("v1.101.1-beta", "1234"); - - expect(obj.Version.getLatestVersionSemver()).toBe("1.101.1-beta"); - }); - - test("Adding Multiple Complicated Versions", () => { - let obj = new PackageObject(); - - obj.Version.addSemver("v1.101.0-beta"); - obj.Version.addSemver("v2.101.0-beta"); - obj.Version.addSemver("3.444.0"); - obj.Version.addSemver("v0.0.1-alpha"); - obj.Version.addSemver("v3.444.1-beta"); - - expect(obj.Version.getLatestVersionSemver()).toBe("3.444.1-beta"); - }); -}); From e39b06c65f0f8fe1bd8fdc98ddd439aaf480b218 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Sun, 28 Jan 2024 22:14:06 -0800 Subject: [PATCH 4/5] Update matchers to work with different subdomains --- tests/full/fixtures/a-pulsar-package/match.js | 2 +- tests/full/fixtures/b-pulsar-package/match.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/full/fixtures/a-pulsar-package/match.js b/tests/full/fixtures/a-pulsar-package/match.js index bdb2697f..1719b62d 100644 --- a/tests/full/fixtures/a-pulsar-package/match.js +++ b/tests/full/fixtures/a-pulsar-package/match.js @@ -19,7 +19,7 @@ module.exports = { versions: { "1.0.0": { dist: { - tarball: "https://api.pulsar-edit.dev/api/packages/a-pulsar-package/versions/1.0.0/tarball" + tarball: expect.stringContaining("/api/packages/a-pulsar-package/versions/1.0.0/tarball") }, main: "./lib/main.js", name: "a-pulsar-package", diff --git a/tests/full/fixtures/b-pulsar-package/match.js b/tests/full/fixtures/b-pulsar-package/match.js index f6f4b635..eb2eb3e5 100644 --- a/tests/full/fixtures/b-pulsar-package/match.js +++ b/tests/full/fixtures/b-pulsar-package/match.js @@ -19,7 +19,7 @@ module.exports = { versions: { "2.0.0": { dist: { - tarball: "https://api.pulsar-edit.dev/api/packages/b-pulsar-package/versions/2.0.0/tarball" + tarball: expect.stringContaining("api/packages/b-pulsar-package/versions/2.0.0/tarball") }, main: "./lib/main.js", name: "b-pulsar-package", @@ -30,7 +30,7 @@ module.exports = { }, "1.0.0": { dist: { - tarball: "https://api.pulsar-edit.dev/api/packages/b-pulsar-package/versions/1.0.0/tarball" + tarball: expect.stringContaining("api/packages/b-pulsar-package/versions/1.0.0/tarball") }, //main: "./lib/main.js", // Since building the package.json of v1.0.0 is an absent build, we From 186494240307225f1a2db6ffb7cd0815db5bdb08 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Sun, 28 Jan 2024 22:49:41 -0800 Subject: [PATCH 5/5] Update `eslintrc` --- .eslintrc.js | 1 + tests/full/fixtures/b-pulsar-package/match.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index f3673a63..9d682925 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,6 +30,7 @@ module.exports = { expect: "readonly", describe: "readonly", beforeAll: "readonly", + beforeEach: "readonly", afterEach: "readonly", afterAll: "readonly", process: "writeable", diff --git a/tests/full/fixtures/b-pulsar-package/match.js b/tests/full/fixtures/b-pulsar-package/match.js index eb2eb3e5..e2847151 100644 --- a/tests/full/fixtures/b-pulsar-package/match.js +++ b/tests/full/fixtures/b-pulsar-package/match.js @@ -19,7 +19,7 @@ module.exports = { versions: { "2.0.0": { dist: { - tarball: expect.stringContaining("api/packages/b-pulsar-package/versions/2.0.0/tarball") + tarball: expect.stringContaining("/api/packages/b-pulsar-package/versions/2.0.0/tarball") }, main: "./lib/main.js", name: "b-pulsar-package", @@ -30,7 +30,7 @@ module.exports = { }, "1.0.0": { dist: { - tarball: expect.stringContaining("api/packages/b-pulsar-package/versions/1.0.0/tarball") + tarball: expect.stringContaining("/api/packages/b-pulsar-package/versions/1.0.0/tarball") }, //main: "./lib/main.js", // Since building the package.json of v1.0.0 is an absent build, we