Skip to content

Commit fd28aae

Browse files
committed
added unit test cases
1 parent be7fc2a commit fd28aae

22 files changed

+3396
-231
lines changed

jest.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
setupFilesAfterEnv: ['./jest.setup.redis-mock.js'],
6+
testMatch: ['**/*.test.ts']
7+
};

jest.setup.redis-mock.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
jest.mock('redis', () => jest.requireActual('redis-mock'));

package-lock.json

Lines changed: 3020 additions & 200 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"version": "1.0.0",
44
"description": "",
55
"scripts": {
6-
"start": "nodemon source/main.ts"
6+
"start": "nodemon source/main.ts",
7+
"test": "jest"
78
},
89
"keywords": [],
910
"author": "",
@@ -21,14 +22,18 @@
2122
"pg": "^8.12.0",
2223
"redis": "^4.6.15",
2324
"reflect-metadata": "^0.2.2",
25+
"ts-jest": "^29.2.5",
2426
"typeorm": "^0.3.20",
2527
"typing-assets": "^2.1.3"
2628
},
2729
"devDependencies": {
2830
"@types/bcrypt": "^5.0.2",
31+
"@types/jest": "^29.5.14",
2932
"@types/jsonwebtoken": "^9.0.6",
3033
"@types/node": "^20.12.3",
34+
"jest": "^29.7.0",
3135
"nodemon": "^3.1.0",
36+
"redis-mock": "^0.56.3",
3237
"ts-node": "^10.9.2"
3338
}
3439
}

source/api/v1/auth/AuthPreHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { extractToken } from "../shared/utils/common/TokenExtractor";
33
import { extractJwtPayload } from "./jwt/PayloadExtractor";
44
import { validateSignature } from "./jwt/SignatureValidator";
55
import { USER_EXCEPTIONS } from "../shared/exceptions/UserExceptions";
6-
import { IAuthService } from "../services/interfaces/AuthServiceInterface";
6+
import { IAuthService } from "../services/auth/AuthServiceInterface";
77
import { isException } from "../shared/utils/guards/ExceptionGuard";
88

99
export type AuthentificationPreHandler = (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {describe, expect, test} from "@jest/globals";
2+
import { generateToken } from "./TokenGenerator";
3+
import * as jwt from "jsonwebtoken"
4+
5+
describe("Token generator tests", () => {
6+
test("Should generate different token twice", () => {
7+
const token1 = generateToken("login")
8+
const token2 = generateToken("login")
9+
10+
expect(token1).toEqual(token2)
11+
});
12+
13+
test("Should have payload", () => {
14+
const token = generateToken("login")
15+
const { login } = jwt.decode(token) as any
16+
expect(login).toEqual("login")
17+
})
18+
});

source/api/v1/handlers/AuthHandlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FastifyInstance } from "fastify";
2-
import { IAuthService } from "../services/interfaces/AuthServiceInterface";
2+
import { IAuthService } from "../services/auth/AuthServiceInterface";
33
import { UserCredentials } from "../database/entities/User";
44
import { AUTH_EXCEPTIONS } from "../shared/exceptions/AuthExceptions";
55
import { AuthUserSchema, ChangePasswordSchema } from "../validation/schemas/AuthSchemas";

source/api/v1/handlers/NotesHandlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FastifyInstance } from "fastify";
2-
import { INotesService } from "../services/interfaces/NotesServiceInterface";
2+
import { INotesService } from "../services/notes/NotesServiceInterface";
33
import { Note, NoteCollaborators, NotePreview, NoteUpdate, NoteWithoutMetadata } from "../database/entities/Note";
44
import { NOTE_EXCEPTIONS } from "../shared/exceptions/NoteExceptions";
55
import { extractJwtPayload } from "../auth/jwt/PayloadExtractor";

