Skip to content

Commit

Permalink
Merge pull request #3 from irfanh94/api_tests
Browse files Browse the repository at this point in the history
Finalized all api tests
  • Loading branch information
irfanh94 authored Oct 8, 2024
2 parents 65a1dc2 + 88a38d1 commit 63fe84a
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 20 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"db:models:generate": "npx prisma generate",
"db:migration:create": "npx prisma migrate dev --create-only",
"db:migration:migrate": "npx prisma migrate deploy",
"test": "npm run build && NODE_ENV=test node ./build/Test/**/*.test.js"
"test": "npm run build && NODE_ENV=test find . -name \"*.test.js\" | xargs -n 1 node"
},
"dependencies": {
"@prisma/client": "^5.20.0",
Expand Down
10 changes: 10 additions & 0 deletions src/Db/Repository/UserRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,14 @@ export class UserRepository {
}
}) as Promise<UserEntity>;
}

public deleteUsers(ids: UuidV7[]) {
return this.dbClient.deleteMany({
where: {
id: {
in: ids.map(id => id.toString())
}
}
})
}
}
4 changes: 2 additions & 2 deletions src/Http/Controller/AuthenticateController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ export class AuthenticateController extends BaseHttpController {
const user = (await this.userRepository.getUserByEmails([body.email])).pop();

if (!user)
return this.json({code: "wrong_credentials"}, 404);
return this.json({code: "wrong_credentials"}, 401);

if (!bcrypt.compareSync(body.password, user.password))
return this.json({code: "wrong_credentials"}, 404);
return this.json({code: "wrong_credentials"}, 401);

return this.json({
token: await this.userAuthentication.createToken({id: user.id, email: user.email}),
Expand Down
82 changes: 82 additions & 0 deletions src/Test/Api/AccountController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import "reflect-metadata";
import {afterEach, beforeEach, describe, it} from "node:test";
import {strictEqual} from "node:assert";
import {container} from "../../Kernel/Container";
import {Http} from "../../Kernel/Http";
import axios from "axios";
import {Environment, EnvironmentKeys} from "../../Kernel/Environment";
import AxiosXHR = Axios.AxiosXHR;
import {ErrorResponse} from "./Types";
import {faker} from '@faker-js/faker';
import {hashSync} from "bcryptjs";
import {sleep} from "../../Common/Misc";
import {UserAuthentication} from "../../Service/Authentication/UserAuthentication";
import {UserRepository} from "../../Db/Repository/UserRepository";
import {UuidV7} from "../../Common/UuidV7";

const environment: Environment = container.get(Environment);
const userRepository: UserRepository = container.get(UserRepository);
const http = new Http(container);

const requestClient: Axios.AxiosInstance = axios.create({
baseURL: `http://localhost:${environment.get(EnvironmentKeys.APP_HTTP_PORT)}/account`,
validateStatus: status => true
});

const userId = UuidV7.new();
const userEmail = faker.internet.email().toLowerCase();
const userPassword = faker.internet.password({length: 8});
const userPasswordHashed = hashSync(userPassword, 13);

beforeEach(async () => {
await sleep(100);
await http.start();
await userRepository.createUser(userId, userEmail, userPasswordHashed);
});

afterEach(async () => {
await http.stop();
await userRepository.deleteUsers([userId]);
});

describe("AccountController", () => {
describe("GET /", () => {
it("should not authorize with empty header", async () => {
const response = await requestClient.get("/") as AxiosXHR<ErrorResponse>;

strictEqual(response.status, 401);
strictEqual(response.data.code, "not_authenticated");
strictEqual(response.data.meta, undefined);
});

it("should not authorize with wrong jwt", async () => {
const response = await requestClient.get("/", {
headers: {
"x-access-token": "false-jwt"
}
}) as AxiosXHR<ErrorResponse>;

strictEqual(response.status, 401);
strictEqual(response.data.code, "not_authenticated");
strictEqual(response.data.meta, undefined);
});

it("should get user data with jwt", async () => {
const userJwt = await container.get(UserAuthentication).createToken({
id: userId.toString(),
email: userEmail
});

const response = await requestClient.get("/", {headers: {"x-access-token": userJwt}}) as AxiosXHR<{
id: string,
email: string,
password: string
}>;

strictEqual(response.status, 200);
strictEqual(userId.toString(), response.data.id);
strictEqual(userEmail, response.data.email);
strictEqual(userPasswordHashed, response.data.password);
});
});
});
101 changes: 101 additions & 0 deletions src/Test/Api/AuthenticateController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import "reflect-metadata";
import {afterEach, beforeEach, describe, it} from "node:test";
import {deepStrictEqual, strictEqual} from "node:assert";
import {container} from "../../Kernel/Container";
import {Http} from "../../Kernel/Http";
import axios from "axios";
import {Environment, EnvironmentKeys} from "../../Kernel/Environment";
import AxiosXHR = Axios.AxiosXHR;
import {ErrorResponse} from "./Types";
import {faker} from '@faker-js/faker';
import {hashSync} from "bcryptjs";
import {sleep} from "../../Common/Misc";
import {UserAuthentication} from "../../Service/Authentication/UserAuthentication";
import {UserRepository} from "../../Db/Repository/UserRepository";
import {UuidV7} from "../../Common/UuidV7";

const environment: Environment = container.get(Environment);
const userRepository: UserRepository = container.get(UserRepository);
const http = new Http(container);

const requestClient: Axios.AxiosInstance = axios.create({
baseURL: `http://localhost:${environment.get(EnvironmentKeys.APP_HTTP_PORT)}/authenticate`,
validateStatus: status => true
});

const userId = UuidV7.new();
const userEmail = faker.internet.email().toLowerCase();
const userPassword = faker.internet.password({length: 8});
const userPasswordHashed = hashSync(userPassword, 13);

beforeEach(async () => {
await sleep(100);
await http.start();
await userRepository.createUser(userId, userEmail, userPasswordHashed);
});

afterEach(async () => {
await http.stop();
await userRepository.deleteUsers([userId]);
});

describe("AuthenticateController", () => {
describe("POST /email-and-password", () => {
it("should not authenticate with empty body", async () => {
const response = await requestClient.post("/email-and-password") as AxiosXHR<ErrorResponse>;

strictEqual(response.status, 400);
strictEqual(response.data.code, "bad_request");
deepStrictEqual(response.data.meta, [
{
location: "body",
msg: "email must be valid",
path: "email",
type: "field"
},
{
location: "body",
msg: "password must be string",
path: "password",
type: "field"
},
{
location: "body",
msg: "password must not be empty",
path: "password",
type: "field"
}
]);
});

it("should not authenticate with wrong email and password", async () => {
const response = await requestClient.post("/email-and-password", {
email: userEmail,
password: userPassword + "-false"
}) as AxiosXHR<ErrorResponse>;

strictEqual(response.status, 401);
strictEqual(response.data.code, "wrong_credentials");
strictEqual(response.data.meta, undefined);
});

it("should authenticate with email and password", async () => {
const response = await requestClient.post("/email-and-password", {email: userEmail, password: userPassword}) as AxiosXHR<{
token: string,
user: {
id: string,
email: string,
password: string
}
}>;

const responseToken = container.get(UserAuthentication).verifyToken(response.data.token);

strictEqual(response.status, 200);
strictEqual(true, responseToken !== null);
strictEqual(userId.toString(), response.data.user.id);
strictEqual(userEmail, response.data.user.email);
strictEqual(userPasswordHashed, response.data.user.password);
});
});
});
34 changes: 17 additions & 17 deletions src/Test/Api/SignUpController.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "reflect-metadata";
import {afterEach, beforeEach, describe, it} from "node:test";
import * as assert from "assert"
import {deepStrictEqual, strictEqual} from "node:assert";
import {container} from "../../Kernel/Container";
import {Http} from "../../Kernel/Http";
import axios from "axios";
Expand Down Expand Up @@ -35,9 +35,9 @@ describe("SignUpController", () => {
it("should return bad request on non-existing data", async () => {
const response = await requestClient.post("/") as AxiosXHR<ErrorResponse>;

assert.strictEqual(response.status, 400);
assert.strictEqual(response.data.code, "bad_request");
assert.deepStrictEqual(response.data.meta, [
strictEqual(response.status, 400);
strictEqual(response.data.code, "bad_request");
deepStrictEqual(response.data.meta, [
{
location: "body",
msg: "email must be valid",
Expand Down Expand Up @@ -65,9 +65,9 @@ describe("SignUpController", () => {
password: "bar"
}) as AxiosXHR<ErrorResponse>;

assert.strictEqual(response.status, 400);
assert.strictEqual(response.data.code, "bad_request");
assert.deepStrictEqual(response.data.meta, [
strictEqual(response.status, 400);
strictEqual(response.data.code, "bad_request");
deepStrictEqual(response.data.meta, [
{
location: "body",
msg: "email must be valid",
Expand All @@ -91,13 +91,13 @@ describe("SignUpController", () => {

const responseWithOk = await requestClient.post("/", {email, password}) as AxiosXHR<ErrorResponse>;

assert.strictEqual(responseWithOk.status, 200);
strictEqual(responseWithOk.status, 200);

const responseWithError = await requestClient.post("/", {email, password}) as AxiosXHR<ErrorResponse>;

assert.strictEqual(responseWithError.status, 422);
assert.strictEqual(responseWithError.data.code, "user_exists");
assert.strictEqual(responseWithError.data.meta, undefined);
strictEqual(responseWithError.status, 422);
strictEqual(responseWithError.data.code, "user_exists");
strictEqual(responseWithError.data.meta, undefined);
});

it('should return user data on sign-up', async () => {
Expand All @@ -115,12 +115,12 @@ describe("SignUpController", () => {

const responseToken = container.get(UserAuthentication).verifyToken(responseWithOk.data.token);

assert.strictEqual(responseWithOk.status, 200);
assert.strictEqual(email.toLowerCase(), responseToken?.email);
assert.strictEqual(true, validate(responseToken?.id ?? ""));
assert.strictEqual(true, validate(responseWithOk.data.user.id));
assert.strictEqual(email.toLowerCase(), responseWithOk.data.user.email);
assert.strictEqual(true, compareSync(password, responseWithOk.data.user.password));
strictEqual(responseWithOk.status, 200);
strictEqual(email.toLowerCase(), responseToken?.email);
strictEqual(true, validate(responseToken?.id ?? ""));
strictEqual(true, validate(responseWithOk.data.user.id));
strictEqual(email.toLowerCase(), responseWithOk.data.user.email);
strictEqual(true, compareSync(password, responseWithOk.data.user.password));
});
});
});

0 comments on commit 63fe84a

Please sign in to comment.