-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from usherlabs/test/setup
Test/setup
- Loading branch information
Showing
12 changed files
with
3,622 additions
and
600 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node | ||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions | ||
|
||
name: Node CI | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
pull_request: | ||
branches: [main] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
# env: | ||
# # ROOCH_ORACLE_ADDRESS= ${{ secrets.ROOCH_ORACLE_ADDRESS }} | ||
# # ROOCH_PRIVATE_KEY= ${{ secrets.ROOCH_PRIVATE_KEY }} | ||
|
||
strategy: | ||
matrix: | ||
node-version: [20.x] | ||
|
||
steps: | ||
- name: Use Node.js ${{ matrix.node-version }} 🛎️ | ||
uses: actions/checkout@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
persist-credentials: false | ||
|
||
- name: Install 🔧 dependencies | ||
run: npx yarn install | ||
|
||
- name: build | ||
run: npx yarn build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
npx lint-staged | ||
# npm test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
|
||
# Check if test cases pass | ||
npm run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// TODO: Improve coverage to 90% | ||
const coverageToNumber = 40; // [0..100] | ||
|
||
/* | ||
* For a detailed explanation regarding each configuration property and type check, visit: | ||
* https://jestjs.io/docs/configuration | ||
*/ | ||
|
||
export default { | ||
testTimeout: 40000, | ||
|
||
verbose: true, | ||
rootDir: "./", | ||
transform: { | ||
"^.+\\.ts?$": "ts-jest", | ||
}, | ||
moduleFileExtensions: ["ts", "js", "json", "node"], | ||
clearMocks: true, // clear mocks before every test | ||
resetMocks: false, // reset mock state before every test | ||
testMatch: [ | ||
"<rootDir>/**/*.spec.ts", // Commenting cache test for github actions | ||
"<rootDir>/**/*.test.ts", | ||
"<rootDir>/**/*.test.js", | ||
], // match only tests inside /tests folder | ||
testPathIgnorePatterns: ["<rootDir>/node_modules/"], // exclude unnecessary folders | ||
|
||
// following lines are about coverage | ||
collectCoverage: true, | ||
collectCoverageFrom: ["<rootDir>/orchestrator/src/**/*.ts"], | ||
coverageDirectory: "<rootDir>/coverage", | ||
coverageReporters: ["lcov"], | ||
coverageThreshold: { | ||
global: { | ||
// branches: coverageToNumber, | ||
// functions: coverageToNumber, | ||
lines: coverageToNumber, | ||
statements: coverageToNumber, | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { randomUUID } from "node:crypto"; | ||
import * as fs from "node:fs"; | ||
import * as path from "node:path"; | ||
import { config } from "dotenv"; | ||
|
||
// Convert object to .env format | ||
const convertToEnvFormat = (obj: Record<string, string | undefined>): string => { | ||
return Object.entries(obj) | ||
.map(([key, value]) => `${key}=${value ?? ""}`) | ||
.join("\n"); | ||
}; | ||
|
||
const envFilePath = path.resolve(__dirname, ".env."); | ||
|
||
// Function to create .env file | ||
const createEnvFile = (config: Record<string, string | undefined>, envFilePath: string) => { | ||
fs.writeFileSync(envFilePath, convertToEnvFormat(config), "utf8"); | ||
}; | ||
|
||
// Function to delete .env file | ||
const deleteEnvFile = (envFilePath: string) => { | ||
if (fs.existsSync(envFilePath)) { | ||
fs.unlinkSync(envFilePath); | ||
} | ||
}; | ||
|
||
describe(".env Check", () => { | ||
let testFilePath: string; | ||
beforeEach(() => { | ||
testFilePath = envFilePath + randomUUID(); | ||
}); | ||
|
||
afterEach(() => { | ||
deleteEnvFile(testFilePath); | ||
}); | ||
|
||
test("test dynamic Env", async () => { | ||
const data = { | ||
test: "tedded", | ||
}; | ||
createEnvFile(data, testFilePath); | ||
config({ path: testFilePath }); | ||
expect(data.test).toBe(process.env.test); | ||
}); | ||
|
||
const tests = [ | ||
{ | ||
name: "Empty .env file", | ||
data: {}, | ||
wantErr: true, | ||
errorMessage: '"roochOracleAddress" is not allowed to be empty', | ||
}, | ||
{ | ||
name: "test invalid roochOracleAddress", | ||
data: { | ||
ROOCH_ORACLE_ADDRESS: "0xf81628c3bf85c3fc628f29a3739365d4428101fbbecca0dcc7e3851f34faea6V", | ||
}, | ||
wantErr: true, | ||
errorMessage: '"roochOracleAddress" contains an invalid value', | ||
}, | ||
{ | ||
name: "Invalid Chain Check ", | ||
data: { | ||
PREFERRED_CHAIN: "ROOCHd", | ||
ROOCH_ORACLE_ADDRESS: "0xf81628c3bf85c3fc628f29a3739365d4428101fbbecca0dcc7e3851f34faea6a", | ||
}, | ||
wantErr: true, | ||
errorMessage: '"preferredChain" must be one of [ROOCH, APTOS]', | ||
}, | ||
{ | ||
name: "valid ROOCH_ORACLE_ADDRESS but missing", | ||
data: { | ||
PREFERRED_CHAIN: "ROOCH", | ||
ROOCH_ORACLE_ADDRESS: "0xf81628c3bf85c3fc628f29a3739365d4428101fbbecca0dcc7e3851f34faea6c", | ||
}, | ||
wantErr: true, | ||
errorMessage: '"roochPrivateKey" is not allowed to be empty', | ||
}, | ||
{ | ||
name: "valid data", | ||
data: { | ||
PREFERRED_CHAIN: "ROOCH", | ||
ROOCH_ORACLE_ADDRESS: "0xf81628c3bf85c3fc628f29a3739365d4428101fbbecca0dcc7e3851f34faea6c", | ||
ROOCH_PRIVATE_KEY: "0xf81628c3bf85c3fc628f29a3739365d4428101fbbecca0dcc7e3851f34faea6c", | ||
}, | ||
wantErr: false, | ||
errorMessage: '"roochPrivateKey" is not allowed to be empty', | ||
}, | ||
{ | ||
name: "rooch variables not required when preferred chain is set to APTOS ", | ||
data: { | ||
PREFERRED_CHAIN: "APTOS", | ||
}, | ||
wantErr: false, | ||
errorMessage: "", | ||
}, | ||
]; | ||
|
||
tests.forEach(({ name, data, wantErr, errorMessage }) => { | ||
it(name, async () => { | ||
createEnvFile(data, testFilePath); | ||
config({ path: testFilePath, override: true }); | ||
if (wantErr) { | ||
try { | ||
const { default: envVars } = await import("../env"); | ||
expect(envVars).toBeNull(); | ||
} catch (err: any) { | ||
expect(err?.message).toBe(errorMessage); | ||
expect(err).toBeInstanceOf(Error); | ||
} | ||
} else { | ||
const { default: envVars } = await import("../env"); | ||
expect(envFilePath).not.toBeNull(); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import Joi from "joi"; | ||
|
||
export const addressValidator = (value: string, helpers: Joi.CustomHelpers<any>) => { | ||
if (/^0x[a-fA-F0-9]{64}$/.test(value)) { | ||
return value; | ||
} | ||
return helpers.error("any.invalid"); | ||
}; | ||
|
||
export const privateKeyValidator = (value: string, helpers: Joi.CustomHelpers<any>) => { | ||
// TODO: Add proper validation for Private key | ||
return value; | ||
}; | ||
|
||
// Define a regex pattern for basic cron expressions (seconds not included) | ||
const cronPattern = | ||
/^(\*|([0-5]?[0-9])) (\*|([01]?[0-9]|2[0-3])) (\*|([0-2]?[0-9]|3[0-1])) (\*|([0-1]?[0-9]|1[0-2])) (\*|[0-6])$/; | ||
|
||
// Define a regex pattern for cron expressions including seconds | ||
const cronPatternWithSeconds = | ||
/^(\*|([0-5]?[0-9])) (\*|([0-5]?[0-9])) (\*|([0-5]?[0-9])) (\*|([0-1]?[0-9]|2[0-3])) (\*|([0-2]?[0-9]|3[0-1])) (\*|([0-1]?[0-9]|1[0-2])) (\*|[0-6])$/; | ||
|
||
// Check for standard cron (5 fields) or extended cron (6 fields including seconds) | ||
export const cronValidator = (value: string, helpers: Joi.CustomHelpers<any>) => { | ||
if (cronPattern.test(value) || cronPatternWithSeconds.test(value)) { | ||
return value; | ||
} | ||
return helpers.error("any.invalid"); | ||
}; | ||
|
||
/* eslint-disable lint/suspicious/noThenProperty */ | ||
export const isRequiredWhenPreferredChainIs = (schema: Joi.StringSchema<string>, value: string) => | ||
Joi.string().when("preferredChain", { | ||
is: value, | ||
then: schema.required(), // 'details' is required if 'status' is 'active' | ||
otherwise: Joi.string().optional().allow("", null).default(""), // 'details' is optional otherwise | ||
}); |
Oops, something went wrong.