source/api/v1/handlers/UsersHandlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { FastifyInstance } from "fastify";
2-
import { IUsersService } from "../services/interfaces/UsersServiceInterface";
2+
import { IUsersService } from "../services/users/UsersServiceInterface";
33
import { UserUpdate, UserWithoutMetadata, UserWithoutSensetives } from "../database/entities/User";
44
import { USER_EXCEPTIONS } from "../shared/exceptions/UserExceptions";
55
import { CreateUserSchema, GetMyProfileSchema, GetUserSchema, UpdateUserSchema } from "../validation/schemas/UserSchemas";
6-
import { UsersService } from "../services/UsersService";
6+
import { UsersService } from "../services/users/UsersService";
77
import { extractJwtPayload } from "../auth/jwt/PayloadExtractor";
88
import { extractToken } from "../shared/utils/common/TokenExtractor";
99
import { isException } from "../shared/utils/guards/ExceptionGuard";
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import {describe, expect, test} from "@jest/globals"
2+
import * as bcrypt from "bcrypt"
3+
import { User } from "../../database/entities/User";
4+
import { AuthService } from "./AuthService";
5+
import redis from "redis";
6+
7+
const user: User = {
8+
login: "login",
9+
password: bcrypt.hashSync("12345678", 4),
10+
11+
username: "username",
12+
personalColor: "#ffffff",
13+
isCollaborating: true,
14+
createdAt: new Date(),
15+
updatedAt: new Date()
16+
}
17+
18+
// setting different values for different test scenarios
19+
const mockGetUser = jest.fn()
20+
const mockUpdateUser = jest.fn()
21+
22+
const usersService = {
23+
getUser: mockGetUser,
24+
updateUserByLogin: mockUpdateUser
25+
}
26+
// @ts-ignore
27+
const authService = new AuthService(usersService, redis.createClient())
28+
29+
30+
describe("Auth service test", () => {
31+
describe("Authorization tests", () => {
32+
test("Should authorize, then generate and return token", async () => {
33+
usersService.getUser.mockResolvedValueOnce(user)
34+
const result = await authService.authorizeAndGenerateToken("[email protected]", "12345678") as any
35+
36+
expect(result).toBeDefined()
37+
expect(result.expiresIn).toBeDefined()
38+
expect(result.token.length).toBeGreaterThan(10)
39+
})
40+
41+
test("Should return wrong credentials error (wrong email)", async () => {
42+
usersService.getUser.mockResolvedValueOnce({
43+
message: "",
44+
statusCode: 404
45+
})
46+
const result = await authService.authorizeAndGenerateToken("[email protected]", "12345678") as any
47+
48+
expect(result).toBeDefined()
49+
expect(result.message).toBeDefined()
50+
expect(result.statusCode).toEqual(400)
51+
})
52+
53+
test("Should return wrong credentials error (wrong password)", async () => {
54+
usersService.getUser.mockResolvedValueOnce(user)
55+
const result = await authService.authorizeAndGenerateToken("[email protected]", "123456789") as any
56+
57+
expect(result).toBeDefined()
58+
expect(result.message).toBeDefined()
59+
expect(result.statusCode).toEqual(400)
60+
})
61+
62+
});
63+
64+
describe("Change password tests", () => {
65+
test("Should return success", async () => {
66+
usersService.getUser.mockResolvedValueOnce(user)
67+
const result = await authService.changePassword("login", "12345678", "23423423") as any
68+
69+
expect(result).toBeDefined()
70+
expect(result.success).toEqual(true)
71+
})
72+
73+
test("Should return wrong credentials error (wrong login)", async () => {
74+
usersService.getUser.mockResolvedValueOnce({
75+
message: "",
76+
statusCode: 404
77+
})
78+
const result = await authService.changePassword("login", "12345678", "23423423") as any
79+
80+
expect(result).toBeDefined()
81+
expect(result.message).toBeDefined()
82+
expect(result.statusCode).toEqual(400)
83+
})
84+
85+
test("Should return wrong credentials error (wrong old password)", async () => {
86+
usersService.getUser.mockResolvedValueOnce(user)
87+
const result = await authService.changePassword("login", "123456789", "23423423") as any
88+
89+
expect(result).toBeDefined()
90+
expect(result.message).toBeDefined()
91+
expect(result.statusCode).toEqual(400)
92+
})
93+
94+
test("Should return passwords are same error", async () => {
95+
usersService.getUser.mockResolvedValueOnce(user)
96+
const result = await authService.changePassword("login", "12345678", "12345678") as any
97+
98+
expect(result).toBeDefined()
99+
expect(result.message).toBeDefined()
100+
expect(result.statusCode).toEqual(400)
101+
})
102+
})
103+
104+
})
105+

