From 6230534cfa360c813428c5169e9905b9e6f4af1d Mon Sep 17 00:00:00 2001 From: Aku-mi Date: Thu, 11 Nov 2021 22:57:32 -0500 Subject: [PATCH 1/4] Auth Test Auth route testing. Some fixes were made in auth controller. --- .../src/controllers/auth.controller.ts | 4 +- cattleia-backend/src/libs/errors.ts | 4 + cattleia-backend/src/test/jest/auth.spec.ts | 158 ++++++++++++++++++ 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 cattleia-backend/src/test/jest/auth.spec.ts diff --git a/cattleia-backend/src/controllers/auth.controller.ts b/cattleia-backend/src/controllers/auth.controller.ts index 66c71a5..2d15155 100644 --- a/cattleia-backend/src/controllers/auth.controller.ts +++ b/cattleia-backend/src/controllers/auth.controller.ts @@ -32,7 +32,7 @@ export const signIn = async ( if (_user) { const matchPass = await comparePassword(_user.password, password); if (!matchPass) { - return res.json({ + return res.status(401).json({ ok: false, error: errors.wrongUserOrPassword, }); @@ -110,7 +110,7 @@ export const signUp = async ( if (role) { const foundRole: Role | null = await RoleModel.findOne({ name: role }); if (foundRole) _user.role = foundRole; - else return res.json({ ok: false }); + else return res.json({ ok: false, error: errors.invalidRole }); } else { const roleD: Role | null = await RoleModel.findOne({ name: "user" }); _user.role = roleD!; diff --git a/cattleia-backend/src/libs/errors.ts b/cattleia-backend/src/libs/errors.ts index ddc9261..bf3f7b5 100644 --- a/cattleia-backend/src/libs/errors.ts +++ b/cattleia-backend/src/libs/errors.ts @@ -24,6 +24,10 @@ export const errors = { message: "Invalid auth token.", code: 9090, }, + invalidRole: { + message: "Invalid role.", + code: 8020, + }, userAlreadyTaken: { message: "Username already taken.", code: 5342, diff --git a/cattleia-backend/src/test/jest/auth.spec.ts b/cattleia-backend/src/test/jest/auth.spec.ts new file mode 100644 index 0000000..b4bc449 --- /dev/null +++ b/cattleia-backend/src/test/jest/auth.spec.ts @@ -0,0 +1,158 @@ +import request from "supertest"; +import mongoose from "mongoose"; +import app from "../../app"; +import { UserModel } from "../../models"; +import { encryptPassword, errors } from "../../libs"; + +const api = request(app); + +const user = { + name: "Testing", + email: "test@email.com", + userName: "test", + password: "1234", +}; + +const user2 = { + name: "Testing2", + email: "test2@email.com", + userName: "test2", + password: "1234", +}; + +beforeEach(async () => { + await UserModel.deleteMany({}); + const _user = new UserModel({ + ...user2, + password: await encryptPassword(user2.password), + }); + await _user.save(); +}); + +afterAll(() => { + mongoose.connection.close(); +}); + +describe("POST /api/v1/auth/sign-up", () => { + test("should register an user with default role (user).", async () => { + const res = await api.post("/api/v1/auth/sign-up").send(user); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.user.id).toBeDefined(); + expect(res.body.user.role).toEqual("user"); + expect(res.body.user.token).toBeDefined(); + expect(res.body.user.account).toBeDefined(); + expect(res.body.user.rank).toBeDefined(); + expect(res.body.user.email).toEqual(user.email); + expect(res.body.user.name).toEqual(user.name); + expect(res.body.user.userName).toEqual(user.userName); + }); + + test("should register an user with role = user.", async () => { + const res = await api + .post("/api/v1/auth/sign-up") + .send({ ...user, role: "user" }); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.user.id).toBeDefined(); + expect(res.body.user.role).toEqual("user"); + expect(res.body.user.token).toBeDefined(); + expect(res.body.user.account).toBeDefined(); + expect(res.body.user.rank).toBeDefined(); + expect(res.body.user.email).toEqual(user.email); + expect(res.body.user.name).toEqual(user.name); + expect(res.body.user.userName).toEqual(user.userName); + }); + + test("should register an user with role = admin.", async () => { + const res = await api + .post("/api/v1/auth/sign-up") + .send({ ...user, role: "admin" }); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.user.id).toBeDefined(); + expect(res.body.user.role).toEqual("admin"); + expect(res.body.user.token).toBeDefined(); + expect(res.body.user.account).toBeDefined(); + expect(res.body.user.rank).toBeDefined(); + expect(res.body.user.email).toEqual(user.email); + expect(res.body.user.name).toEqual(user.name); + expect(res.body.user.userName).toEqual(user.userName); + }); + + test("should respond with 'invalid role' error.", async () => { + const res = await api + .post("/api/v1/auth/sign-up") + .send({ ...user, role: "test" }); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidRole.message); + expect(res.body.error.code).toEqual(errors.invalidRole.code); + }); + + test("should respond with 'userName taken' error.", async () => { + const res = await api.post("/api/v1/auth/sign-up").send(user2); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.userAlreadyTaken.message); + expect(res.body.error.code).toEqual(errors.userAlreadyTaken.code); + }); +}); + +describe("POST /api/v1/auth/sign-in", () => { + test("should be able to login.", async () => { + await api.post("/api/v1/auth/sign-up").send(user); + + const res = await api + .post("/api/v1/auth/sign-in") + .send({ userName: user.userName, password: user.password }); + + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.user.id).toBeDefined(); + expect(res.body.user.role).toEqual("user"); + expect(res.body.user.token).toBeDefined(); + expect(res.body.user.account).toBeDefined(); + expect(res.body.user.rank).toBeDefined(); + expect(res.body.user.email).toEqual(user.email); + expect(res.body.user.name).toEqual(user.name); + expect(res.body.user.userName).toEqual(user.userName); + }); + + test("shouldn't be able to login with wrong username.", async () => { + await api.post("/api/v1/auth/sign-up").send(user); + + const res = await api + .post("/api/v1/auth/sign-in") + .send({ userName: "wrong", password: user.password }); + + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.wrongUserOrPassword.message); + expect(res.body.error.code).toEqual(errors.wrongUserOrPassword.code); + }); + + test("shouldn't be able to login with wrong password.", async () => { + await api.post("/api/v1/auth/sign-up").send(user); + + const res = await api + .post("/api/v1/auth/sign-in") + .send({ userName: user.userName, password: "wrong" }); + + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.wrongUserOrPassword.message); + expect(res.body.error.code).toEqual(errors.wrongUserOrPassword.code); + }); + + test("shouldn't be able to login with wrong password and wrong username.", async () => { + await api.post("/api/v1/auth/sign-up").send(user); + + const res = await api + .post("/api/v1/auth/sign-in") + .send({ userName: "wrong1", password: "wrong2" }); + + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.wrongUserOrPassword.message); + expect(res.body.error.code).toEqual(errors.wrongUserOrPassword.code); + }); +}); From d1e057122d4f35f5d1c7caec0118c6419a0f54f6 Mon Sep 17 00:00:00 2001 From: Aku-mi Date: Fri, 12 Nov 2021 21:30:33 -0500 Subject: [PATCH 2/4] API Test Update Now is possible to test all routes. Some controllers were fixed. --- .github/workflows/build.yml | 4 +- .github/workflows/test.yml | 5 +- cattleia-backend/.env.example | 4 +- cattleia-backend/jest.config.js | 1 + cattleia-backend/package.json | 2 +- cattleia-backend/src/app.ts | 10 +- cattleia-backend/src/config/index.ts | 1 + .../src/controllers/index.controller.ts | 14 +- .../src/controllers/payment.controller.ts | 16 +- .../src/controllers/web3.controller.ts | 40 ++- cattleia-backend/src/libs/errors.ts | 19 +- cattleia-backend/src/middlewares/index.ts | 2 +- cattleia-backend/src/models/account.ts | 7 +- cattleia-backend/src/models/rank.ts | 2 +- cattleia-backend/src/models/role.ts | 2 +- cattleia-backend/src/models/user.ts | 2 +- cattleia-backend/src/test/jest/admin.spec.ts | 5 +- cattleia-backend/src/test/jest/auth.spec.ts | 29 +- cattleia-backend/src/test/jest/helper.ts | 62 ++++ cattleia-backend/src/test/jest/index.spec.ts | 5 +- .../src/test/jest/payment.spec.ts | 55 ++++ cattleia-backend/src/test/jest/setup.ts | 1 + cattleia-backend/src/test/jest/web3.spec.ts | 308 ++++++++++++++++++ cattleia-backend/src/test/test.http | 68 ---- cattleia-backend/types.d.ts | 1 + 25 files changed, 516 insertions(+), 149 deletions(-) create mode 100644 cattleia-backend/src/test/jest/helper.ts create mode 100644 cattleia-backend/src/test/jest/payment.spec.ts create mode 100644 cattleia-backend/src/test/jest/setup.ts create mode 100644 cattleia-backend/src/test/jest/web3.spec.ts delete mode 100644 cattleia-backend/src/test/test.http diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 429d265..d49b8a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,14 +18,14 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} + - name: Building using Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} cache: "yarn" cache-dependency-path: "**/yarn.lock" - run: yarn - - run: yarn run build + - run: yarn build - run: pm2 stop all - run: pm2 delete all - run: pm2 save --force diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 80ac11f..b4a792a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ on: branches: [master] jobs: - build: + test: runs-on: self-hosted defaults: @@ -18,12 +18,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} + - name: Testing using Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} cache: "yarn" cache-dependency-path: "**/yarn.lock" - run: yarn - - run: yarn run build - run: yarn test diff --git a/cattleia-backend/.env.example b/cattleia-backend/.env.example index 80f8988..3afa1c4 100644 --- a/cattleia-backend/.env.example +++ b/cattleia-backend/.env.example @@ -2,10 +2,12 @@ PORT= JWT_ACCESS= JWT_REFRESH= MONGODB_URI= +MONGODB_URI_TEST= DAPP_TOKEN= INFURA_MAIN= INFURA_ROPSTEN= INFURA_RINKEBY= GANACHE= STRIPE_SECRET= -STRIPE_PUBLISHABLE= \ No newline at end of file +STRIPE_PUBLISHABLE= +TEST_PRIVATE_KEY \ No newline at end of file diff --git a/cattleia-backend/jest.config.js b/cattleia-backend/jest.config.js index 266f90f..9a22711 100644 --- a/cattleia-backend/jest.config.js +++ b/cattleia-backend/jest.config.js @@ -3,6 +3,7 @@ module.exports = { preset: "ts-jest", testEnvironment: "node", rootDir: "./src/test/jest", + setupFiles: ["/setup.ts"], verbose: true, silent: true, detectOpenHandles: false, diff --git a/cattleia-backend/package.json b/cattleia-backend/package.json index cea1cfc..0ef9161 100644 --- a/cattleia-backend/package.json +++ b/cattleia-backend/package.json @@ -6,7 +6,7 @@ "scripts": { "start": "cross-env NODE_ENV=production node dist", "dev": "cross-env NODE_ENV=development ts-node-dev --respawn src", - "test": "cross-env NODE_ENV=test jest", + "test": "cross-env NODE_ENV=test jest --runInBand", "build": "tsc" }, "repository": { diff --git a/cattleia-backend/src/app.ts b/cattleia-backend/src/app.ts index 68c4856..f13a11e 100644 --- a/cattleia-backend/src/app.ts +++ b/cattleia-backend/src/app.ts @@ -1,8 +1,8 @@ +import cookieParser from "cookie-parser"; import express from "express"; import morgan from "morgan"; -import cors from "cors"; -import cookieParser from "cookie-parser"; import config from "./config"; +import cors from "cors"; import { initRoles, initRanks } from "./libs"; //Inits @@ -25,7 +25,11 @@ app.use( credentials: true, }) ); -app.use(morgan("dev")); + +if (config.NODE_ENV === "development") { + app.use(morgan("dev")); +} + app.use(cookieParser()); app.use(express.urlencoded({ extended: false })); app.use(express.json()); diff --git a/cattleia-backend/src/config/index.ts b/cattleia-backend/src/config/index.ts index a79b231..555fbb0 100644 --- a/cattleia-backend/src/config/index.ts +++ b/cattleia-backend/src/config/index.ts @@ -1,5 +1,6 @@ export default { PORT: process.env.PORT || 4000, + NODE_ENV: process.env.NODE_ENV, JWT: { ACCESS: process.env.JWT_ACCESSS || "acc", REFRESH: process.env.JWT_REFRESH || "ref", diff --git a/cattleia-backend/src/controllers/index.controller.ts b/cattleia-backend/src/controllers/index.controller.ts index f1e23fb..aac2b45 100644 --- a/cattleia-backend/src/controllers/index.controller.ts +++ b/cattleia-backend/src/controllers/index.controller.ts @@ -20,17 +20,17 @@ export const refreshToken = async ( ): Promise => { const token = req.cookies.jid; if (!token) - return res.json({ + return res.status(400).json({ ok: false, - accessToken: "", + error: errors.noAuthToken, }); try { const payload = verify(token, config.JWT.REFRESH) as Payload; const user: User | null = await UserModel.findById(payload._id); if (!user || user.tokenVersion !== payload.tokenVersion) - return res.json({ + return res.status(400).json({ ok: false, - accessToken: "", + error: errors.invalidAuthToken, }); res.cookie("jid", createRefreshToken(user), cookieConf); return res.json({ @@ -38,9 +38,9 @@ export const refreshToken = async ( accessToken: createAcessToken(user), }); } catch (error) { - return res.json({ + return res.status(400).json({ ok: false, - accessToken: "", + error: errors.invalidAuthToken, }); } }; @@ -52,7 +52,7 @@ export const revokeRefreshTokens = async ( const id = req.id; const user: User | null = await UserModel.findById(id); if (!user) - return res.json({ + return res.status(400).json({ ok: false, error: errors.invalidIDorNoWallet(id), }); diff --git a/cattleia-backend/src/controllers/payment.controller.ts b/cattleia-backend/src/controllers/payment.controller.ts index 5411bd8..4d97f53 100644 --- a/cattleia-backend/src/controllers/payment.controller.ts +++ b/cattleia-backend/src/controllers/payment.controller.ts @@ -1,20 +1,31 @@ import Stripe from "stripe"; import config from "../config"; import { Request, Response } from "express"; +import { errors } from "../libs"; const stripe = new Stripe(config.STRIPE_SECRET, { apiVersion: "2020-08-27", typescript: true, }); -export const stripePaymentIntent = async (req: Request, res: Response) => { +export const stripePaymentIntent = async ( + req: Request, + res: Response +): Promise => { const { amount } = req.body; + if (amount < 2000 || !amount) { + return res.status(400).json({ + ok: false, + error: errors.invalidAmount, + }); + } + const paymentIntent = await stripe.paymentIntents.create({ amount, currency: "cop", }); - res.json({ + return res.json({ ok: true, clientSecret: paymentIntent.client_secret, }); @@ -22,6 +33,7 @@ export const stripePaymentIntent = async (req: Request, res: Response) => { export const publishableKey = (_req: Request, res: Response) => { res.json({ + ok: true, publishableKey: config.STRIPE_PUBLISHABLE, }); }; diff --git a/cattleia-backend/src/controllers/web3.controller.ts b/cattleia-backend/src/controllers/web3.controller.ts index 963f481..76c4103 100644 --- a/cattleia-backend/src/controllers/web3.controller.ts +++ b/cattleia-backend/src/controllers/web3.controller.ts @@ -31,12 +31,12 @@ export const access = async ( const { password } = req.body; const _user: User | null = await UserModel.findById(id).populate("account"); if (!_user || !_user.account.payload) - return res.json({ + return res.status(400).json({ ok: false, error: errors.invalidIDorNoWallet(id), }); if (!(await comparePassword(_user.account.password, password))) - return res.json({ + return res.status(401).json({ ok: false, error: errors.wrongWalletPassword, }); @@ -55,10 +55,16 @@ export const createAccount = async ( const _user = await UserModel.findById(_id).populate("account"); - if (!_user || _user.account.payload) - return res.json({ + if (!_user) + return res.status(400).json({ ok: false, - error: errors.invalidIDorNoWallet(_id), + error: errors.invalidID(_id), + }); + + if (_user.account.payload) + return res.status(400).json({ + ok: false, + error: errors.hasWalletAccount, }); const account = web3.eth.accounts.create(getNonce()); @@ -72,7 +78,7 @@ export const createAccount = async ( if (!_account) return res.json({ ok: false, - error: errors.invalidIDorNoWallet(_id), + error: errors.noWalletAccount, }); _account.password = await encryptPassword(password); @@ -96,10 +102,16 @@ export const importAccount = async ( const _user: User | null = await UserModel.findById(id).populate("account"); - if (!_user || _user.account.payload) - return res.json({ + if (!_user) + return res.status(400).json({ ok: false, - error: errors.invalidIDorNoWallet(id), + error: errors.invalidID(id), + }); + + if (_user.account.payload) + return res.status(400).json({ + ok: false, + error: errors.hasWalletAccount, }); let { privateKey, password } = req.body; @@ -147,14 +159,14 @@ export const getPrivateKey = async ( const _user = await UserModel.findById(_id).populate("account"); if (!_user || !_user.account.payload) { - return res.json({ + return res.status(400).json({ ok: false, error: errors.invalidIDorNoWallet(_id), }); } if (!(await comparePassword(_user.account.password, password))) - return res.json({ + return res.status(401).json({ ok: false, error: errors.wrongWalletPassword, }); @@ -180,13 +192,13 @@ export const transferTo = async ( const _user: User | null = await UserModel.findById(id).populate("account"); if (!_user || !_user.account.payload) - return res.json({ + return res.status(400).json({ ok: false, error: errors.invalidIDorNoWallet(id), }); if (!(await comparePassword(_user.account.password, password))) - return res.json({ + return res.status(401).json({ ok: false, error: errors.wrongWalletPassword, }); @@ -223,7 +235,7 @@ export const transferTo = async ( errorStack(stack, e); } - return res.json({ + return res.status(400).json({ ok: false, error: stack[0], }); diff --git a/cattleia-backend/src/libs/errors.ts b/cattleia-backend/src/libs/errors.ts index bf3f7b5..f7a18fa 100644 --- a/cattleia-backend/src/libs/errors.ts +++ b/cattleia-backend/src/libs/errors.ts @@ -28,6 +28,10 @@ export const errors = { message: "Invalid role.", code: 8020, }, + invalidAmount: { + message: "Invalid amount, must be greater than 0.5$", + code: 6060, + }, userAlreadyTaken: { message: "Username already taken.", code: 5342, @@ -41,15 +45,24 @@ export const errors = { code: 5040, }, noWalletAccount: { - message: "There's no wallet accout for this user.", - code: 4050, + message: "There's no wallet account for this user.", + code: 4065, + }, + hasWalletAccount: { + message: "There's already a wallet account for this user.", + code: 4055, }, noAdmin: { message: "You need to be an admin.", code: 4045, }, + + invalidID: (id: string) => ({ + message: `There's no user with ID <${id}>.`, + code: 4040, + }), invalidIDorNoWallet: (id: string) => ({ message: `There's no user with ID <${id}> or the user has no wallet account.`, - code: 4040, + code: 4020, }), }; diff --git a/cattleia-backend/src/middlewares/index.ts b/cattleia-backend/src/middlewares/index.ts index ebfd4fa..1409038 100644 --- a/cattleia-backend/src/middlewares/index.ts +++ b/cattleia-backend/src/middlewares/index.ts @@ -31,7 +31,7 @@ export const validateToken = ( } else { return res.status(401).json({ ok: false, - error: errors.invalidAuthToken, + error: errors.noAuthToken, }); } } diff --git a/cattleia-backend/src/models/account.ts b/cattleia-backend/src/models/account.ts index 182fc1e..dfaad6e 100644 --- a/cattleia-backend/src/models/account.ts +++ b/cattleia-backend/src/models/account.ts @@ -15,9 +15,4 @@ const accountSchema = new Schema( } ); -export const AccountModel = model( - "Account", - accountSchema, - "Account", - true -); +export const AccountModel = model("Account", accountSchema); diff --git a/cattleia-backend/src/models/rank.ts b/cattleia-backend/src/models/rank.ts index eaec7ff..62ff90a 100644 --- a/cattleia-backend/src/models/rank.ts +++ b/cattleia-backend/src/models/rank.ts @@ -11,4 +11,4 @@ const rankSchema = new Schema( } ); -export const RankModel = model("Rank", rankSchema, "Rank", true); +export const RankModel = model("Rank", rankSchema); diff --git a/cattleia-backend/src/models/role.ts b/cattleia-backend/src/models/role.ts index bab07cf..53562a0 100644 --- a/cattleia-backend/src/models/role.ts +++ b/cattleia-backend/src/models/role.ts @@ -10,4 +10,4 @@ const roleSchema = new Schema( } ); -export const RoleModel = model("Role", roleSchema, "Role", true); +export const RoleModel = model("Role", roleSchema); diff --git a/cattleia-backend/src/models/user.ts b/cattleia-backend/src/models/user.ts index fc770ef..02454e7 100644 --- a/cattleia-backend/src/models/user.ts +++ b/cattleia-backend/src/models/user.ts @@ -48,4 +48,4 @@ const userSchema: Schema = new Schema( } ); -export const UserModel = model("User", userSchema, "User", true); +export const UserModel = model("User", userSchema); diff --git a/cattleia-backend/src/test/jest/admin.spec.ts b/cattleia-backend/src/test/jest/admin.spec.ts index 94afca1..2e14705 100644 --- a/cattleia-backend/src/test/jest/admin.spec.ts +++ b/cattleia-backend/src/test/jest/admin.spec.ts @@ -1,9 +1,6 @@ -import request from "supertest"; import mongoose from "mongoose"; -import app from "../../app"; import { errors } from "../../libs"; - -const api = request(app); +import { api } from "./helper"; afterEach(() => { mongoose.connection.close(); diff --git a/cattleia-backend/src/test/jest/auth.spec.ts b/cattleia-backend/src/test/jest/auth.spec.ts index b4bc449..d0fc54b 100644 --- a/cattleia-backend/src/test/jest/auth.spec.ts +++ b/cattleia-backend/src/test/jest/auth.spec.ts @@ -1,24 +1,7 @@ -import request from "supertest"; import mongoose from "mongoose"; -import app from "../../app"; -import { UserModel } from "../../models"; import { encryptPassword, errors } from "../../libs"; - -const api = request(app); - -const user = { - name: "Testing", - email: "test@email.com", - userName: "test", - password: "1234", -}; - -const user2 = { - name: "Testing2", - email: "test2@email.com", - userName: "test2", - password: "1234", -}; +import { api, user, user2 } from "./helper"; +import { UserModel } from "../../models"; beforeEach(async () => { await UserModel.deleteMany({}); @@ -100,11 +83,9 @@ describe("POST /api/v1/auth/sign-up", () => { describe("POST /api/v1/auth/sign-in", () => { test("should be able to login.", async () => { await api.post("/api/v1/auth/sign-up").send(user); - const res = await api .post("/api/v1/auth/sign-in") .send({ userName: user.userName, password: user.password }); - expect(res.status).toBe(200); expect(res.body.ok).toEqual(true); expect(res.body.user.id).toBeDefined(); @@ -119,11 +100,9 @@ describe("POST /api/v1/auth/sign-in", () => { test("shouldn't be able to login with wrong username.", async () => { await api.post("/api/v1/auth/sign-up").send(user); - const res = await api .post("/api/v1/auth/sign-in") .send({ userName: "wrong", password: user.password }); - expect(res.status).toBe(401); expect(res.body.ok).toEqual(false); expect(res.body.error.message).toEqual(errors.wrongUserOrPassword.message); @@ -132,11 +111,9 @@ describe("POST /api/v1/auth/sign-in", () => { test("shouldn't be able to login with wrong password.", async () => { await api.post("/api/v1/auth/sign-up").send(user); - const res = await api .post("/api/v1/auth/sign-in") .send({ userName: user.userName, password: "wrong" }); - expect(res.status).toBe(401); expect(res.body.ok).toEqual(false); expect(res.body.error.message).toEqual(errors.wrongUserOrPassword.message); @@ -145,11 +122,9 @@ describe("POST /api/v1/auth/sign-in", () => { test("shouldn't be able to login with wrong password and wrong username.", async () => { await api.post("/api/v1/auth/sign-up").send(user); - const res = await api .post("/api/v1/auth/sign-in") .send({ userName: "wrong1", password: "wrong2" }); - expect(res.status).toBe(401); expect(res.body.ok).toEqual(false); expect(res.body.error.message).toEqual(errors.wrongUserOrPassword.message); diff --git a/cattleia-backend/src/test/jest/helper.ts b/cattleia-backend/src/test/jest/helper.ts new file mode 100644 index 0000000..cb40fe8 --- /dev/null +++ b/cattleia-backend/src/test/jest/helper.ts @@ -0,0 +1,62 @@ +import request from "supertest"; +import app from "../../app"; + +export const api = request(app); + +const baseUrl = "/api/v1"; + +export const user = { + name: "Testing", + email: "test@email.com", + userName: "test", + password: "1234", +}; + +export const user2 = { + name: "Testing2", + email: "test2@email.com", + userName: "test2", + password: "1234", +}; + +export const user3 = { + name: "Testing3", + email: "test3@email.com", + userName: "test3", + password: "1234", +}; + +export const invalidToken = async ( + url: string, + token: boolean +): Promise => { + return await api + .post(baseUrl + url) + .set({ Authorization: token ? "bearer adsfsdfdf" : "" }) + .send({ password: "1234" }); +}; + +export const simplePost = async ( + url: string, + data: object +): Promise => { + return await api.post(baseUrl + url).send(data); +}; + +export const postWithToken = async ( + url1: string, + url2: string, + data1: object, + data2: object +): Promise<{ res: request.Response; res1: request.Response }> => { + const res1 = await api.post(baseUrl + url1).send(data1); + const res = await api + .post(baseUrl + url2) + .set({ Authorization: `bearer ${res1.body.user.token}` }) + .send(data2); + + return { + res, + res1, + }; +}; diff --git a/cattleia-backend/src/test/jest/index.spec.ts b/cattleia-backend/src/test/jest/index.spec.ts index e902beb..4ae81fd 100644 --- a/cattleia-backend/src/test/jest/index.spec.ts +++ b/cattleia-backend/src/test/jest/index.spec.ts @@ -1,8 +1,5 @@ -import request from "supertest"; import mongoose from "mongoose"; -import app from "../../app"; - -const api = request(app); +import { api } from "./helper"; afterEach(() => { mongoose.connection.close(); diff --git a/cattleia-backend/src/test/jest/payment.spec.ts b/cattleia-backend/src/test/jest/payment.spec.ts new file mode 100644 index 0000000..ccf70eb --- /dev/null +++ b/cattleia-backend/src/test/jest/payment.spec.ts @@ -0,0 +1,55 @@ +import mongoose from "mongoose"; +import config from "../../config"; +import { errors } from "../../libs"; +import { api } from "./helper"; + +afterEach(async () => { + await mongoose.connection.close(); +}); + +describe("GET /api/v1/payment/get-publishable-key", () => { + test("should respond with a stripe publishableKey.", async () => { + const res = await api.get("/api/v1/payment/get-publishable-key"); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.publishableKey).toEqual(config.STRIPE_PUBLISHABLE); + }); +}); + +describe("POST /api/v1/payment/create-payment-intent", () => { + test("should respond with a stripe client-secret.", async () => { + const res = await api + .post("/api/v1/payment/create-payment-intent") + .withCredentials() + .set({ Authorization: `Bearer ${config.STRIPE_SECRET}` }) + .send({ + amount: 1000000, + }); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.clientSecret).toBeDefined(); + }); + + test("should respond with invalid amount error (amount defined).", async () => { + const res = await api + .post("/api/v1/payment/create-payment-intent") + .set({ Authorization: `Bearer ${config.STRIPE_SECRET}` }) + .send({ + amount: 1000, + }); + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAmount.message); + expect(res.body.error.code).toEqual(errors.invalidAmount.code); + }); + test("should respond with invalid amount error (amount undefined).", async () => { + const res = await api + .post("/api/v1/payment/create-payment-intent") + .set({ Authorization: `Bearer ${config.STRIPE_SECRET}` }) + .send({}); + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAmount.message); + expect(res.body.error.code).toEqual(errors.invalidAmount.code); + }); +}); diff --git a/cattleia-backend/src/test/jest/setup.ts b/cattleia-backend/src/test/jest/setup.ts new file mode 100644 index 0000000..4dad5d2 --- /dev/null +++ b/cattleia-backend/src/test/jest/setup.ts @@ -0,0 +1 @@ +require("dotenv").config({ path: __dirname + "./../../../.env" }); diff --git a/cattleia-backend/src/test/jest/web3.spec.ts b/cattleia-backend/src/test/jest/web3.spec.ts new file mode 100644 index 0000000..64f5de5 --- /dev/null +++ b/cattleia-backend/src/test/jest/web3.spec.ts @@ -0,0 +1,308 @@ +import mongoose from "mongoose"; +import { AccountModel, UserModel } from "../../models"; +import { errors } from "../../libs"; +import { + user, + user2, + user3, + invalidToken, + postWithToken, + simplePost, +} from "./helper"; + +beforeAll(async () => { + await AccountModel.deleteMany({}); + await UserModel.deleteMany({}); +}); + +afterAll(async () => { + await mongoose.connection.close(); +}); + +describe("POST /api/v1/web3/create-account", () => { + test("shouldn't be able to create a wallet (token error).", async () => { + const res = await invalidToken("/web3/create-account", true); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAuthToken.message); + expect(res.body.error.code).toEqual(errors.invalidAuthToken.code); + }); + test("shouldn't be able to create a wallet (no-token error).", async () => { + const res = await invalidToken("/web3/create-account", false); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.noAuthToken.message); + expect(res.body.error.code).toEqual(errors.noAuthToken.code); + }); + + test("should be able to create a wallet.", async () => { + const { res } = await postWithToken( + "/auth/sign-up", + "/web3/create-account", + user, + { password: "1234" } + ); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.address).toBeDefined(); + expect(res.body.balance).toBeDefined(); + }); + + test("shouldn't be able to create a wallet (user already has one).", async () => { + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/create-account", + { password: user.password, userName: user.userName }, + { password: "1234" } + ); + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.hasWalletAccount.message); + expect(res.body.error.code).toEqual(errors.hasWalletAccount.code); + }); +}); + +describe("POST /api/v1/web3/import-account", () => { + test("shouldn't be able to import a wallet (token error).", async () => { + const res = await invalidToken("/web3/import-account", true); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAuthToken.message); + expect(res.body.error.code).toEqual(errors.invalidAuthToken.code); + }); + test("shouldn't be able to import a wallet (no-token error).", async () => { + const res = await invalidToken("/web3/import-account", false); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.noAuthToken.message); + expect(res.body.error.code).toEqual(errors.noAuthToken.code); + }); + + test("should be able to import a wallet.", async () => { + const { res } = await postWithToken( + "/auth/sign-up", + "/web3/import-account", + user2, + { password: "1234", privateKey: process.env.TEST_PRIVATE_KEY } + ); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.address).toBeDefined(); + expect(res.body.balance).toBeDefined(); + }); + + test("shouldn't be able to import a wallet (user already has one).", async () => { + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/import-account", + { password: user2.password, userName: user2.userName }, + { password: "1234", privateKey: process.env.TEST_PRIVATE_KEY } + ); + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.hasWalletAccount.message); + expect(res.body.error.code).toEqual(errors.hasWalletAccount.code); + }); +}); + +describe("POST /api/v1/web3/access", () => { + test("shouldn't be able to access to wallet (token error).", async () => { + const res = await invalidToken("/web3/access", true); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAuthToken.message); + expect(res.body.error.code).toEqual(errors.invalidAuthToken.code); + }); + test("shouldn't be able to access to wallet (no-token error).", async () => { + const res = await invalidToken("/web3/access", false); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.noAuthToken.message); + expect(res.body.error.code).toEqual(errors.noAuthToken.code); + }); + + test("should be able to access to wallet.", async () => { + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/access", + { password: user2.password, userName: user2.userName }, + { password: "1234" } + ); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + }); + + test("shouldn't be able to access to wallet (wrong password).", async () => { + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/access", + { password: user2.password, userName: user2.userName }, + { password: "1256" } + ); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.wrongWalletPassword.message); + expect(res.body.error.code).toEqual(errors.wrongWalletPassword.code); + }); + test("shouldn't be able to access to wallet (no wallet).", async () => { + const { res, res1 } = await postWithToken( + "/auth/sign-up", + "/web3/access", + { password: user3.password, userName: user3.userName }, + { password: "1256" } + ); + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual( + errors.invalidIDorNoWallet(res1.body.user.id).message + ); + expect(res.body.error.code).toEqual( + errors.invalidIDorNoWallet(res1.body.user.id).code + ); + }); +}); + +describe("POST /api/v1/web3/get-key", () => { + test("shouldn't be able to get privateKey (token error).", async () => { + const res = await invalidToken("/web3/get-key", true); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAuthToken.message); + expect(res.body.error.code).toEqual(errors.invalidAuthToken.code); + }); + test("shouldn't be able to get privateKey (no-token error).", async () => { + const res = await invalidToken("/web3/get-key", false); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.noAuthToken.message); + expect(res.body.error.code).toEqual(errors.noAuthToken.code); + }); + + test("should be able to get privateKey.", async () => { + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/get-key", + { password: user2.password, userName: user2.userName }, + { password: "1234" } + ); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.privateKey).toEqual("0x" + process.env.TEST_PRIVATE_KEY); + }); + + test("shouldn't be able to get privateKey (wrong password).", async () => { + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/get-key", + { password: user2.password, userName: user2.userName }, + { password: "1256" } + ); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.wrongWalletPassword.message); + expect(res.body.error.code).toEqual(errors.wrongWalletPassword.code); + }); + test("shouldn't be able to get privateKey (no wallet).", async () => { + const { res, res1 } = await postWithToken( + "/auth/sign-in", + "/web3/get-key", + { password: user3.password, userName: user3.userName }, + { password: "1256" } + ); + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual( + errors.invalidIDorNoWallet(res1.body.user.id).message + ); + expect(res.body.error.code).toEqual( + errors.invalidIDorNoWallet(res1.body.user.id).code + ); + }); +}); + +jest.setTimeout(30000); + +describe("POST /api/v1/web3/transfer-to", () => { + test("shouldn't be able to transfer eth (token error).", async () => { + const res = await invalidToken("/web3/transfer-to", true); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.invalidAuthToken.message); + expect(res.body.error.code).toEqual(errors.invalidAuthToken.code); + }); + test("shouldn't be able to transfer eth (no-token error).", async () => { + const res = await invalidToken("/web3/transfer-to", false); + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.noAuthToken.message); + expect(res.body.error.code).toEqual(errors.noAuthToken.code); + }); + + test("should be able to transfer eth.", async () => { + const res_ = await simplePost("/auth/sign-in", { + password: user.password, + userName: user.userName, + }); + + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/transfer-to", + { password: user2.password, userName: user2.userName }, + { password: "1234", to: res_.body.user.account.address, value: "0.001" } + ); + expect(res.status).toBe(200); + expect(res.body.ok).toEqual(true); + expect(res.body.to).toEqual("0x" + res_.body.user.account.address); + expect(res.body.hash).toBeDefined(); + expect(res.body.status).toBeDefined(); + }); + + test("shouldn't be able to transfer eth (wrong password).", async () => { + const res_ = await simplePost("/auth/sign-in", { + password: user.password, + userName: user.userName, + }); + + const { res } = await postWithToken( + "/auth/sign-in", + "/web3/transfer-to", + { password: user2.password, userName: user2.userName }, + { + password: "1256", + to: res_.body.user.account.address, + value: "0.001", + } + ); + + expect(res.status).toBe(401); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual(errors.wrongWalletPassword.message); + expect(res.body.error.code).toEqual(errors.wrongWalletPassword.code); + }); + test("shouldn't be able to transfer eth (no wallet).", async () => { + const res_ = await simplePost("/auth/sign-in", { + password: user.password, + userName: user.userName, + }); + + const { res, res1 } = await postWithToken( + "/auth/sign-in", + "/web3/transfer-to", + { password: user3.password, userName: user3.userName }, + { + password: "1234", + to: res_.body.user.account.address, + value: "0.001", + } + ); + + expect(res.status).toBe(400); + expect(res.body.ok).toEqual(false); + expect(res.body.error.message).toEqual( + errors.invalidIDorNoWallet(res1.body.user.id).message + ); + expect(res.body.error.code).toEqual( + errors.invalidIDorNoWallet(res1.body.user.id).code + ); + }); +}); diff --git a/cattleia-backend/src/test/test.http b/cattleia-backend/src/test/test.http deleted file mode 100644 index 90051dd..0000000 --- a/cattleia-backend/src/test/test.http +++ /dev/null @@ -1,68 +0,0 @@ -POST http://localhost:4000/api/v1/auth/sign-up HTTP/1.1 -Content-Type: application/json - -{ - "name":"akumi2", - "email":"aku2@gmail.com", - "userName":"aku2", - "password":"test2" -} - -### - -POST http://localhost:4000/api/v1/auth/sign-in HTTP/1.1 -Content-Type: application/json - -{ - "userName":"aku2", - "password":"test2" -} - -### - -POST http://localhost:4000/api/v1/web3/create-account HTTP/1.1 -Content-Type: application/json -Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWt1MiIsIl9pZCI6IjYxMmJiNDRjOTk2OGU5MWVhODIyYjI5YyIsInJvbGUiOiJ1c2VyIiwidG9rZW5WZXJzaW9uIjowLCJpYXQiOjE2MzAyNTQxNTksImV4cCI6MTYzMDM0MDU1OX0.PGNkNRtpblQ_JtrtEJVkNU21a1ViwKPHi_qNZb7bi5g - -{ - "password":"test2" -} - -### - -POST http://localhost:4000/api/v1/web3/get-key HTTP/1.1 -Content-Type: application/json -Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWt1MiIsIl9pZCI6IjYxMmJiNDRjOTk2OGU5MWVhODIyYjI5YyIsInJvbGUiOiJ1c2VyIiwidG9rZW5WZXJzaW9uIjowLCJpYXQiOjE2MzAyNTQxNTksImV4cCI6MTYzMDM0MDU1OX0.PGNkNRtpblQ_JtrtEJVkNU21a1ViwKPHi_qNZb7bi5g - -{ - "password":"test2" -} - -### - -GET http://localhost:4000/api/v1/web3/balance HTTP/1.1 -Content-Type: application/json -Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWt1IiwiX2lkIjoiNjEyYmE5OWY3MjY0M2ExZTE4YjRjYmQ1Iiwicm9sZSI6InVzZXIiLCJ0b2tlblZlcnNpb24iOjAsImlhdCI6MTYzMDI1NDU2OCwiZXhwIjoxNjMwMzQwOTY4fQ._fQXzPQvilyfZ2uXpf-8jwtc71sJPwMrq0Cm4shV3iY - -### - -POST http://localhost:4000/api/v1/web3/import-account HTTP/1.1 -Content-Type: application/json -Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWt1MiIsIl9pZCI6IjYxMmVjZjU2MjYzNGE5MDVmNGJmODcxYyIsInJvbGUiOiJ1c2VyIiwidG9rZW5WZXJzaW9uIjowLCJpYXQiOjE2MzA0NTc2OTcsImV4cCI6MTYzMDU0NDA5N30.8Ut_GEg75J5bJVa2PN4SyEeVXCOb1uk2MQlTiy7rgZU - -{ - "privateKey":"2587479acdce25311aee18357b73a01187e070707251f2763a64daa7b8a155a5", - "password":"test2" -} - -### - -POST http://localhost:4000/api/v1/web3/transfer-to HTTP/1.1 -Content-Type: application/json -Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWt1MiIsIl9pZCI6IjYxMmJiNDRjOTk2OGU5MWVhODIyYjI5YyIsInJvbGUiOiJ1c2VyIiwidG9rZW5WZXJzaW9uIjowLCJpYXQiOjE2MzAyNTQxNTksImV4cCI6MTYzMDM0MDU1OX0.PGNkNRtpblQ_JtrtEJVkNU21a1ViwKPHi_qNZb7bi5g - -{ - "password":"test2", - "to":"0xc59dc2664c12fa760d3b2f16c49ceef5b96dd2f7", - "value":"1" -} \ No newline at end of file diff --git a/cattleia-backend/types.d.ts b/cattleia-backend/types.d.ts index e2f95c6..9f8f4ff 100644 --- a/cattleia-backend/types.d.ts +++ b/cattleia-backend/types.d.ts @@ -21,6 +21,7 @@ declare namespace NodeJS { STRIPE_SECRET: string; STRIPE_PUBLISHABLE: string; NODE_ENV: "production" | "development" | "test"; + TEST_PRIVATE_KEY: string; } } From 56fb994510030dbed0faabff444241b6e40bf3b8 Mon Sep 17 00:00:00 2001 From: Aku-mi Date: Fri, 12 Nov 2021 21:50:27 -0500 Subject: [PATCH 3/4] Workflows Update Loads .env file from host system. --- .github/workflows/build.yml | 1 + .github/workflows/test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d49b8a5..817e50b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,7 @@ jobs: node-version: ${{ matrix.node-version }} cache: "yarn" cache-dependency-path: "**/yarn.lock" + - run: cp ~/envs/.env . - run: yarn - run: yarn build - run: pm2 stop all diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4a792a..daa1c02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,5 +24,6 @@ jobs: node-version: ${{ matrix.node-version }} cache: "yarn" cache-dependency-path: "**/yarn.lock" + - run: cp ~/envs/.env . - run: yarn - run: yarn test From 99174d2f5c8ce9939b54957cf383e1e3051f26ea Mon Sep 17 00:00:00 2001 From: Aku-mi Date: Fri, 12 Nov 2021 22:29:24 -0500 Subject: [PATCH 4/4] Jest ENV load Loads env within jest tests. --- cattleia-backend/jest.config.js | 3 ++- cattleia-backend/src/test/jest/setup.ts | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 cattleia-backend/src/test/jest/setup.ts diff --git a/cattleia-backend/jest.config.js b/cattleia-backend/jest.config.js index 9a22711..d5ebae2 100644 --- a/cattleia-backend/jest.config.js +++ b/cattleia-backend/jest.config.js @@ -1,9 +1,10 @@ +require("dotenv").config(); + /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: "ts-jest", testEnvironment: "node", rootDir: "./src/test/jest", - setupFiles: ["/setup.ts"], verbose: true, silent: true, detectOpenHandles: false, diff --git a/cattleia-backend/src/test/jest/setup.ts b/cattleia-backend/src/test/jest/setup.ts deleted file mode 100644 index 4dad5d2..0000000 --- a/cattleia-backend/src/test/jest/setup.ts +++ /dev/null @@ -1 +0,0 @@ -require("dotenv").config({ path: __dirname + "./../../../.env" });