-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/local register (#7) user register api & unit test
* feat: 全域驗證器默認設定 * feat: 使用者資料表初始化 * feat: 使用者註冊驗證 * feat: 使用者註冊資料儲存 * feat: 使用者註冊密碼加密 * feat: 使用者註冊 swagger demo * feat: 使用者註冊加上回應狀態 * test: auth service unit test * test: auth controller 單元測試-dto資料驗證 * test: auth controller 單元測試-創建本地使用者201回應 * test: auth controller 單元測試-創建本地使用者資料驗證失敗406回應 * test: auth controller 單元測試-providers 資料庫命名錯誤修正 * test: auth controller 單元測試-創建本地使用者資料重覆失敗409回應 * test: auth service 單元測試加上測試後復原資料庫 * test: 將測試資料庫改成 sqlite * test: 將 afterEach 搬移至最後面並只需要一次 * fix: 將非此次測試的刪除 * fix: 修正狀態碼 406->400 * refactor: 將 createAt 改成 createdAt * fix: 範例訊息移除 name 的重複衝突 * test: usersService 單元測試-創建使用者 * refactor: 將 try catch 移除 * refactor: 更改為使用 hashSync * refactor: 使用單數 user * fix: auth controller 單元測試-重新撰寫是否會正確驗證傳入的 payload * refactor: 將 DTO 驗證搬移出去 * refactor: 將自定義的 validationPipe 獨立出來 * refactor: 應使用 pips 命名資料夾 * refactor: 刪除多餘命名 * refactor: 驗證 dto 使用自訂 validation-pipe
- Loading branch information
Showing
21 changed files
with
1,245 additions
and
18 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
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,79 @@ | ||
import { ConflictException } from "@nestjs/common"; | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { getRepositoryToken, TypeOrmModule } from "@nestjs/typeorm"; | ||
import { dataSourceJest } from "src/config/data-source"; | ||
import { UserEntity } from "src/user/entities/user.entity"; | ||
import { CreateUserRespose } from "src/user/resposes/create-user-respose"; | ||
import { UserService } from "src/user/user.service"; | ||
import { Repository } from "typeorm"; | ||
|
||
import { CreateUserDto } from "../user/dto/create-user.dto"; | ||
import { AuthController } from "./auth.controller"; | ||
import { AuthService } from "./auth.service"; | ||
|
||
describe("AuthController", () => { | ||
let authController: AuthController; | ||
let authService: AuthService; | ||
let userRepository: Repository<UserEntity>; | ||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
imports: [TypeOrmModule.forRoot(dataSourceJest)], | ||
controllers: [AuthController], | ||
providers: [ | ||
AuthService, | ||
UserService, | ||
{ | ||
provide: getRepositoryToken(UserEntity), | ||
useValue: UserEntity, // 使用測試資料庫的 Repository | ||
}, | ||
], | ||
}).compile(); | ||
|
||
authController = module.get<AuthController>(AuthController); | ||
authService = module.get<AuthService>(AuthService); | ||
userRepository = module.get<Repository<UserEntity>>( | ||
getRepositoryToken(UserEntity), | ||
); | ||
}); | ||
describe("create", () => { | ||
it("應該會創建一個使用者,並返回 201 狀態碼", async () => { | ||
const createUserDto: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account", | ||
password: "Password@123", | ||
}; | ||
const expectedResponse: CreateUserRespose = { | ||
statusCode: 201, | ||
message: "創建成功", | ||
}; | ||
jest.spyOn(authService, "register").mockResolvedValue(expectedResponse); | ||
const result = await authController.register(createUserDto); | ||
expect(result).toEqual(expectedResponse); | ||
}); | ||
it("應該會發生資料使用者重覆,並返回 409 狀態碼", async () => { | ||
const createUserDto1: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account1", | ||
password: "Password@123", | ||
}; | ||
try { | ||
await authService.register(createUserDto1); | ||
await authService.register(createUserDto1); | ||
} catch (error) { | ||
expect(error).toBeInstanceOf(ConflictException); | ||
expect(error.response).toEqual({ | ||
statusCode: 409, | ||
message: ["email 已被註冊。", "account 已被註冊。"], | ||
error: "Conflict", | ||
}); | ||
} | ||
}); | ||
}); | ||
afterEach(async () => { | ||
if (userRepository && userRepository.clear) { | ||
await userRepository.clear(); | ||
} | ||
}); | ||
}); |
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 @@ | ||
import { Body, Controller, Post } from "@nestjs/common"; | ||
import { | ||
ApiBadRequestResponse, | ||
ApiConflictResponse, | ||
ApiCreatedResponse, | ||
ApiOperation, | ||
ApiTags, | ||
} from "@nestjs/swagger"; | ||
import { CreateUserDto } from "src/user/dto/create-user.dto"; | ||
import { CreateUserBadrequestError } from "src/user/exceptions/create-user-badrequest-error.exception"; | ||
import { CreateUserConflictError } from "src/user/exceptions/create-user-conflict-error.exception"; | ||
import { CreateUserRespose } from "src/user/resposes/create-user-respose"; | ||
|
||
import { AuthService } from "./auth.service"; | ||
|
||
@ApiTags("Auth") | ||
@Controller("auth") | ||
export class AuthController { | ||
constructor(private readonly authService: AuthService) {} | ||
@Post("register") | ||
@ApiOperation({ | ||
summary: "使用者註冊", | ||
description: "會檢查是否重複過的資料", | ||
}) | ||
@ApiCreatedResponse({ | ||
description: "使用者創建成功", | ||
type: CreateUserRespose, | ||
}) | ||
@ApiConflictResponse({ | ||
description: "使用者資料重覆", | ||
type: CreateUserConflictError, | ||
}) | ||
@ApiBadRequestResponse({ | ||
description: "使用者格式不符", | ||
type: CreateUserBadrequestError, | ||
}) | ||
register(@Body() userDto: CreateUserDto) { | ||
return this.authService.register(userDto); | ||
} | ||
} |
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,14 @@ | ||
import { Module } from "@nestjs/common"; | ||
import { TypeOrmModule } from "@nestjs/typeorm"; | ||
import { UserEntity } from "src/user/entities/user.entity"; | ||
import { UserModule } from "src/user/user.module"; | ||
|
||
import { AuthController } from "./auth.controller"; | ||
import { AuthService } from "./auth.service"; | ||
|
||
@Module({ | ||
imports: [UserModule, TypeOrmModule.forFeature([UserEntity])], | ||
controllers: [AuthController], | ||
providers: [AuthService], | ||
}) | ||
export class AuthModule {} |
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,129 @@ | ||
import { ConflictException, HttpStatus } from "@nestjs/common"; | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { getRepositoryToken, TypeOrmModule } from "@nestjs/typeorm"; | ||
import { validate } from "class-validator"; | ||
import { dataSourceJest } from "src/config/data-source"; | ||
import { CreateUserDto } from "src/user/dto/create-user.dto"; | ||
import { UserEntity } from "src/user/entities/user.entity"; | ||
import { UserService } from "src/user/user.service"; | ||
import { Repository } from "typeorm"; | ||
|
||
import { AuthService } from "./auth.service"; | ||
|
||
describe("AuthService", () => { | ||
let authService: AuthService; | ||
let userRepository: Repository<UserEntity>; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
imports: [TypeOrmModule.forRoot(dataSourceJest)], | ||
providers: [ | ||
AuthService, | ||
UserService, | ||
{ | ||
provide: getRepositoryToken(UserEntity), | ||
useValue: UserEntity, // 使用測試資料庫的 Repository | ||
}, | ||
], | ||
}).compile(); | ||
|
||
authService = module.get<AuthService>(AuthService); | ||
userRepository = module.get<Repository<UserEntity>>( | ||
getRepositoryToken(UserEntity), | ||
); | ||
}); | ||
|
||
describe("createUser - Data", () => { | ||
it("應該會創建 一個使用者", async () => { | ||
const test_data: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account1", | ||
password: "Password@123", | ||
}; | ||
const user = await authService.register(test_data); | ||
|
||
expect(user).toBeDefined(); | ||
expect(user.statusCode).toEqual(HttpStatus.CREATED); | ||
expect(user.message).toEqual("創建成功"); | ||
}); | ||
it("應該會發生 email、account 已被註冊衝突", async () => { | ||
const createUserDto1: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account", | ||
password: "Password@123", | ||
}; | ||
try { | ||
await authService.register(createUserDto1); | ||
await authService.register(createUserDto1); | ||
} catch (error) { | ||
expect(error).toBeInstanceOf(ConflictException); | ||
expect(error.response).toEqual({ | ||
statusCode: 409, | ||
message: ["email 已被註冊。", "account 已被註冊。"], | ||
error: "Conflict", | ||
}); | ||
} | ||
}); | ||
it("應該會發生 email 已被註冊衝突", async () => { | ||
const test_data1: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account1", | ||
password: "Password@123", | ||
}; | ||
const test_data2: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account2", | ||
password: "Password@123", | ||
}; | ||
const errors = await validate(test_data1); | ||
expect(errors.length).toBe(0); | ||
try { | ||
await authService.register(test_data1); | ||
await authService.register(test_data2); | ||
} catch (error) { | ||
expect(error).toBeInstanceOf(ConflictException); | ||
expect(error.response).toEqual({ | ||
statusCode: 409, | ||
message: ["email 已被註冊。"], | ||
error: "Conflict", | ||
}); | ||
} | ||
}); | ||
it("應該會發生 account 已被註冊衝突", async () => { | ||
const test_data1: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account", | ||
password: "Password@123", | ||
}; | ||
const test_data2: CreateUserDto = { | ||
email: "[email protected]", | ||
name: "displayname", | ||
account: "account", | ||
password: "Password@123", | ||
}; | ||
const errors = await validate(test_data1); | ||
expect(errors.length).toBe(0); | ||
try { | ||
await authService.register(test_data1); | ||
await authService.register(test_data2); | ||
} catch (error) { | ||
expect(error).toBeInstanceOf(ConflictException); | ||
expect(error.response).toEqual({ | ||
statusCode: 409, | ||
message: ["account 已被註冊。"], | ||
error: "Conflict", | ||
}); | ||
} | ||
}); | ||
}); | ||
afterEach(async () => { | ||
if (userRepository && userRepository.clear) { | ||
await userRepository.clear(); | ||
} | ||
}); | ||
}); |
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,32 @@ | ||
import { ConflictException, Injectable } from "@nestjs/common"; | ||
import { InjectRepository } from "@nestjs/typeorm"; | ||
import { CreateUserDto } from "src/user/dto/create-user.dto"; | ||
import { UserEntity } from "src/user/entities/user.entity"; | ||
import { UserService } from "src/user/user.service"; | ||
import { Repository } from "typeorm"; | ||
|
||
@Injectable() | ||
export class AuthService { | ||
constructor( | ||
private userService: UserService, | ||
@InjectRepository(UserEntity) | ||
private userRepository: Repository<UserEntity>, | ||
) {} | ||
async register(userDto: CreateUserDto) { | ||
const existingUser = await this.userRepository.findOne({ | ||
where: [{ email: userDto.email }, { account: userDto.account }], | ||
}); | ||
|
||
if (existingUser) { | ||
const keys = ["email", "account"]; | ||
const conflictedAttributes = []; | ||
keys.forEach(key => { | ||
if (existingUser[key] === userDto[key]) { | ||
conflictedAttributes.push(key + " 已被註冊。"); | ||
} | ||
}); | ||
throw new ConflictException(conflictedAttributes); | ||
} | ||
return this.userService.create(userDto); | ||
} | ||
} |
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
Oops, something went wrong.