source/api/v1/services/AuthService.ts renamed to source/api/v1/services/auth/AuthService.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { generateToken } from "../auth/jwt/TokenGenerator";
2-
import { IUsersService } from "./interfaces/UsersServiceInterface";
3-
import { AUTH_EXCEPTIONS } from "../shared/exceptions/AuthExceptions";
4-
import { IAuthService } from "./interfaces/AuthServiceInterface";
5-
import { CONFIG } from "../shared/config/ServerConfiguration";
1+
import { generateToken } from "../../auth/jwt/TokenGenerator";
2+
import { AUTH_EXCEPTIONS } from "../../shared/exceptions/AuthExceptions";
3+
import { IAuthService } from "./AuthServiceInterface";
4+
import { CONFIG } from "../../shared/config/ServerConfiguration";
65
import bcrypt from 'bcrypt'
7-
import { USER_EXCEPTIONS } from "../shared/exceptions/UserExceptions";
6+
import { USER_EXCEPTIONS } from "../../shared/exceptions/UserExceptions";
87
import { RedisClientType } from "redis";
9-
import { withExceptionCatch } from "../shared/decorators/WithExceptionCatch";
10-
import { isException } from "../shared/utils/guards/ExceptionGuard";
8+
import { withExceptionCatch } from "../../shared/decorators/WithExceptionCatch";
9+
import { isException } from "../../shared/utils/guards/ExceptionGuard";
10+
import { IUsersService } from "../users/UsersServiceInterface";
1111

1212
export class AuthService implements IAuthService {
1313
constructor(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {describe, expect, test} from "@jest/globals";
2+
3+
describe("Notes service tests", () => {
4+
5+
});

source/api/v1/services/NotesService.ts renamed to source/api/v1/services/notes/NotesService.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import {
44
NotePreview,
55
NoteUpdate,
66
NoteEntity,
7-
} from "../database/entities/Note";
7+
} from "../../database/entities/Note";
88
import {
99
User,
1010
UserEntity,
11-
} from "../database/entities/User";
12-
import {IUsersService} from "./interfaces/UsersServiceInterface";
13-
import {INotesService, NotesSearchOptions} from "./interfaces/NotesServiceInterface";
14-
import {NOTE_EXCEPTIONS} from "../shared/exceptions/NoteExceptions";
11+
} from "../../database/entities/User";
12+
import {IUsersService} from "../users/UsersServiceInterface";
13+
import {INotesService, NotesSearchOptions} from "./NotesServiceInterface";
14+
import {NOTE_EXCEPTIONS} from "../../shared/exceptions/NoteExceptions";
1515
import {Repository} from "typeorm";
16-
import {transformNoteCollaborators} from "../shared/utils/common/TransformNoteCollaborators";
16+
import {transformNoteCollaborators} from "../../shared/utils/common/TransformNoteCollaborators";
1717
import {excludeProperties} from "typing-assets";
18-
import {withExceptionCatch} from "../shared/decorators/WithExceptionCatch";
19-
import {isException} from "../shared/utils/guards/ExceptionGuard";
20-
import {USER_EXCEPTIONS} from "../shared/exceptions/UserExceptions";
18+
import {withExceptionCatch} from "../../shared/decorators/WithExceptionCatch";
19+
import {isException} from "../../shared/utils/guards/ExceptionGuard";
20+
import {USER_EXCEPTIONS} from "../../shared/exceptions/UserExceptions";
2121

