From 2e84f56098b6b0433397ea0939b271eda8f92878 Mon Sep 17 00:00:00 2001 From: Stefan Werfling Date: Thu, 29 Aug 2024 15:19:03 +0200 Subject: [PATCH] add jest for unit testing --- backend/.babelrc | 6 ++ backend/.eslintrc.json | 6 +- backend/jest.config.ts | 20 ++++++ backend/jest.setup.ts | 1 + backend/package.json | 4 +- .../src/inc/Provider/IpLocate/IpLocateIo.ts | 72 +++++++++++++++---- backend/src/types/ExpectSchema.ts | 36 ++++++++++ .../Provider/IpLocate/IpLocateIo.test.ts | 21 ++++++ backend/tsconfig.json | 4 +- package.json | 13 +++- 10 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 backend/.babelrc create mode 100644 backend/jest.config.ts create mode 100644 backend/jest.setup.ts create mode 100644 backend/src/types/ExpectSchema.ts create mode 100644 backend/tests/Provider/IpLocate/IpLocateIo.test.ts diff --git a/backend/.babelrc b/backend/.babelrc new file mode 100644 index 00000000..c58af5b1 --- /dev/null +++ b/backend/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + "@babel/preset-env", + "@babel/preset-typescript" + ] +} \ No newline at end of file diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json index bad6a6db..4e3ad1e4 100644 --- a/backend/.eslintrc.json +++ b/backend/.eslintrc.json @@ -2,7 +2,8 @@ "env": { "amd": true, "es6": true, - "node": true + "node": true, + "jest": true }, "extends": [ "eslint:all" @@ -13,7 +14,8 @@ }, "plugins": [ "@typescript-eslint", - "prefer-arrow" + "prefer-arrow", + "jest" ], "rules": { // typescript rules diff --git a/backend/jest.config.ts b/backend/jest.config.ts new file mode 100644 index 00000000..8ea3718e --- /dev/null +++ b/backend/jest.config.ts @@ -0,0 +1,20 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + verbose: true, + transform: { + '^.+\\.ts?$': [ + 'ts-jest', + { + useESM: true, + }, + ], + }, + extensionsToTreatAsEsm: ['.ts'], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + setupFilesAfterEnv: ['/jest.setup.ts'] +}; + +export default config; \ No newline at end of file diff --git a/backend/jest.setup.ts b/backend/jest.setup.ts new file mode 100644 index 00000000..429aa211 --- /dev/null +++ b/backend/jest.setup.ts @@ -0,0 +1 @@ +// TODO Env \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 6d36416a..5a213245 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,6 +5,7 @@ "main": "dist/main.js", "type": "module", "scripts": { + "test": "node --experimental-vm-modules ../node_modules/.bin/jest", "npm-check-updates": "npm-check-updates", "tsc": "tsc", "build": "npm run tsc" @@ -37,6 +38,7 @@ "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", "eslint": "^8.46.0", + "eslint-plugin-jest": "^28.8.0", "eslint-plugin-prefer-arrow": "^1.2.3", "typescript": "^4.9.5" }, @@ -54,8 +56,8 @@ "express-session": "^1.17.3", "fast-xml-parser": "^4.2.7", "flyingfish_core": "file:../core", - "flyingfish_schemas": "file:../schemas", "flyingfish_plugin_letsencrypt": "file:../plugins/letsencrypt", + "flyingfish_schemas": "file:../schemas", "got": "^12.6.1", "helmet": "^7.1.0", "moment": "^2.29.4", diff --git a/backend/src/inc/Provider/IpLocate/IpLocateIo.ts b/backend/src/inc/Provider/IpLocate/IpLocateIo.ts index 6966367c..55e40307 100644 --- a/backend/src/inc/Provider/IpLocate/IpLocateIo.ts +++ b/backend/src/inc/Provider/IpLocate/IpLocateIo.ts @@ -1,24 +1,66 @@ import {Logger} from 'flyingfish_core'; import got from 'got'; +import {ExtractSchemaResultType, Vts} from 'vts'; + +export const SchemaIpLocateData = Vts.object({ + id: Vts.or([ + Vts.string(), + Vts.null() + ]), + country: Vts.or([ + Vts.string(), + Vts.null() + ]), + country_code: Vts.or([ + Vts.string(), + Vts.null() + ]), + city: Vts.or([ + Vts.string(), + Vts.null() + ]), + continent: Vts.or([ + Vts.string(), + Vts.null() + ]), + latitude: Vts.or([ + Vts.string(), + Vts.null() + ]), + longitude: Vts.or([ + Vts.string(), + Vts.null() + ]), + time_zone: Vts.or([ + Vts.string(), + Vts.null() + ]), + postal_code: Vts.or([ + Vts.string(), + Vts.null() + ]), + org: Vts.or([ + Vts.string(), + Vts.null() + ]), + asn: Vts.or([ + Vts.string(), + Vts.null() + ]), + subdivision: Vts.or([ + Vts.string(), + Vts.null() + ]), + subdivision2: Vts.or([ + Vts.string(), + Vts.null() + ]) +}); /** * IpLocateData */ -export type IpLocateData = { - ip: string|null; - country: string|null; - country_code: string|null; - city: string|null; - continent: string|null; - latitude: string|null; - longitude: string|null; - time_zone: string|null; - postal_code: string|null; - org: string|null; - asn: string|null; - subdivision: string|null; - subdivision2: string|null; -}; +export type IpLocateData = ExtractSchemaResultType; /** * IpLocateIo diff --git a/backend/src/types/ExpectSchema.ts b/backend/src/types/ExpectSchema.ts new file mode 100644 index 00000000..7384c19d --- /dev/null +++ b/backend/src/types/ExpectSchema.ts @@ -0,0 +1,36 @@ +import {Schema, SchemaErrors} from 'vts'; + +interface CustomMatcherResult { + pass: boolean; + message: () => string; +} + +export const SchemaMatchers = { + toValidateSchema: (received: any, schema: Schema): CustomMatcherResult | Promise => { + const errors: SchemaErrors = []; + + if (schema.validate(received, errors)) { + return { + message: () => 'expected object schema is not validate', + pass: false, + }; + } + + return { + message: () => 'expected object schema is validate', + pass: true, + }; + } +}; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace jest { + + interface Matchers { + toValidateSchema(schema: Schema): R; + } + } +} + +export {}; \ No newline at end of file diff --git a/backend/tests/Provider/IpLocate/IpLocateIo.test.ts b/backend/tests/Provider/IpLocate/IpLocateIo.test.ts new file mode 100644 index 00000000..b4ccf319 --- /dev/null +++ b/backend/tests/Provider/IpLocate/IpLocateIo.test.ts @@ -0,0 +1,21 @@ +import {IpLocateData, IpLocateIo, SchemaIpLocateData} from '../../../src/inc/Provider/IpLocate/IpLocateIo'; +import {SchemaMatchers} from '../../../src/types/ExpectSchema'; + +expect.extend(SchemaMatchers); + +describe('testing ip locate IO by ip 8.8.8.8', () => { + const ip = '8.8.8.8'; + let result: IpLocateData|null = null; + + beforeAll(async() => { + result = await IpLocateIo.location(ip); + }); + + test('test result is not empty', async() => { + expect(result).toBeTruthy(); + }); + + test('test result type by schema: SchemaIpLocateData', async() => { + expect(result).toValidateSchema(SchemaIpLocateData); + }); +}); \ No newline at end of file diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 0ae0c930..e1175f76 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -12,9 +12,9 @@ "sourceMap": true, "strict": true, "target": "ES2022", - "rootDir": "src" + "rootDir": "src", }, "include": [ - "./src/**/*.ts" + "./src/**/*.ts", ] } \ No newline at end of file diff --git a/package.json b/package.json index b38d37a4..a9817071 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,23 @@ "sshserver", "frontend", "ddnsserver", - "plugins" + "plugins", + "vpn" ], "scripts": { "npm-check-updates": "npm-check-updates" }, "dependencies": { "husky": "^9.0.11" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@babel/preset-typescript": "^7.24.7", + "@types/jest": "^29.5.12", + "babel-jest": "^29.7.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2" } }