From 22746a952951184a12258fc8c7f3058dd3948d36 Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:56:32 +0530 Subject: [PATCH 1/8] feat: setup initial testing for netlify edge functions --- .../workflows/netlify-edge-functions-test.yml | 20 +++++ netlify/edge-functions/serve-definitions.ts | 2 +- .../tests/serve-definitions.test.ts | 85 +++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/netlify-edge-functions-test.yml create mode 100644 netlify/edge-functions/tests/serve-definitions.test.ts diff --git a/.github/workflows/netlify-edge-functions-test.yml b/.github/workflows/netlify-edge-functions-test.yml new file mode 100644 index 000000000000..83150c38be0a --- /dev/null +++ b/.github/workflows/netlify-edge-functions-test.yml @@ -0,0 +1,20 @@ +name: Run tests for netlify edge-functions + +on: + workflow_dispatch + +jobs: + netlify-tests: + strategy: + matrix: + deno-version: [1.30.0] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Deno + uses: denolib/setup-deno@v2 + with: + deno-version: ${{ matrix.deno-version }} + - name: Test with Deno + run: deno test --allow-env --trace-ops + \ No newline at end of file diff --git a/netlify/edge-functions/serve-definitions.ts b/netlify/edge-functions/serve-definitions.ts index 3197f584478c..d688c6e8b9c4 100644 --- a/netlify/edge-functions/serve-definitions.ts +++ b/netlify/edge-functions/serve-definitions.ts @@ -1,4 +1,4 @@ -import type { Context } from "netlify:edge"; +import type { Context } from "https://edge-bootstrap.netlify.app/v1/index.ts"; const GITHUB_TOKEN = Deno.env.get("GITHUB_TOKEN_NR"); const NR_API_KEY = Deno.env.get("NR_API_KEY"); diff --git a/netlify/edge-functions/tests/serve-definitions.test.ts b/netlify/edge-functions/tests/serve-definitions.test.ts new file mode 100644 index 000000000000..c2a4e43986db --- /dev/null +++ b/netlify/edge-functions/tests/serve-definitions.test.ts @@ -0,0 +1,85 @@ +import serveDefinitions from "../serve-definitions.ts"; +import { Context } from "https://edge-bootstrap.netlify.app/v1/index.ts"; +import * as mf from "https://deno.land/x/mock_fetch@0.3.0/mod.ts"; +import { assertEquals } from "https://deno.land/std@0.208.0/assert/assert_equals.ts"; + +const validRequests = [ + { + requestURL: "http://localhost:8888/definitions/2.4.0/info.json", + responseURL: + "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/definitions/2.4.0/info.json", + }, + { + requestURL: "http://localhost:8888/schema-store/2.5.0-without-$id.json", + responseURL: + "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.5.0-without-$id.json", + }, +]; + +const invalidRequests = [ + { + requestURL: "http://localhost:8888/definitions/asyncapi.yaml", + }, + { + requestURL: "http://localhost:8888/schema-store/2.4.0.JSON", + }, +]; + +const context = { + next: () => {}, + log: () => {}, +}; + +function setup() { + mf.install(); + + mf.mock("*", (req) => { + console.log(req.url); + + const body = { + url: req.url, + method: req.method, + headers: req.headers, + }; + + return new Response(JSON.stringify(body), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + }); +} + +Deno.test("serve-definitions test for validRequests", async () => { + setup(); + + for (const entry of validRequests) { + console.log("Testing: " + entry.requestURL); + + const request = new Request(entry.requestURL, { method: "GET" }); + const response = await serveDefinitions(request, context as Context); + const body = await response.json(); + + assertEquals(response.status, 200); + assertEquals(body.url, entry.responseURL); + + console.log("\n"); + } + + mf.uninstall(); +}); + +Deno.test("serve-definitions test for invalidRequests", async () => { + setup(); + + for (const entry of invalidRequests) { + console.log("Testing: " + entry.requestURL); + const request = new Request(entry.requestURL, { method: "GET" }); + const response = await serveDefinitions(request, context as Context); + + assertEquals(response, undefined); + } + + mf.uninstall(); +}); From aabef87bdbbe3df31018214f1b9a0324b8897e54 Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Fri, 22 Dec 2023 02:37:28 +0530 Subject: [PATCH 2/8] feat: add tests for metric calls * change url to asyncapi.com * add test script to package.json --- netlify/edge-functions/serve-definitions.ts | 4 ++- .../tests/serve-definitions.test.ts | 28 ++++++++++++++++--- package.json | 1 + 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/netlify/edge-functions/serve-definitions.ts b/netlify/edge-functions/serve-definitions.ts index d688c6e8b9c4..f1afa846491c 100644 --- a/netlify/edge-functions/serve-definitions.ts +++ b/netlify/edge-functions/serve-definitions.ts @@ -69,7 +69,9 @@ export default async (request: Request, context: Context) => { function buildRewrite(originalRequest: Request): (Request | null) { const extractResult = legitimateRequestRegex.exec(new URL(originalRequest.url).pathname); - if (extractResult === null) { + + // No need to rewrite the request if it's not a legitimate request for a definition file + if (extractResult === null || extractResult.length < 2 || !extractResult[2]) { return null; } diff --git a/netlify/edge-functions/tests/serve-definitions.test.ts b/netlify/edge-functions/tests/serve-definitions.test.ts index c2a4e43986db..60365842056f 100644 --- a/netlify/edge-functions/tests/serve-definitions.test.ts +++ b/netlify/edge-functions/tests/serve-definitions.test.ts @@ -3,14 +3,16 @@ import { Context } from "https://edge-bootstrap.netlify.app/v1/index.ts"; import * as mf from "https://deno.land/x/mock_fetch@0.3.0/mod.ts"; import { assertEquals } from "https://deno.land/std@0.208.0/assert/assert_equals.ts"; +const metricURL = "https://metric-api.eu.newrelic.com/metric/v1"; + const validRequests = [ { - requestURL: "http://localhost:8888/definitions/2.4.0/info.json", + requestURL: "https://asyncapi.com/definitions/2.4.0/info.json", responseURL: "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/definitions/2.4.0/info.json", }, { - requestURL: "http://localhost:8888/schema-store/2.5.0-without-$id.json", + requestURL: "https://asyncapi.com/schema-store/2.5.0-without-$id.json", responseURL: "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.5.0-without-$id.json", }, @@ -18,11 +20,14 @@ const validRequests = [ const invalidRequests = [ { - requestURL: "http://localhost:8888/definitions/asyncapi.yaml", + requestURL: "https://asyncapi.com/definitions/asyncapi.yaml", }, { - requestURL: "http://localhost:8888/schema-store/2.4.0.JSON", + requestURL: "https://asyncapi.com/schema-store/2.4.0.JSON", }, + { + requestURL: "https://asyncapi.com/foobar", + } ]; const context = { @@ -30,11 +35,17 @@ const context = { log: () => {}, }; +let metricCalls = 0; + function setup() { mf.install(); mf.mock("*", (req) => { console.log(req.url); + + if( req.url === metricURL ) { + metricCalls++; + } const body = { url: req.url, @@ -52,6 +63,8 @@ function setup() { } Deno.test("serve-definitions test for validRequests", async () => { + metricCalls = 0; + setup(); for (const entry of validRequests) { @@ -67,10 +80,14 @@ Deno.test("serve-definitions test for validRequests", async () => { console.log("\n"); } + assertEquals(metricCalls, validRequests.length); + mf.uninstall(); }); Deno.test("serve-definitions test for invalidRequests", async () => { + metricCalls = 0; + setup(); for (const entry of invalidRequests) { @@ -81,5 +98,8 @@ Deno.test("serve-definitions test for invalidRequests", async () => { assertEquals(response, undefined); } + // No metrics should be sent for invalid requests + assertEquals(metricCalls, 0); + mf.uninstall(); }); diff --git a/package.json b/package.json index 4143d2049dc4..b43bff6db49a 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "generate:videos": "node scripts/build-newsroom-videos.js", "generate:tools": "node scripts/build-tools.js", "test": "npx cypress run --component", + "test:netlify": "deno test --allow-env --trace-ops", "cy:open": "cypress open", "cy:run": "cypress run" }, From 32c06a6fd52cb52aa0a00af0c7698001fbba6ec3 Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:03:42 +0530 Subject: [PATCH 3/8] chore: add some more tests * assert returned content type --- netlify/edge-functions/serve-definitions.ts | 1 + .../tests/serve-definitions.test.ts | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/netlify/edge-functions/serve-definitions.ts b/netlify/edge-functions/serve-definitions.ts index f1afa846491c..0bde8bec5ed0 100644 --- a/netlify/edge-functions/serve-definitions.ts +++ b/netlify/edge-functions/serve-definitions.ts @@ -1,3 +1,4 @@ +// Changes to URL from 'netlify:edge' because we don't have package aliasing setup in our workflow. import type { Context } from "https://edge-bootstrap.netlify.app/v1/index.ts"; const GITHUB_TOKEN = Deno.env.get("GITHUB_TOKEN_NR"); diff --git a/netlify/edge-functions/tests/serve-definitions.test.ts b/netlify/edge-functions/tests/serve-definitions.test.ts index 60365842056f..04333c2b7025 100644 --- a/netlify/edge-functions/tests/serve-definitions.test.ts +++ b/netlify/edge-functions/tests/serve-definitions.test.ts @@ -11,6 +11,16 @@ const validRequests = [ responseURL: "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/definitions/2.4.0/info.json", }, + { + requestURL: "https://asyncapi.com/definitions/2.4.0.json", + responseURL: + "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.4.0.json", + }, + { + requestURL: "https://asyncapi.com/schema-store/2.5.0/operation.json", + responseURL: + "https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/definitions/2.5.0/operation.json", + }, { requestURL: "https://asyncapi.com/schema-store/2.5.0-without-$id.json", responseURL: @@ -27,7 +37,10 @@ const invalidRequests = [ }, { requestURL: "https://asyncapi.com/foobar", - } + }, + { + requestURL: "https://asyncapi.com/", + }, ]; const context = { @@ -75,6 +88,7 @@ Deno.test("serve-definitions test for validRequests", async () => { const body = await response.json(); assertEquals(response.status, 200); + assertEquals(response.headers.get("Content-Type"), "application/schema+json"); assertEquals(body.url, entry.responseURL); console.log("\n"); From 88f0a72ad7eb12d289c81eb0ed8de841a40fd5af Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Thu, 23 May 2024 01:00:03 +0530 Subject: [PATCH 4/8] chore: add more tests --- .../tests/serve-definitions.test.ts | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/netlify/edge-functions/tests/serve-definitions.test.ts b/netlify/edge-functions/tests/serve-definitions.test.ts index 04333c2b7025..ea9bbdf76215 100644 --- a/netlify/edge-functions/tests/serve-definitions.test.ts +++ b/netlify/edge-functions/tests/serve-definitions.test.ts @@ -56,7 +56,7 @@ function setup() { mf.mock("*", (req) => { console.log(req.url); - if( req.url === metricURL ) { + if (req.url === metricURL) { metricCalls++; } @@ -117,3 +117,102 @@ Deno.test("serve-definitions test for invalidRequests", async () => { mf.uninstall(); }); + +Deno.test("serve-definitions test for various response statuses", async () => { + const testCases = [ + { requestURL: "https://asyncapi.com/definitions/2.4.0/info.json", status: 200, mockParam: "GET@https://asyncapi.com/definitions/2.4.0/info.json" }, + { requestURL: "https://asyncapi.com/definitions/2.4.0/info.json", status: 304, mockParam: "GET@https://asyncapi.com/definitions/2.4.0/info.json" }, + { requestURL: "https://asyncapi.com/definitions/2.4.0/info.json", status: 404, mockParam: "GET@https://asyncapi.com/definitions/2.4.0/info.json" }, + { requestURL: "https://asyncapi.com/definitions/2.4.0/info.json", status: 500, mockParam: "GET@https://asyncapi.com/definitions/2.4.0/info.json" }, + ]; + + for (const { requestURL, status, mockParam } of testCases) { + console.log("Testing: " + requestURL); + + mf.install(); + + mf.mock("*", () => { + return new Response(status === 200 ? JSON.stringify({ url: requestURL }) : null, { + status, + }); + }); + + const request = new Request(requestURL, { method: "GET" }); + const response = await serveDefinitions(request, context as Context); + + if (status === 200) { + const body = response.body ? await response.json() : null; + assertEquals(response.status, status); + assertEquals(body?.url, requestURL); + } else { + assertEquals(response.status, status); + } + + if (status === 200 || status === 304) { + metricCalls++; + } + + + console.log("\n"); + mf.uninstall(); + } + + assertEquals(metricCalls, testCases.filter((testCase) => testCase.status === 200 || testCase.status === 304).length); +}); + +Deno.test("serve-definitions test for schema-unrelated requests", async () => { + setup(); + + const schemaUnrelatedRequests = [ + "https://asyncapi.com/definitions/asyncapi.yaml", + "https://asyncapi.com/schema-store/2.4.0.JSON", + "https://asyncapi.com/foobar", + "https://asyncapi.com/", + ]; + + for (const requestURL of schemaUnrelatedRequests) { + console.log("Testing: " + requestURL); + const request = new Request(requestURL, { method: "GET" }); + const response = await serveDefinitions(request, context as Context); + + assertEquals(response, undefined); + } + + mf.uninstall(); +}); + +Deno.test("serve-definitions test for schema-related non-JSON requests", async () => { + setup(); + metricCalls = 0; + + const schemaRelatedNonJsonRequests = [ + "https://asyncapi.com/schema-store/2.5.0-without-$id", + ]; + + for (const requestURL of schemaRelatedNonJsonRequests) { + const context = { + next: () => { + return new Response(JSON.stringify({ url: requestURL }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + }, + log: () => {}, + } + + console.log("Testing: " + requestURL); + const request = new Request(requestURL, { method: "GET" }); + const response = await serveDefinitions(request, context as unknown as Context); + const body = response?.body ? await response.json() : null; + + assertEquals(response?.status, 200); + assertEquals(body.url, requestURL); + assertEquals(response.headers.get("Content-Type"), "application/json"); // Default content type for non-JSON requests + } + + mf.uninstall(); + + assertEquals(metricCalls, 0); +}); From b1d4ed1c362e54dfe1d12743a80e1afb512c53cd Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Fri, 31 May 2024 19:34:00 +0530 Subject: [PATCH 5/8] chore: remove comma --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02f1892bec8d..99dfd3ae1c0c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "generate:dashboard": "node scripts/dashboard/build-dashboard.js", "generate:videos": "node scripts/build-newsroom-videos.js", "generate:tools": "node scripts/build-tools.js", - "test:netlify": "deno test --allow-env --trace-ops", + "test:netlify": "deno test --allow-env --trace-ops" }, "repository": { "type": "git", From 15e9bc98281280426b96eae2c876f8839ee2a18e Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:10:53 +0000 Subject: [PATCH 6/8] chore: missing comma --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 343ef3e4042e..b54dfc5bf391 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "generate:dashboard": "node scripts/dashboard/build-dashboard.js", "generate:videos": "node scripts/build-newsroom-videos.js", "generate:tools": "node scripts/build-tools.js", - "test:netlify": "deno test --allow-env --trace-ops" + "test:netlify": "deno test --allow-env --trace-ops", "dev:storybook": "storybook dev -p 6006", "build:storybook": "storybook build" }, From fa8589b5dde6f7210468bacab69cf8a7ac264430 Mon Sep 17 00:00:00 2001 From: Ashish Padhy <100484401+Shurtu-gal@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:21:27 +0000 Subject: [PATCH 7/8] fix: make tests localised --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b54dfc5bf391..42a565604c57 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "npm run build-scripts && next -p 3000", "build": "npm run build-scripts && next build", - "test": "jest --passWithNoTests", + "test": "jest --passWithNoTests tests/**/*.test.js", "build:pages": "node scripts/build-pages.js && npm run format:mdx", "build:posts": "node scripts/index.js", "build-scripts": "npm run build:pages && npm run lint:mdx && npm run build:posts", @@ -22,7 +22,7 @@ "generate:dashboard": "node scripts/dashboard/build-dashboard.js", "generate:videos": "node scripts/build-newsroom-videos.js", "generate:tools": "node scripts/build-tools.js", - "test:netlify": "deno test --allow-env --trace-ops", + "test:netlify": "deno test --allow-env --trace-ops netlify/**/*.test.ts", "dev:storybook": "storybook dev -p 6006", "build:storybook": "storybook build" }, From 2dadccfcc0ac3f957bf1bed41370fea6471b28d5 Mon Sep 17 00:00:00 2001 From: Ansh Goyal Date: Fri, 13 Sep 2024 10:03:21 +0000 Subject: [PATCH 8/8] fix tests on windows --- jest.config.js | 8 +++++--- package.json | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/jest.config.js b/jest.config.js index 496a9fddf929..25c7865434ed 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,7 @@ module.exports = { - verbose: true, // display individual test results with the test suite hierarchy - collectCoverage: true, // collect test coverage information\ - collectCoverageFrom: ['scripts/**/*.js'] + verbose: true, + collectCoverage: true, + collectCoverageFrom: ['scripts/**/*.js'], + // To disallow netlify edge function tests from running + testMatch: ['**/tests/**/*.test.*', '!**/netlify/**/*.test.*'], }; diff --git a/package.json b/package.json index 4d8ad9596bdf..92a955ced588 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "npm run build-scripts && next dev", "build": "npm run build-scripts && next build", - "test": "jest --passWithNoTests tests/**/*.test.js", + "test": "jest --passWithNoTests", "build:pages": "node scripts/build-pages.js && npm run format:mdx", "build:posts": "node scripts/index.js", "build-scripts": "npm run build:pages && npm run lint:mdx && npm run build:posts",