2222
export class NotesService implements INotesService {
2323
private static generateNoteId(): string {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { UserWithoutMetadata } from "../../database/entities/User"
2+
import { UsersService } from "./UsersService"
3+
import {describe, expect, test} from "@jest/globals"
4+
5+
const mockFindOneBy = jest.fn()
6+
const mockSave = jest.fn()
7+
8+
const mockUpdate = jest.fn()
9+
10+
const usersRepository = {
11+
findOneBy: mockFindOneBy,
12+
save: mockSave,
13+
update: mockUpdate
14+
}
15+
// @ts-ignore
16+
const usersService = new UsersService(usersRepository)
17+
18+
const user: UserWithoutMetadata = {
19+
login: "login",
20+
password: "12345678",
21+
22+
username: "username",
23+
personalColor: "#ffffff",
24+
isCollaborating: true
25+
}
26+
27+
describe("Users service tests", () => {
28+
describe("Create user tests", () => {
29+
test("Should not return 'user already exists' error", async () => {
30+
usersRepository.save.mockResolvedValueOnce(true)
31+
32+
const result = await usersService.createUser(user) as any
33+
34+
expect(result).toBeDefined()
35+
expect(result).toEqual(true)
36+
})
37+
38+
test("Should return 'user already exists' error", async () => {
39+
usersRepository.save.mockResolvedValueOnce(true)
40+
usersRepository.findOneBy.mockResolvedValueOnce(user)
41+
42+
const result = await usersService.createUser(user) as any
43+
44+
expect(result).toBeDefined()
45+
expect(result.message).toBeDefined()
46+
expect(result.statusCode).toEqual(400)
47+
})
48+
})
49+
50+
describe("Get user tests", () => {
51+
test("Should return user data", async () => {
52+
usersRepository.findOneBy.mockResolvedValueOnce(user)
53+
54+
const result = await usersService.getUser("login", "login") as any
55+
56+
expect(result).toBeDefined()
57+
expect(result.login).toEqual("login")
58+
})
59+
60+
test("Should return 'user doesn't exist' error", async () => {
61+
usersRepository.findOneBy.mockResolvedValueOnce(undefined)
62+
63+
const result = await usersService.getUser("login", "login") as any
64+
65+
expect(result).toBeDefined()
66+
expect(result.message).toBeDefined()
67+
expect(result.statusCode).toEqual(404)
68+
})
69+
})
70+
71+
describe("Update user tests", () => {
72+
test("Should return user data", async () => {
73+
usersRepository.findOneBy.mockResolvedValueOnce(user)
74+
usersRepository.update.mockResolvedValueOnce(
75+
{ affected: true }
76+
)
77+
78+
const result = await usersService.updateUserByLogin("login", {}) as any
79+
80+
expect(result).toBeDefined()
81+
expect(result.login).toEqual("login")
82+
})
83+
84+
test("Should return 'user doesn't exist' error", async () => {
85+
usersRepository.update.mockResolvedValueOnce(
86+
{ affected: false }
87+
)
88+
89+
const result = await usersService.updateUserByLogin("login", {}) as any
90+
91+
expect(result).toBeDefined()
92+
expect(result.message).toBeDefined()
93+
expect(result.statusCode).toEqual(404)
94+
})
95+
})
96+
});

source/api/v1/services/UsersService.ts renamed to source/api/v1/services/users/UsersService.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import { User, UserUpdate, UserWithoutMetadata, UserWithoutSensetives } from "../database/entities/User";
2-
import { IUsersService } from "./interfaces/UsersServiceInterface";
3-
import { USER_EXCEPTIONS } from "../shared/exceptions/UserExceptions";
1+
import { User, UserUpdate, UserWithoutMetadata, UserWithoutSensetives } from "../../database/entities/User";
2+
import { IUsersService } from "./UsersServiceInterface";
3+
import { USER_EXCEPTIONS } from "../../shared/exceptions/UserExceptions";
44
import bcrypt from 'bcrypt'
55
import { Repository } from "typeorm";
6-
import { withExceptionCatch } from "../shared/decorators/WithExceptionCatch";
6+
import { withExceptionCatch } from "../../shared/decorators/WithExceptionCatch";
77

88
export class UsersService implements IUsersService {
99
/**
1010
* @mutable Mutates initial
1111
* @param user `User` to omit
12-
* @returns User without sensetive data such as `passoword` and `validToken`
12+
* @returns User without sensetive data such as `password` and `validToken`
1313
*/
1414
public static omitSensetiveData(user: User): UserWithoutSensetives {
1515
try {

0 commit comments

Comments
 (0)