Skip to content

Commit 6f3a482

Browse files
0xverinBillyWooo
andauthored
Add a template for JSON-RPC mock tests. (#3577)
* add requestEmailVerificationCode case * Added JSON-RPC mock test scripts and related Docker configurations * update README * revert docke compose * update lock file * update pnpm-lock.yaml * add todo comment * fmt * update environment * chmod file * update volumes * rename project * update docker compose * revert docker-compose.test.yml * enable mock-server * using OMNI_WORKER_ENDPOINT as rpc endpoint * update ci workflow, always rebuild omni executor while need to run omni executor test * add mock api * rm unused code --------- Co-authored-by: wli-pro <wli-pro> Co-authored-by: BillyWooo <yang@trustcomputing.de>
1 parent dc01ce4 commit 6f3a482

File tree

17 files changed

+1429
-1107
lines changed

17 files changed

+1429
-1107
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ jobs:
173173
fi
174174
if [ "${{ steps.filter.outputs.omni_executor_test }}" = "true" ] || [ "$rebuild_parachain" = "true" ] || [ "$rebuild_omni_executor" = "true" ]; then
175175
run_omni_executor_test=true
176+
rebuild_omni_executor=true
176177
fi
177178
178179
echo "rebuild_parachain=$rebuild_parachain" | tee -a $GITHUB_OUTPUT
@@ -618,6 +619,8 @@ jobs:
618619
file: tee-worker/omni-executor/Dockerfile
619620
tags: litentry/omni-executor:latest
620621
target: executor-worker
622+
build-args: |
623+
CARGO_FEATURES=mock-server
621624
622625
- name: Pull omni-executor image optionally
623626
if: needs.set-condition.outputs.rebuild_omni_executor == 'false'
@@ -1048,6 +1051,7 @@ jobs:
10481051
matrix:
10491052
include:
10501053
- test_name: omni-account-test
1054+
- test_name: jsonrpc-mock-tests
10511055
# disable it during RPC method refactoring
10521056
# - test_name: omni-client-sdk-test
10531057
name: ${{ matrix.test_name }}
@@ -1075,7 +1079,7 @@ jobs:
10751079
run: |
10761080
# Create .env file that needs by the docker-compose.yml
10771081
touch ../.env
1078-
docker compose -f docker-compose.yml -f ${{ matrix.test_name }}.yml up --no-build --exit-code-from ${{ matrix.test_name }} ${{ matrix.test_name }}
1082+
docker compose -f docker-compose.yml -f docker-compose.test.yml -f ${{ matrix.test_name }}.yml up --no-build --exit-code-from ${{ matrix.test_name }} ${{ matrix.test_name }}
10791083
10801084
- name: Collect docker logs if test fails
10811085
continue-on-error: true
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
# Copyright 2020-2024 Trust Computing GmbH.
4+
5+
# Enable strict error handling: exit on error (-e), error on undefined vars (-u), exit on pipe failures (-o pipefail)
6+
set -euo pipefail
7+
8+
# Parse command line options:
9+
# -u: Node URL to connect to
10+
while getopts ":u" opt; do
11+
case $opt in
12+
u)
13+
NODE_URL=$OPTARG
14+
;;
15+
?)
16+
echo "Invalid option: -${OPTARG}."
17+
exit 1
18+
;;
19+
esac
20+
done
21+
22+
NODE_URL=${NODE_URL:-"http://heima-node:9944"}
23+
echo "Using node url $NODE_URL"
24+
25+
echo "Running JSON-RPC tests"
26+
27+
echo "Installing dependencies and building ts-tests"
28+
cd /ts-tests
29+
pnpm install --force
30+
31+
echo "Running JSON-RPC tests"
32+
OMNI_WORKER_ENDPOINT=http://omni-executor:2100 pnpm --filter jsonrpc-mock-tests test jsonrpc.test.ts
33+
34+
echo "JSON-RPC tests completed successfully"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
services:
2+
jsonrpc-mock-tests:
3+
image: litentry/omni-executor:latest
4+
container_name: litentry-jsonrpc-mock-tests
5+
volumes:
6+
- ../ts-tests:/ts-tests
7+
- ../../client-api:/client-api
8+
- ../cli:/usr/local/executor-worker-cli
9+
build:
10+
context: ${PWD}/../../
11+
dockerfile: tee-worker/omni-executor/Dockerfile
12+
target: executor-worker
13+
depends_on:
14+
omni-executor:
15+
condition: service_healthy
16+
restart: true
17+
networks:
18+
- litentry-test-network
19+
entrypoint: "sh -c '/usr/local/executor-worker-cli/ts_jsonrpc_mock_test.sh 3>&1' "
20+
restart: "no"
21+
networks:
22+
litentry-test-network:
23+
driver: bridge

