diff --git a/.github/workflows/test-nodejs-apis.yml b/.github/workflows/test-nodejs-apis.yml new file mode 100644 index 00000000..97729214 --- /dev/null +++ b/.github/workflows/test-nodejs-apis.yml @@ -0,0 +1,39 @@ +name: Test Node.js APIs + +on: + schedule: + # Run every day at 3:30 AM UTC + - cron: '30 3 * * *' + +jobs: + test-nodejs-apis: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stage' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} + + - name: Install dependencies + run: yarn install + + - name: Install Docker Compose + run: | + curl -L "https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + docker-compose --version + + - name: Run E2E tests + run: yarn test:nodejs-apis + + - name: Process README.md and Commit + run: | + git config --global user.email "bundler@azion.com" + git config --global user.name "Azion Bundler Reports" + git add ./docs/nodejs-apis.md + git commit -m "chore: update reports node.js apis" --no-verify || echo "No changes to commit." + git push --force + env: + GH_TOKEN: ${{ secrets.CUSTOM_GITHUB_TOKEN }} diff --git a/.gitmodules b/.gitmodules index 0d25b045..47267ca6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "examples"] path = examples - url = https://github.com/aziontech/vulcan-examples + url = https://github.com/aziontech/bundler-examples diff --git a/Dockerfile b/Dockerfile index 7172a756..829549bc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,4 +10,4 @@ RUN apk add ruby-dev RUN gem install jekyll bundler -COPY . /vulcan +COPY . /bundler diff --git a/README.md b/README.md index d417e2b0..526f01b5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ One of the key highlights of Azion Bundler is its ability to establish an intuit ## Supported -E2E tests run daily in the [Vulcan Examples](https://github.com/aziontech/vulcan-examples/tree/main/examples) to ensure that the presets and frameworks continue to work correctly. +E2E tests run daily in the [Bundler Examples](https://github.com/aziontech/bundler-examples/tree/main/examples) to ensure that the presets and frameworks continue to work correctly. Table: | Test | Status | @@ -227,6 +227,12 @@ To use wasm presets you need to install the necessary tools to build your code: - Emscripten: [emsdk](https://emscripten.org/docs/getting_started/downloads.html); - Rust/Wasm: [wasm-bindgen-cli](https://crates.io/crates/wasm-bindgen-cli) +## Node.js Support and Report + +The compatibility between Azion Runtime and Node.js is an ongoing task, but a set of Node Runtime APIs are listed and compatible with Azion Runtime. + +- [Node.js APIs support](docs/nodejs-apis.md) + ## Contributing Check the [Contributing doc](CONTRIBUTING.md). diff --git a/docker-compose.yml b/docker-compose.yml index e2cf7f23..33b9ee01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,6 @@ services: ports: - "3000-3040:3000-3040" volumes: - - ./:/vulcan/ + - ./:/bundler/ working_dir: / tty: true diff --git a/docs/nodejs-apis.md b/docs/nodejs-apis.md new file mode 100644 index 00000000..3e0e29e4 --- /dev/null +++ b/docs/nodejs-apis.md @@ -0,0 +1,58 @@ +### Node.js APIs + +Azion Bundler provides a set of APIs to help you build and test your Node.js projects. The following sections detail the available APIs and how to use them. + +#### Example usage + +See more: [Buffer Example](https://github.com/aziontech/bundler-examples/tree/main/examples/runtime-apis/nodejs/buffer) + +```javascript +import { Buffer } from "node:buffer"; + +const main = async (event) => { + const helloBuffer = Buffer.from("Hello Edge!", "utf8"); + console.log(helloBuffer.toString("hex")); + // 48656c6c6f204564676521 + console.log(helloBuffer.toString("base64")); + // SGVsbG8gRWRnZSE= + + helloBuffer.write("World", 6, 5, "utf8"); + console.log(helloBuffer.toString()); + // Hello World! + return new Response(helloBuffer.toString(), { status: 200 }); +}; + +export default main; + +``` + +#### Support report + +Tests run daily in the [Bundler Examples](https://github.com/aziontech/bundler-examples/tree/main/examples/runtime-apis/nodejs). + +Table: +| Test | Status | +| -------------- | ------ | +| Timers | ✅ | +| Http | ✅ | +| Async Hooks | ✅ | +| String Decoder | ✅ | +| Url | ✅ | +| Crypto | ✅ | +| Process | ✅ | +| Util | ✅ | +| Vm | ✅ | +| Zlib | ✅ | +| Os | ✅ | +| Buffer | ✅ | +| Module | ✅ | +| Stream | ✅ | +| Fs | ✅ | +| Events | ✅ | +| Path | ✅ | + +Last test run date: 10/30/24 11:16:36 AM +#### Docs support + +See support for the Node.js APIs in the [https://www.azion.com/en/documentation/products/azion-edge-runtime/compatibility/node/](https://www.azion.com/en/documentation/products/azion-edge-runtime/compatibility/node/) + diff --git a/examples b/examples index 27b154c9..a6fb4608 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 27b154c98184ed89e6aaf3f2c8fce0d12b59caf0 +Subproject commit a6fb46089efb71b820a32966351c699b78726250 diff --git a/package.json b/package.json index ae982791..32a9daba 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "start": "node lib/main.js", "task:aliases": "node tasks/sync-aliases.js", "task:reports": "node tasks/process-reports-e2e.js", + "task:reports-nodejs-apis": "node tasks/process-reports-nodejs-apis.js", "lint": "eslint .", "lint:fix": "eslint --fix .", "format": "prettier --write .", @@ -25,6 +26,7 @@ "e2e:stop": "tests/scripts/stop-e2e-env.sh", "e2e:destroy": "tests/scripts/destroy-e2e-env.sh", "test:e2e": "yarn e2e:start && jest --maxWorkers 1 tests/e2e/ --json --outputFile e2e_results.json && yarn e2e:stop; yarn task:reports", + "test:nodejs-apis": "yarn e2e:start && jest --maxWorkers 1 tests/nodejs-apis/ --json --outputFile nodejs_apis_results.json && yarn e2e:stop && yarn task:reports-nodejs-apis", "prepare": "husky install", "submodule:update": "git submodule update --init --recursive && git submodule foreach git pull origin main" }, diff --git a/tasks/process-reports-nodejs-apis.js b/tasks/process-reports-nodejs-apis.js new file mode 100755 index 00000000..791b6190 --- /dev/null +++ b/tasks/process-reports-nodejs-apis.js @@ -0,0 +1,100 @@ +import fs from 'fs'; +import { feedback } from '#utils'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { markdownTable } from 'markdown-table'; + +/** + * + */ +function processReports() { + try { + feedback.interactive.await('Processing Node.js APIs report...'); + + // Read the JSON file + const data = fs.readFileSync('nodejs_apis_results.json'); + const results = JSON.parse(data); + + // Process the test results + results.testResults = results.testResults.map((test) => { + // Remove the path from the test name + const testName = test.name + .split('/') + .pop() + .replace('.test.js', '') + .replace(/-/g, ' '); + + // Transform the test name into a more readable format + const readableTestName = testName + .split(' ') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + + // Check if all tests in the suite passed + const suitePassed = test.assertionResults.every( + (result) => result.status === 'passed', + ); + + return { + name: readableTestName, + passed: suitePassed, + }; + }); + + // Update the overall test status + results.passes = results.testResults.every((test) => test.passed); + + // Create a new object with the testResults and passes properties + const newResults = { + testResults: results.testResults, + passes: results.passes, + }; + + // Write the new object back to the JSON file + fs.writeFileSync( + 'nodejs_apis_results.json', + JSON.stringify(newResults, null, 2), + ); + + // Create the Markdown table + const table = [ + ['Test', 'Status'], + ...newResults.testResults.map((test) => [ + test.name, + test.passed ? '✅' : '⚠️', + ]), + ]; + + // Write the Markdown table to the README.md file + const readme = fs.readFileSync('./docs/nodejs-apis.md', 'utf8'); + const newReadme = readme.replace( + /(Table:\n)(.*?)(\n#### Docs support)/s, + (match, p1, p2, p3) => { + const dateOptions = { + day: '2-digit', + month: '2-digit', + year: '2-digit', + }; + const timeOptions = { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }; + const newDate = `${new Date().toLocaleDateString( + 'en-US', + dateOptions, + )} ${new Date().toLocaleTimeString('en-US', timeOptions)}`; + return `${p1}${markdownTable( + table, + )}\n\nLast test run date: ${newDate}${p3}`; + }, + ); + fs.writeFileSync('./docs/nodejs-apis.md', newReadme); + + feedback.interactive.success('Report processed successfully.'); + } catch (error) { + console.error('An error occurred:', error); + } +} + +// Call the function +processReports(); diff --git a/tests/nodejs-apis/async-hooks.test.js b/tests/nodejs-apis/async-hooks.test.js new file mode 100644 index 00000000..83b6e8e6 --- /dev/null +++ b/tests/nodejs-apis/async-hooks.test.js @@ -0,0 +1,33 @@ +import supertest from 'supertest'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/async-hooks'; + +describe('Node.js APIs - async_hooks', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/').expect(200); + expect(response.text).toContain('ok'); + }); +}); diff --git a/tests/nodejs-apis/buffer.test.js b/tests/nodejs-apis/buffer.test.js new file mode 100644 index 00000000..ef45b1fe --- /dev/null +++ b/tests/nodejs-apis/buffer.test.js @@ -0,0 +1,34 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/buffer'; + +describe('Node.js APIs - buffer', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/').expect(200); + expect(response.text).toContain('Hello World'); + }); +}); diff --git a/tests/nodejs-apis/crypto.test.js b/tests/nodejs-apis/crypto.test.js new file mode 100644 index 00000000..772d5b38 --- /dev/null +++ b/tests/nodejs-apis/crypto.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/crypto'; + +describe('Node.js APIs - crypto', () => { + let request; + const uuidRegex = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/').expect(200); + expect(response.text).toMatch(uuidRegex); + }); +}); diff --git a/tests/nodejs-apis/events.test.js b/tests/nodejs-apis/events.test.js new file mode 100644 index 00000000..bd5926a4 --- /dev/null +++ b/tests/nodejs-apis/events.test.js @@ -0,0 +1,34 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/events'; + +describe('Node.js APIs - events', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/').expect(200); + expect(response.text).toEqual('Event emitted'); + }); +}); diff --git a/tests/nodejs-apis/fs.test.js b/tests/nodejs-apis/fs.test.js new file mode 100644 index 00000000..1f252d1a --- /dev/null +++ b/tests/nodejs-apis/fs.test.js @@ -0,0 +1,34 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/fs'; + +describe('Node.js APIs - fs', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/').expect(200); + expect(response.text).toEqual('Done!'); + }); +}); diff --git a/tests/nodejs-apis/http.test.js b/tests/nodejs-apis/http.test.js new file mode 100644 index 00000000..f4bc1b5c --- /dev/null +++ b/tests/nodejs-apis/http.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/http'; + +describe('Node.js APIs - http', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual(expect.stringContaining('id')); + }); +}); diff --git a/tests/nodejs-apis/module.test.js b/tests/nodejs-apis/module.test.js new file mode 100644 index 00000000..79d0491d --- /dev/null +++ b/tests/nodejs-apis/module.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/module'; + +describe('Node.js APIs - module', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('Done'); + }); +}); diff --git a/tests/nodejs-apis/os.test.js b/tests/nodejs-apis/os.test.js new file mode 100644 index 00000000..de093931 --- /dev/null +++ b/tests/nodejs-apis/os.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/os'; + +describe('Node.js APIs - os', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual(expect.stringContaining('platform: ')); + }); +}); diff --git a/tests/nodejs-apis/path.test.js b/tests/nodejs-apis/path.test.js new file mode 100644 index 00000000..90e0424a --- /dev/null +++ b/tests/nodejs-apis/path.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/path'; + +describe('Node.js APIs - path', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual(expect.stringContaining('Path: ')); + }); +}); diff --git a/tests/nodejs-apis/process.test.js b/tests/nodejs-apis/process.test.js new file mode 100644 index 00000000..c261c685 --- /dev/null +++ b/tests/nodejs-apis/process.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/process'; + +describe('Node.js APIs - process', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual(expect.stringContaining('NODE_ENV: ')); + }); +}); diff --git a/tests/nodejs-apis/stream.test.js b/tests/nodejs-apis/stream.test.js new file mode 100644 index 00000000..e35a7ceb --- /dev/null +++ b/tests/nodejs-apis/stream.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/stream'; + +describe('Node.js APIs - stream', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('Done'); + }); +}); diff --git a/tests/nodejs-apis/string-decoder.test.js b/tests/nodejs-apis/string-decoder.test.js new file mode 100644 index 00000000..4b454f15 --- /dev/null +++ b/tests/nodejs-apis/string-decoder.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/string-decoder'; + +describe('Node.js APIs - string-decoder', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('¢'); + }); +}); diff --git a/tests/nodejs-apis/timers.test.js b/tests/nodejs-apis/timers.test.js new file mode 100644 index 00000000..d39edfcc --- /dev/null +++ b/tests/nodejs-apis/timers.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/timers'; + +describe('Node.js APIs - timers', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('Done!'); + }); +}); diff --git a/tests/nodejs-apis/url.test.js b/tests/nodejs-apis/url.test.js new file mode 100644 index 00000000..49a2ed9e --- /dev/null +++ b/tests/nodejs-apis/url.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/url'; + +describe('Node.js APIs - url', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('Done!'); + }); +}); diff --git a/tests/nodejs-apis/util.test.js b/tests/nodejs-apis/util.test.js new file mode 100644 index 00000000..073dca25 --- /dev/null +++ b/tests/nodejs-apis/util.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/util'; + +describe('Node.js APIs - util', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('Done!'); + }); +}); diff --git a/tests/nodejs-apis/vm.test.js b/tests/nodejs-apis/vm.test.js new file mode 100644 index 00000000..4be13823 --- /dev/null +++ b/tests/nodejs-apis/vm.test.js @@ -0,0 +1,36 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/vm'; + +describe('Node.js APIs - vm', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/text/); + expect(response.text).toEqual('Done!'); + }); +}); diff --git a/tests/nodejs-apis/zlib.test.js b/tests/nodejs-apis/zlib.test.js new file mode 100644 index 00000000..e5738357 --- /dev/null +++ b/tests/nodejs-apis/zlib.test.js @@ -0,0 +1,45 @@ +import supertest from 'supertest'; +import { expect } from '@jest/globals'; +import zlib from 'node:zlib'; +import projectInitializer from '../utils/project-initializer.js'; +import projectStop from '../utils/project-stop.js'; +import { getContainerPort } from '../utils/docker-env-actions.js'; + +// timeout in minutes +const TIMEOUT = 1 * 60 * 3000; + +let serverPort; +let localhostBaseUrl; +const EXAMPLE_PATH = '/examples/runtime-apis/nodejs/zlib'; + +describe('Node.js APIs - zlib', () => { + let request; + + beforeAll(async () => { + serverPort = getContainerPort(); + localhostBaseUrl = `http://0.0.0.0:${serverPort}`; + + request = supertest(localhostBaseUrl); + + await projectInitializer(EXAMPLE_PATH, 'javascript', serverPort, false); + }, TIMEOUT); + + afterAll(async () => { + await projectStop(serverPort, EXAMPLE_PATH.replace('/examples/', '')); + }, TIMEOUT); + + test('should request the "/" route and get a 200 status code', async () => { + const response = await request.get('/'); + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toMatch(/octet-stream/); + expect(response.headers['transfer-encoding']).toEqual('chunked'); + expect(response.body.toString()).toEqual( + 'H4sIAAAAAAAAA/NIzcnJ11EIzy/KSVEEANDDSuwNAAAA', + ); + // decompressed + const decodeBase64 = Buffer.from(response.body.toString(), 'base64'); + const decompressedBody = zlib.gunzipSync(decodeBase64).toString(); + expect(decompressedBody).toEqual('Hello, World!'); + // Hello, World! + }); +}); diff --git a/tests/scripts/setup-e2e-env.sh b/tests/scripts/setup-e2e-env.sh index 93fddc05..7f274b4e 100755 --- a/tests/scripts/setup-e2e-env.sh +++ b/tests/scripts/setup-e2e-env.sh @@ -18,10 +18,10 @@ log_with_color() { # isolate examples log_with_color "* STARTING ..." $GREEN -SENTINEL_FILE="/vulcan/.initialized.keep" +SENTINEL_FILE="/bundler/.initialized.keep" -# create vulcan temp to install in verdaccio -create_vulcan_temp() { +# create bundler temp to install in verdaccio +create_bundler_temp() { local target="$1" local folder="$2" rm -rf $folder @@ -34,32 +34,32 @@ create_vulcan_temp() { # TODO: improve this init check if test -f $SENTINEL_FILE; then log_with_color "Container already initialized!" $CYAN - log_with_color "* Vulcan version:" $GREEN + log_with_color "* Bundler version:" $GREEN npx --yes --registry=http://verdaccio:4873 edge-functions@latest --version else log_with_color "Container NOT initialized! Initializing container ..." $YELLOW # log_with_color "* Isolate examples" $GREEN - cp -r /vulcan/examples/examples /examples/ + cp -r /bundler/examples/examples /examples/ - # create vulcan temp to install - VULCAN_TEMP=vulcan-temp - create_vulcan_temp /vulcan $VULCAN_TEMP + # create bundler temp to install + BUNDLER_TEMP=bundler-temp + create_bundler_temp /bundler $BUNDLER_TEMP - # install vulcan temp locally - log_with_color "* Install vulcan" $GREEN - cd /$VULCAN_TEMP + # install bundler temp locally + log_with_color "* Install Bundler" $GREEN + cd /$BUNDLER_TEMP yarn # login in verdaccio registry log_with_color "* Login in verdaccio" $GREEN npx --yes npm-cli-login -u test -p 1234 -e test@domain.test -r http://verdaccio:4873 - # publish vulcan in verdaccio - log_with_color "* Publish vulcan in verdaccio" $GREEN + # publish bundler in verdaccio + log_with_color "* Publish Bundler in verdaccio" $GREEN npm publish --registry http://verdaccio:4873 npm info edge-functions --json --registry http://verdaccio:4873 - log_with_color "* Vulcan version:" $GREEN + log_with_color "* Bundler version:" $GREEN npx --yes --registry=http://verdaccio:4873 edge-functions@latest --version cd / diff --git a/tests/scripts/start-e2e-env.sh b/tests/scripts/start-e2e-env.sh index 37bfd519..82bfb575 100755 --- a/tests/scripts/start-e2e-env.sh +++ b/tests/scripts/start-e2e-env.sh @@ -2,4 +2,4 @@ docker-compose up -d && \ sleep 3 && \ - docker-compose exec -T test sh /vulcan/tests/scripts/setup-e2e-env.sh \ No newline at end of file + docker-compose exec -T test sh /bundler/tests/scripts/setup-e2e-env.sh \ No newline at end of file diff --git a/tests/utils/project-initializer.js b/tests/utils/project-initializer.js index 5b059ffd..0504a2f1 100644 --- a/tests/utils/project-initializer.js +++ b/tests/utils/project-initializer.js @@ -7,8 +7,8 @@ import { /** * Run actions to build and run project in docker container * @param {string} examplePath - project path in container - * @param {string} preset - vulcan preset to build - * @param {number} serverPort - port to use in vulcan server + * @param {string} preset - bundler preset to build + * @param {number} serverPort - port to use in bundler server * @param {boolean} installPkgs - dependencies need to be installed? * @param {string} url - url test container * @param {boolean} isFirewall - is firewall project @@ -22,7 +22,7 @@ async function projectInitializer( isFirewall = false, ) { const example = examplePath.replace('/examples/', ''); - const vulcanCmd = + const bundlerCmd = 'npx --yes --registry=http://verdaccio:4873 edge-functions@latest'; if (installPkgs) { @@ -32,20 +32,20 @@ async function projectInitializer( feedback.info(`[${example}] Building the project ...`); await execCommandInContainer( - `${vulcanCmd} build --preset ${preset} ${isFirewall ? '--firewall' : ''}`, + `${bundlerCmd} build --preset ${preset} ${isFirewall ? '--firewall' : ''}`, examplePath, ); - feedback.info(`[${example}] Starting vulcan local server ...`); + feedback.info(`[${example}] Starting Bundler local server ...`); await execCommandInContainer( - `${vulcanCmd} dev -p ${serverPort} ${isFirewall ? '--firewall' : ''}`, + `${bundlerCmd} dev -p ${serverPort} ${isFirewall ? '--firewall' : ''}`, examplePath, true, ); await waitForVulcanServer(`${url}:${serverPort}`); - feedback.info(`[${example}] vulcan local server started!`); + feedback.info(`[${example}] Bundler local server started!`); } export default projectInitializer; diff --git a/tests/utils/project-stop.js b/tests/utils/project-stop.js index 2216d041..a8c0885c 100644 --- a/tests/utils/project-stop.js +++ b/tests/utils/project-stop.js @@ -2,15 +2,15 @@ import { feedback } from '#utils'; import { execCommandInContainer } from './docker-env-actions.js'; /** - * Stop vulcan server in test container - * @param {number} serverPort - used port in vulcan server + * Stop bundler server in test container + * @param {number} serverPort - used port in bundler server * @param {string} example - current example name */ async function projectStop(serverPort, example) { - feedback.info(`[${example}] Stopping vulcan local server ...`); + feedback.info(`[${example}] Stopping Bundler local server ...`); await execCommandInContainer(`pkill -9 -f "dev -p ${serverPort}"`); - feedback.info(`[${example}] vulcan local server stopped!`); + feedback.info(`[${example}] Bundler local server stopped!`); } export default projectStop;