Skip to content

Commit

Permalink
test: add tests for netlify functions (#2493)
Browse files Browse the repository at this point in the history
Co-authored-by: Ansh Goyal <[email protected]>%0ACo-authored-by: asyncapi-bot <[email protected]>
  • Loading branch information
Shurtu-gal and asyncapi-bot authored Sep 13, 2024
1 parent 9e4e1b6 commit 9f6308c
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 5 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/netlify-edge-functions-test.yml
Original file line number Diff line number Diff line change
@@ -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

8 changes: 5 additions & 3 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -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.*'],
};
6 changes: 4 additions & 2 deletions netlify/edge-functions/serve-definitions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Context } from "netlify:edge";
// 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");
const NR_API_KEY = Deno.env.get("NR_API_KEY");
Expand Down Expand Up @@ -69,7 +70,8 @@ export default async (request: Request, context: Context) => {

function buildRewrite(originalRequest: Request): (Request | null) {
const extractResult = SchemasRelatedRequestRegex.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;
}

Expand Down
218 changes: 218 additions & 0 deletions netlify/edge-functions/tests/serve-definitions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
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/[email protected]/mod.ts";
import { assertEquals } from "https://deno.land/[email protected]/assert/assert_equals.ts";

const metricURL = "https://metric-api.eu.newrelic.com/metric/v1";

const validRequests = [
{
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: "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:
"https://raw.githubusercontent.com/asyncapi/spec-json-schemas/master/schemas/2.5.0-without-$id.json",
},
];

const invalidRequests = [
{
requestURL: "https://asyncapi.com/definitions/asyncapi.yaml",
},
{
requestURL: "https://asyncapi.com/schema-store/2.4.0.JSON",
},
{
requestURL: "https://asyncapi.com/foobar",
},
{
requestURL: "https://asyncapi.com/",
},
];

const context = {
next: () => {},
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,
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 () => {
metricCalls = 0;

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(response.headers.get("Content-Type"), "application/schema+json");
assertEquals(body.url, entry.responseURL);

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) {
console.log("Testing: " + entry.requestURL);
const request = new Request(entry.requestURL, { method: "GET" });
const response = await serveDefinitions(request, context as Context);

assertEquals(response, undefined);
}

// No metrics should be sent for invalid requests
assertEquals(metricCalls, 0);

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);
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +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 netlify/**/*.test.ts",
"dev:storybook": "storybook dev -p 6006",
"build:storybook": "storybook build"
},
Expand Down

0 comments on commit 9f6308c

Please sign in to comment.