tee-worker/omni-executor/mock-server/src/pumpx.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ pub(crate) fn handle() -> impl Filter<Extract = (impl warp::Reply,), Error = war
2222
Response::builder().status(200).body("").unwrap()
2323
});
2424

25+
let post_heima_login = warp::post()
26+
.and(warp::path(BASE_PATH))
27+
.and(warp::path!("v3" / "account" / "post_heima_login"))
28+
.map(|| {
29+
// TODO implements your response logic
30+
Response::builder().status(200).body("").unwrap()
31+
});
32+
2533
// mock other APIs you need here
2634

27-
user_connect.or(verify_google_code)
35+
user_connect.or(verify_google_code).or(post_heima_login)
2836
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# JSON-RPC Mock Tests
2+
3+
This project contains TypeScript-based mock tests for the Omni Executor JSON-RPC API endpoints.
4+
5+
## Quick Start
6+
7+
### Local Development Setup
8+
9+
1. **Start the worker services:**
10+
11+
```bash
12+
make build-docker-test
13+
make start-test
14+
```
15+
16+
2. **Run the JSON-RPC tests:**
17+
```bash
18+
pnpm --filter jsonrpc-mock-tests run test jsonrpc.test.ts
19+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
3+
"parser": "@typescript-eslint/parser",
4+
"plugins": ["@typescript-eslint"],
5+
"root": true,
6+
"rules": {
7+
/**
8+
It's a temporary solution, folks. We had no choice but to shut it off,
9+
because there's just a liiittle bit too much "any" lurking around in the code.
10+
But fear not, my friends, for this is not the end of the story.
11+
We shall return, armed with determination and resolve,
12+
to tackle those "any" types head-on in the near future.
13+
**/
14+
"@typescript-eslint/no-explicit-any": ["off"],
15+
"@typescript-eslint/no-non-null-assertion": ["off"],
16+
"@typescript-eslint/no-var-requires": ["off"],
17+
18+
// explanation: https://typescript-eslint.io/rules/naming-convention/
19+
"@typescript-eslint/naming-convention": [
20+
"error",
21+
{
22+
"selector": "typeLike",
23+
"format": ["StrictPascalCase"]
24+
},
25+
{
26+
"selector": "variable",
27+
"modifiers": ["const"],
28+
"format": ["strictCamelCase", "UPPER_CASE"]
29+
},
30+
{
31+
"selector": "function",
32+
"format": ["strictCamelCase", "StrictPascalCase"]
33+
},
34+
{
35+
"selector": "parameter",
36+
"format": ["strictCamelCase"]
37+
}
38+
]
39+
}
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { describe, it } from 'mocha';
2+
import { expect } from 'chai';
3+
import { omniApi, RequestEmailVerificationCodeResponse } from './utils';
4+
5+
enum ClientId {
6+
Wildmeta = 'wildmeta',
7+
Heima = 'heima',
8+
Pumpx = 'pumpx',
9+
}
10+
11+
describe('Request Email Verification Code', () => {
12+
it('should request email verification code successfully', async function () {
13+
this.timeout(10000);
14+
15+
const result: RequestEmailVerificationCodeResponse = await omniApi.requestEmailVerificationCode({
16+
client_id: ClientId.Wildmeta,
17+
user_email: 'test@gmail.com',
18+
});
19+
20+
expect(result).to.be.null;
21+
});
22+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "jsonrpc-mock-tests",
3+
"license": "ISC",
4+
"type": "module",
5+
"scripts": {
6+
"check-format": "prettier --check .",
7+
"format": "prettier --write .",
8+
"lint": "eslint .",
9+
"test": "mocha --exit --sort -r ts-node/register --loader=ts-node/esm",
10+
"check-types": "tsc --noEmit"
11+
},
12+
"dependencies": {
13+
"mocha": "^10.6.0",
14+
"mocha-steps": "^1.3.0",
15+
"chai": "^4.3.6"
16+
},
17+
"devDependencies": {
18+
"@types/chai": "^4.3.3",
19+
"@types/mocha": "^10.0.10",
20+
"@types/node": "^20.4.4",
21+
"@typescript-eslint/eslint-plugin": "^5.60.0",
22+
"@typescript-eslint/parser": "^5.60.0",
23+
"prettier": "^2.8.3",
24+
"ts-node": "^10.9.1",
25+
"typescript": "^5.1.0",
26+
"eslint": "^8.46.0"
27+
},
28+
"packageManager": "pnpm@9.15.2",
29+
"engines": {
30+
"node": ">=20.0.0"
31+
}
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"moduleResolution": "Node",
6+
"declaration": true,
7+
"strict": true,
8+
"skipLibCheck": true,
9+
"esModuleInterop": true,
10+
"allowSyntheticDefaultImports": true,
11+
"resolveJsonModule": true,
12+
"baseUrl": "."
13+
},
14+
"ts-node": {
15+
"esm": true,
16+
"experimentalResolver": true,
17+
"experimentalSpecifierResolution": "node",
18+
"transpileOnly": true
19+
}
20+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
export interface JsonRpcRequest {
2+
jsonrpc: '2.0';
3+
method: string;
4+
params?: any[];
5+
id: string | number;
6+
}
7+
8+
export interface JsonRpcResponse<T = any> {
9+
jsonrpc: '2.0';
10+
result?: T;
11+
error?: {
12+
code: number;
13+
message: string;
14+
data?: any;
15+
};
16+
id: string | number;
17+
}
18+
19+
export class JsonRpcError extends Error {
20+
public readonly code: number;
21+
public readonly data?: any;
22+
23+
constructor(error: { code: number; message: string; data?: any }) {
24+
super(error.message);
25+
this.name = 'JsonRpcError';
26+
this.code = error.code;
27+
this.data = error.data;
28+
}
29+
}
30+
31+
export class JsonRpcClient {
32+
private static instance: JsonRpcClient;
33+
private readonly endpoint = process.env.OMNI_WORKER_ENDPOINT ?? 'http://localhost:2100/';
34+
private requestId = 1;
35+
36+
private constructor() {}
37+
38+
static getInstance(): JsonRpcClient {
39+
if (!JsonRpcClient.instance) {
40+
JsonRpcClient.instance = new JsonRpcClient();
41+
}
42+
return JsonRpcClient.instance;
43+
}
44+
45+
async call<T = any>(method: string, params?: any, token?: string): Promise<T> {
46+
const request: JsonRpcRequest = {
47+
jsonrpc: '2.0',
48+
method,
49+
params: params || [],
50+
id: this.requestId++,
51+
};
52+
53+
const headers: Record<string, string> = {
54+
'Content-Type': 'application/json',
55+
};
56+
57+
if (token) {
58+
headers['Authorization'] = `Bearer ${token}`;
59+
}
60+
61+
const response = await fetch(this.endpoint, {
62+
method: 'POST',
63+
headers,
64+
body: JSON.stringify(request),
65+
});
66+
67+
if (!response.ok) {
68+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
69+
}
70+
71+
const jsonResponse: JsonRpcResponse<T> = await response.json();
72+
console.log('jsonrpc method', method);
73+
console.log('jsonrpc params', params);
74+
console.log('jsonrpc response', jsonResponse);
75+
if (jsonResponse.error) {
76+
throw new JsonRpcError(jsonResponse.error);
77+
}
78+
79+
return jsonResponse.result!;
80+
}
81+
}

0 commit comments

Comments
 (0)