From b18fe3452888552ed4476d9a2ef8f99f704abd95 Mon Sep 17 00:00:00 2001
From: tsukino <87639218+0xtsukino@users.noreply.github.com>
Date: Thu, 28 Mar 2024 07:17:21 -0400
Subject: [PATCH] chore: fix test in github action (#41)
* Typescript tests in chrome
* increase timeout to 5min
* close page
* force close browser
* force exit process
* refactor: clean up test code
* chore: update readme for adding a new test
---
.github/workflows/test.yaml | 19 +-
.mocharc.json | 2 +-
package.json | 10 +-
readme.md | 7 +
test/simple-verify.spec.ts | 23 ---
.../full-integration-swapi.spec.ts | 20 +-
test/specs/simple-verify.spec.ts | 34 ++++
test/testRunner.ts | 184 ++++++++++++++----
test/utils.ts | 3 +
utils/.gitignore | 1 +
utils/build-tlsn-binaries.sh | 32 +++
{.cargo => wasm/.cargo}/config.toml | 0
webpack.web.dev.config.js | 7 +-
13 files changed, 260 insertions(+), 82 deletions(-)
delete mode 100644 test/simple-verify.spec.ts
rename test/{ => specs}/full-integration-swapi.spec.ts (61%)
create mode 100644 test/specs/simple-verify.spec.ts
create mode 100644 test/utils.ts
create mode 100644 utils/.gitignore
create mode 100755 utils/build-tlsn-binaries.sh
rename {.cargo => wasm/.cargo}/config.toml (100%)
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 5603fa0..497e979 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -7,7 +7,8 @@ on:
branches: [ main ]
env:
- LOCAL: true
+ LOCAL-NOTARY: true
+ LOCAL-WS: false
HEADLESS: true
PUPPETEER_SKIP_DOWNLOAD: true
@@ -31,6 +32,15 @@ jobs:
with:
workspaces: wasm/prover
+ - name: Install Chrome
+ uses: browser-actions/setup-chrome@v1
+ id: setup-chrome
+ with:
+ chrome-version: 121.0.6167.85
+
+ - name: Set CHROME_PATH environment variable
+ run: echo "CHROME_PATH=${{ steps.setup-chrome.outputs['chrome-path'] }}" >> $GITHUB_ENV
+
- name: Install Node.js
uses: actions/setup-node@v4
with:
@@ -50,7 +60,7 @@ jobs:
- uses: actions/cache@v4
name: Setup pnpm cache
with:
- path: ${{ env.STORE_PATH }}
+ path: ${STORE_PATH}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
@@ -67,9 +77,8 @@ jobs:
- name: Build WASM
run: npm run build:wasm
- - name: Test WASM
- run: npm run test:wasm
+ - name: Build Test dependencies
+ run: npm run build:tlsn-binaries
- name: Test
- if: false
run: npm run test
\ No newline at end of file
diff --git a/.mocharc.json b/.mocharc.json
index 4d570b0..1f31d44 100644
--- a/.mocharc.json
+++ b/.mocharc.json
@@ -6,5 +6,5 @@
"source-map-support/register"
],
"recursive": true,
- "timeout": 60000
+ "timeout": 300000
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 4d09872..983e7c1 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"update:wasm": "sh utils/check-wasm.sh -f",
"test:wasm": "cd wasm/prover; wasm-pack test --firefox --release --headless",
"build:wasm": "wasm-pack build --target web wasm/prover",
+ "build:tlsn-binaries": "sh utils/build-tlsn-binaries.sh",
"watch:dev": "webpack --config webpack.web.dev.config.js --watch",
"predev": "sh utils/check-wasm.sh",
"lint:wasm": "cd wasm/prover; cargo clippy --target wasm32-unknown-unknown",
@@ -28,7 +29,7 @@
"lint:tsc": "tsc --noEmit",
"lint": "concurrently npm:lint:tsc npm:lint:eslint",
"run:test": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha -r ts-node/register 'test/testRunner.ts'",
- "test": "npm run build:test && npm run run:test"
+ "test": "npm run build:tlsn-binaries && npm run build:test && npm run run:test"
},
"dependencies": {
"comlink": "^4.4.1"
@@ -38,6 +39,7 @@
"@types/mocha": "^10.0.6",
"@types/serve-handler": "^6.1.4",
"@typescript-eslint/eslint-plugin": "^6.15.0",
+ "@typescript-eslint/parser": "7.0.2",
"browserify": "^17.0.0",
"concurrently": "^5.1.0",
"constants-browserify": "^1.0.0",
@@ -50,6 +52,7 @@
"html-webpack-plugin": "~5.3.2",
"https-browserify": "^1.0.0",
"image-webpack-loader": "^6.0.0",
+ "js-yaml": "^4.1.0",
"mocha": "^10.2.0",
"node-loader": "^0.6.0",
"prettier": "^3.0.2",
@@ -65,11 +68,12 @@
"webpack": "^5.75.0",
"webpack-cli": "^5.0.0",
"webpack-dev-server": "^4.11.1",
- "webpack-node-externals": "^3.0.0"
+ "webpack-node-externals": "^3.0.0",
+ "wtfnode": "^0.9.1"
},
"author": "",
"license": "ISC",
"engines": {
"node": ">= 16.20.2"
}
-}
\ No newline at end of file
+}
diff --git a/readme.md b/readme.md
index b4d36ec..99dc831 100644
--- a/readme.md
+++ b/readme.md
@@ -63,3 +63,10 @@ npm run dev
npm install
npm run build
```
+
+## Adding a new test
+1. Create a new `new-test.spec.ts` file in the `test/` directory
+2. Add your spec file to the entry object fin `webpack.web.dev.config.js`
+3. Add a new `div` block to `test/test.ejs` like this: `
`. The div id must be the same as the filename.
+
+
diff --git a/test/simple-verify.spec.ts b/test/simple-verify.spec.ts
deleted file mode 100644
index d660dba..0000000
--- a/test/simple-verify.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { verify } from '../src';
-import simple_proof_redacted from './assets/simple_proof_redacted.json';
-
-(async function verify_simple() {
- try {
- const pem = `-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBv36FI4ZFszJa0DQFJ3wWCXvVLFr\ncRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg==\n-----END PUBLIC KEY-----`;
- const proof = {
- ...simple_proof_redacted,
- notaryUrl: 'http://localhost:7047',
- };
- console.log(proof);
- console.time('verify');
- const result = await verify(proof, pem);
- console.timeEnd('verify');
-
- // @ts-ignore
- document.getElementById('simple-verify').textContent =
- JSON.stringify(result);
- } catch (err) {
- console.log('caught error from wasm');
- console.error(err);
- }
-})();
diff --git a/test/full-integration-swapi.spec.ts b/test/specs/full-integration-swapi.spec.ts
similarity index 61%
rename from test/full-integration-swapi.spec.ts
rename to test/specs/full-integration-swapi.spec.ts
index 614c67f..f4a7e8d 100644
--- a/test/full-integration-swapi.spec.ts
+++ b/test/specs/full-integration-swapi.spec.ts
@@ -1,4 +1,5 @@
-import { prove, verify } from '../src';
+import { prove, verify } from '../../src';
+import { assert } from '../utils';
(async function () {
try {
@@ -9,10 +10,10 @@ import { prove, verify } from '../src';
method: 'GET',
headers: { secret: 'test_secret' },
maxTranscriptSize: 16384,
- notaryUrl: process.env.LOCAL
+ notaryUrl: process.env.LOCAL_NOTARY
? 'http://localhost:7047'
: 'https://notary.pse.dev',
- websocketProxyUrl: process.env.LOCAL
+ websocketProxyUrl: process.env.LOCAL_WS
? 'ws://localhost:55688'
: 'wss://notary.pse.dev/proxy?token=swapi.dev',
secretHeaders: ['test_secret'],
@@ -27,11 +28,20 @@ import { prove, verify } from '../src';
console.timeEnd('verify');
console.log(result);
+
+ assert(result.sent.includes('host: swapi.dev'));
+ assert(result.sent.includes('secret: XXXXXXXXXXX'));
+ assert(result.recv.includes('Luke Skywalker'));
+ assert(result.recv.includes('"hair_color":"XXXXX"'));
+ assert(result.recv.includes('"skin_color":"XXXX"'));
+
// @ts-ignore
- document.getElementById('full-integration-swapi').textContent =
- JSON.stringify(result);
+ document.getElementById('full-integration-swapi').textContent = 'OK';
} catch (err) {
console.log('caught error from wasm');
console.error(err);
+
+ // @ts-ignore
+ document.getElementById('full-integration-swapi').textContent = err.message;
}
})();
diff --git a/test/specs/simple-verify.spec.ts b/test/specs/simple-verify.spec.ts
new file mode 100644
index 0000000..c94b4e8
--- /dev/null
+++ b/test/specs/simple-verify.spec.ts
@@ -0,0 +1,34 @@
+import { verify } from '../../src';
+import simple_proof_redacted from '../assets/simple_proof_redacted.json';
+import { assert } from '../utils';
+
+(async function verify_simple() {
+ try {
+ const pem = `-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBv36FI4ZFszJa0DQFJ3wWCXvVLFr\ncRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg==\n-----END PUBLIC KEY-----`;
+ const proof = {
+ ...simple_proof_redacted,
+ notaryUrl: 'http://127.0.0.1:7047',
+ };
+ console.log(proof);
+ console.time('verify');
+ const result = await verify(proof, pem);
+ console.timeEnd('verify');
+
+ assert(
+ result.sent.includes(
+ 'user-agent: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ ),
+ );
+ assert(result.recv.includes('XXXXXXXXXXXXXX
'));
+ assert(result);
+
+ // @ts-ignore
+ document.getElementById('simple-verify').textContent = 'OK';
+ } catch (err) {
+ console.log('caught error from wasm');
+ console.error(err);
+
+ // @ts-ignore
+ document.getElementById('simple-verify').textContent = err.message;
+ }
+})();
diff --git a/test/testRunner.ts b/test/testRunner.ts
index 173973d..2a71281 100644
--- a/test/testRunner.ts
+++ b/test/testRunner.ts
@@ -1,67 +1,167 @@
-import puppeteer from 'puppeteer';
+import puppeteer, { Browser, Page, PuppeteerLaunchOptions } from 'puppeteer';
import { describe, it, before, after } from 'mocha';
const assert = require('assert');
-const { exec } = require('node:child_process');
+import { exec, ChildProcess } from 'node:child_process';
+import * as fs from 'fs';
+import path from 'path';
+const yaml = require('js-yaml');
+
+const timeout = 300000;
// puppeteer options
-const opts = {
- headless: !!process.env.HEADLESS,
+let opts: PuppeteerLaunchOptions = {
+ headless: !!process.env.HEADLESS ? 'new' : false,
slowMo: 100,
- timeout: 60000,
+ timeout: timeout,
};
-let browser: any, page: any, server: any;
+if (process.env.CHROME_PATH) {
+ opts = {
+ ...opts,
+ executablePath: process.env.CHROME_PATH,
+ };
+}
+
+let browser: Browser;
+let page: Page;
+let server: ChildProcess;
+
+let tlsnServerFixture: ChildProcess;
+const spawnTlsnServerFixture = () => {
+ const tlsnServerFixturePath = './utils/tlsn/tlsn/tlsn-server-fixture/';
+ // Spawn the server process
+ // tlsnServerFixture = spawn(tlsnServerFixturePath, []);
+ tlsnServerFixture = exec(`../target/release/main`, {
+ cwd: tlsnServerFixturePath,
+ });
+
+ tlsnServerFixture.stdout?.on('data', (data) => {
+ console.log(`Server: ${data}`);
+ });
+
+ tlsnServerFixture.stderr?.on('data', (data) => {
+ console.error(`Server Error: ${data}`);
+ });
+};
+
+let localNotaryServer: ChildProcess;
+const spawnLocalNotaryServer = async () => {
+ const localNotaryServerPath = './utils/tlsn/notary-server/';
+ localNotaryServer = exec(`target/release/notary-server`, {
+ cwd: localNotaryServerPath,
+ });
+ localNotaryServer.stdout?.on('data', (data) => {
+ console.log(`Server: ${data}`);
+ });
+
+ localNotaryServer.stderr?.on('data', (data) => {
+ console.error(`Server Error: ${data}`);
+ });
+
+ // wait for the notary server to be ready
+ while (true) {
+ try {
+ const response = await fetch('http://127.0.0.1:7047/info');
+ if (response.ok) {
+ return;
+ }
+ } catch (error) {
+ console.error('Waiting for local notary server...', error);
+ }
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ }
+};
+
+const configureNotarySerer = () => {
+ try {
+ const configPath = './utils/tlsn/notary-server/config/config.yaml';
+ const fileContents = fs.readFileSync(configPath, 'utf8');
+ const data = yaml.load(fileContents) as any;
+ data.tls.enabled = false;
+ data.server.host = '127.0.0.1';
+ const newYaml = yaml.dump(data);
+ fs.writeFileSync(configPath, newYaml, 'utf8');
+ console.log('YAML file has been updated.');
+ } catch (error) {
+ console.error('Error reading or updating the YAML file:', error);
+ }
+};
// expose variables
before(async function () {
server = exec('serve --config ../serve.json ./test-build -l 3001');
+
+ spawnTlsnServerFixture();
+ configureNotarySerer();
+ await spawnLocalNotaryServer();
browser = await puppeteer.launch(opts);
page = await browser.newPage();
- await page.goto('http://localhost:3001');
+ await page.goto('http://127.0.0.1:3001');
});
// close browser and reset global variables
after(async function () {
- await server.kill();
- // @ts-ignore
- await browser.close();
+ console.log('Cleaning up:');
+
+ try {
+ tlsnServerFixture.kill();
+ console.log('* Stopped TLSN Server Fixture ✅');
+
+ localNotaryServer.kill();
+ console.log('* Stopped Notary Server ✅');
+
+ server.kill();
+ console.log('* Stopped Test Web Server ✅');
+
+ await page.close();
+ await browser.close();
+ const childProcess = browser.process();
+ if (childProcess) {
+ childProcess.kill(9);
+ }
+ console.log('* Closed browser ✅');
+ process.exit(0);
+ } catch (e) {
+ console.error(e);
+ process.exit(0);
+ }
});
describe('tlsn-js test suite', function () {
- it('should prove and verify swapi.dev', async function () {
- const content = await check('full-integration-swapi');
- const result = safeParseJson(content);
- assert(result.sent.includes('host: swapi.dev'));
- assert(result.sent.includes('secret: XXXXXXXXXXX'));
- assert(result.recv.includes('Luke Skywalker'));
- assert(result.recv.includes('"hair_color":"XXXXX"'));
- assert(result.recv.includes('"skin_color":"XXXX"'));
- });
-
- it('should verify', async function () {
- const content = await check('simple-verify');
- const result = safeParseJson(content);
- assert(
- result.sent.includes(
- 'user-agent: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
- ),
- );
- assert(result.recv.includes('XXXXXXXXXXXXXX
'));
- assert(result);
+ fs.readdirSync(path.join(__dirname, 'specs')).forEach((file) => {
+ const [id] = file.split('.');
+ it(`Test ID: ${id}`, async function () {
+ const content = await check(id);
+ assert(content === 'OK');
+ });
});
+ // it('should prove and verify data from the local tlsn-server-fixture', async function () {
+ // const content = await check('full-integration-swapi');
+ // assert(content === 'OK');
+ // });
+ //
+ // it('should verify', async function () {
+ // const content = await check('simple-verify');
+ // assert(content === 'OK');
+ // });
});
async function check(testId: string): Promise {
- const content = await page.$eval('#' + testId, (n: any) => n.innerText);
- if (content) return content;
- await new Promise((r) => setTimeout(r, 1000));
- return check(testId);
-}
-
-function safeParseJson(data: string): any | null {
- try {
- return JSON.parse(data);
- } catch (e) {
- return null;
- }
+ const startTime = Date.now();
+ const attemptFetchContent = async (): Promise => {
+ const content = await page.$eval(
+ `#${testId}`,
+ (el: any) => el.textContent || '',
+ );
+ if (content) return content;
+ const elapsedTime = Date.now() - startTime;
+ if (elapsedTime >= timeout) {
+ throw new Error(
+ `Timeout: Failed to retrieve content for '#${testId}' within ${timeout} ms.`,
+ );
+ }
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ return attemptFetchContent();
+ };
+ return attemptFetchContent();
}
diff --git a/test/utils.ts b/test/utils.ts
new file mode 100644
index 0000000..3ddb6a6
--- /dev/null
+++ b/test/utils.ts
@@ -0,0 +1,3 @@
+export function assert(expr: any, msg = 'unknown assertion error') {
+ if (!Boolean(expr)) throw new Error(msg);
+}
diff --git a/utils/.gitignore b/utils/.gitignore
new file mode 100644
index 0000000..0285576
--- /dev/null
+++ b/utils/.gitignore
@@ -0,0 +1 @@
+tlsn
diff --git a/utils/build-tlsn-binaries.sh b/utils/build-tlsn-binaries.sh
new file mode 100755
index 0000000..9504051
--- /dev/null
+++ b/utils/build-tlsn-binaries.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Run tlsn Server fixture
+
+# Set the directory to the location of the script
+cd "$(dirname "$0")"
+
+# Name of the directory where the repo will be cloned
+REPO_DIR="tlsn"
+
+# Check if the directory exists
+if [ ! -d "$REPO_DIR" ]; then
+ # Clone the repository if it does not exist
+ git clone https://github.com/tlsnotary/tlsn.git "$REPO_DIR"
+ cd "$REPO_DIR"
+else
+ # If the directory exists, just change to it
+ cd "$REPO_DIR"
+ # Fetch the latest changes in the repo without checkout
+ git fetch
+fi
+
+# Checkout the specific tag
+git checkout "v0.1.0-alpha.4"
+
+for dir in "tlsn/tlsn-server-fixture/" "notary-server"; do
+ # Change to the specific subdirectory
+ cd ${dir}
+
+ # Build the project
+ cargo build --release
+ cd -
+done
diff --git a/.cargo/config.toml b/wasm/.cargo/config.toml
similarity index 100%
rename from .cargo/config.toml
rename to wasm/.cargo/config.toml
diff --git a/webpack.web.dev.config.js b/webpack.web.dev.config.js
index 8a17d39..08bbba2 100644
--- a/webpack.web.dev.config.js
+++ b/webpack.web.dev.config.js
@@ -7,7 +7,8 @@ const isProd = process.env.NODE_ENV === 'production';
const envPlugin = new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
- LOCAL: false,
+ LOCAL_NOTARY: true,
+ LOCAL_WS: false,
HEADLESS: false,
});
@@ -37,8 +38,8 @@ module.exports = [
target: 'web',
mode: isProd ? 'production' : 'development',
entry: {
- 'full-integration-swapi.spec': path.join(__dirname, 'test', 'full-integration-swapi.spec.ts'),
- 'simple-verify': path.join(__dirname, 'test', 'simple-verify.spec.ts'),
+ 'full-integration-swapi.spec': path.join(__dirname, 'test', 'specs', 'full-integration-swapi.spec.ts'),
+ 'simple-verify': path.join(__dirname, 'test', 'specs', 'simple-verify.spec.ts'),
},
output: {
path: __dirname + '/test-build',