-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5edb57a
commit c56fc30
Showing
15 changed files
with
704 additions
and
5 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
Large diffs are not rendered by default.
Oops, something went wrong.
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
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,52 @@ | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { AuthController } from "./auth.controller"; | ||
import { AuthService } from "./auth.service"; | ||
|
||
jest.mock("nanoid", () => ({ | ||
nanoid: jest.fn(() => "mockNanoId123"), | ||
})); | ||
|
||
describe("AuthController", () => { | ||
let authController: AuthController; | ||
let authService: AuthService; | ||
|
||
const mockAuthService = { | ||
register: jest.fn(), | ||
login: jest.fn(), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [AuthController], | ||
providers: [{ provide: AuthService, useValue: mockAuthService }], | ||
}).compile(); | ||
|
||
authController = module.get<AuthController>(AuthController); | ||
authService = module.get<AuthService>(AuthService); | ||
}); | ||
|
||
it("should be defined", () => { | ||
expect(authController).toBeDefined(); | ||
}); | ||
|
||
describe("register", () => { | ||
it("should call authService.register and return the result", async () => { | ||
const dto = { | ||
email: "[email protected]", | ||
password: "password123", | ||
name: "Test User", | ||
}; | ||
const mockResult = { | ||
id: "mockNanoId123", | ||
email: "[email protected]", | ||
name: "Test User", | ||
}; | ||
mockAuthService.register.mockResolvedValue(mockResult); | ||
|
||
const result = await authController.register(dto); | ||
|
||
expect(authService.register).toHaveBeenCalledWith(dto.email, dto.password, dto.name); | ||
expect(result).toEqual(mockResult); | ||
}); | ||
}); | ||
}); |
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,29 @@ | ||
import { Controller, Post, Body, Request, UseGuards } from "@nestjs/common"; | ||
import { AuthService } from "./auth.service"; | ||
import { JwtAuthGuard } from "./jwt-auth.guard"; | ||
|
||
@Controller("auth") | ||
export class AuthController { | ||
constructor(private authService: AuthService) {} | ||
|
||
@Post("register") | ||
async register(@Body() body: { email: string; password: string; name: string }) { | ||
const { email, password, name } = body; | ||
return this.authService.register(email, password, name); | ||
} | ||
|
||
@Post("login") | ||
async login(@Body() body: { email: string; password: string }) { | ||
const user = await this.authService.validateUser(body.email, body.password); | ||
if (!user) { | ||
throw new Error("Invalid credentials"); | ||
} | ||
return this.authService.login(user); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Post("profile") | ||
getProfile(@Request() req) { | ||
return req.user; | ||
} | ||
} |
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,22 @@ | ||
import { Module } from "@nestjs/common"; | ||
import { MongooseModule } from "@nestjs/mongoose"; | ||
import { User, UserSchema } from "./schemas/user.schema"; | ||
import { AuthService } from "./auth.service"; | ||
import { AuthController } from "./auth.controller"; | ||
import { JwtModule } from "@nestjs/jwt"; | ||
import { PassportModule } from "@nestjs/passport"; | ||
import { JwtStrategy } from "./jwt.strategy"; | ||
|
||
@Module({ | ||
imports: [ | ||
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), | ||
PassportModule, | ||
JwtModule.register({ | ||
secret: process.env.JWT_SECRET, | ||
signOptions: { expiresIn: "1h" }, | ||
}), | ||
], | ||
providers: [AuthService, JwtStrategy], | ||
controllers: [AuthController], | ||
}) | ||
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,119 @@ | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { AuthService } from "./auth.service"; | ||
import { getModelToken } from "@nestjs/mongoose"; | ||
import { JwtService } from "@nestjs/jwt"; | ||
import * as bcrypt from "bcrypt"; | ||
import { nanoid } from "nanoid"; | ||
|
||
jest.mock("nanoid", () => ({ | ||
nanoid: jest.fn(() => "mockNanoId123"), | ||
})); | ||
|
||
jest.mock("bcrypt", () => ({ | ||
hash: jest.fn(() => Promise.resolve("hashedPassword123")), | ||
compare: jest.fn(() => Promise.resolve(true)), | ||
})); | ||
|
||
describe("AuthService", () => { | ||
let authService: AuthService; | ||
let userModel: any; | ||
let jwtService: JwtService; | ||
|
||
const mockUserModel = { | ||
create: jest.fn(), | ||
findOne: jest.fn(), | ||
}; | ||
|
||
const mockJwtService = { | ||
sign: jest.fn(), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
AuthService, | ||
{ provide: getModelToken("User"), useValue: mockUserModel }, | ||
{ provide: JwtService, useValue: mockJwtService }, | ||
], | ||
}).compile(); | ||
|
||
authService = module.get<AuthService>(AuthService); | ||
userModel = module.get(getModelToken("User")); | ||
jwtService = module.get<JwtService>(JwtService); | ||
}); | ||
|
||
it("should be defined", () => { | ||
expect(authService).toBeDefined(); | ||
}); | ||
|
||
describe("register", () => { | ||
it("should create a new user with a nanoid as id", async () => { | ||
// nanoid mock 설정 | ||
const mockId = "mockNanoId123"; | ||
(nanoid as jest.Mock).mockReturnValue(mockId); | ||
|
||
const hashedPassword = "hashedPassword123"; | ||
jest.spyOn(bcrypt, "hash").mockResolvedValue(hashedPassword); | ||
|
||
const mockUser = { | ||
id: mockId, | ||
email: "[email protected]", | ||
password: hashedPassword, | ||
name: "Test User", | ||
}; | ||
|
||
userModel.create.mockResolvedValue(mockUser); | ||
|
||
const result = await authService.register("[email protected]", "password123", "Test User"); | ||
|
||
expect(nanoid).toHaveBeenCalled(); // nanoid 호출 확인 | ||
expect(userModel.create).toHaveBeenCalledWith({ | ||
id: mockId, | ||
email: "[email protected]", | ||
password: hashedPassword, | ||
name: "Test User", | ||
}); | ||
expect(result).toEqual(mockUser); | ||
}); | ||
}); | ||
|
||
describe("validateUser", () => { | ||
it("should return the user if credentials are valid", async () => { | ||
const mockUser = { | ||
id: "mockNanoId123", | ||
email: "[email protected]", | ||
password: "hashedPassword123", | ||
}; | ||
userModel.findOne.mockResolvedValue(mockUser); | ||
jest.spyOn(bcrypt, "compare").mockResolvedValue(true); | ||
|
||
const result = await authService.validateUser("[email protected]", "password123"); | ||
|
||
expect(userModel.findOne).toHaveBeenCalledWith({ email: "[email protected]" }); | ||
expect(result).toEqual(mockUser); | ||
}); | ||
|
||
it("should return null if credentials are invalid", async () => { | ||
userModel.findOne.mockResolvedValue(null); | ||
|
||
const result = await authService.validateUser("[email protected]", "password123"); | ||
|
||
expect(result).toBeNull(); | ||
}); | ||
}); | ||
|
||
describe("login", () => { | ||
it("should return a JWT token", async () => { | ||
const mockUser = { id: "mockNanoId123", email: "[email protected]" }; | ||
mockJwtService.sign.mockReturnValue("mockJwtToken"); | ||
|
||
const result = await authService.login(mockUser); | ||
|
||
expect(jwtService.sign).toHaveBeenCalledWith({ | ||
sub: mockUser.id, | ||
email: mockUser.email, | ||
}); | ||
expect(result).toEqual({ accessToken: "mockJwtToken" }); | ||
}); | ||
}); | ||
}); |
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,39 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
import { InjectModel } from "@nestjs/mongoose"; | ||
import { Model } from "mongoose"; | ||
import { User, UserDocument } from "./schemas/user.schema"; | ||
import * as bcrypt from "bcrypt"; | ||
import { JwtService } from "@nestjs/jwt"; | ||
|
||
@Injectable() | ||
export class AuthService { | ||
constructor( | ||
@InjectModel(User.name) private userModel: Model<UserDocument>, | ||
private jwtService: JwtService, | ||
) {} | ||
|
||
async register(email: string, password: string, name: string): Promise<User> { | ||
const hashedPassword = await bcrypt.hash(password, 10); | ||
const newUser = new this.userModel({ | ||
email, | ||
password: hashedPassword, | ||
name, | ||
}); | ||
return newUser.save(); | ||
} | ||
|
||
async validateUser(email: string, password: string): Promise<User | null> { | ||
const user = await this.userModel.findOne({ email }); | ||
if (user && (await bcrypt.compare(password, user.password))) { | ||
return user; | ||
} | ||
return null; | ||
} | ||
|
||
async login(user: { id: string; email: string }) { | ||
const payload = { sub: user.id, email: user.email }; | ||
return { | ||
accessToken: this.jwtService.sign(payload), | ||
}; | ||
} | ||
} |
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,5 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
import { AuthGuard } from "@nestjs/passport"; | ||
|
||
@Injectable() | ||
export class JwtAuthGuard extends AuthGuard("jwt") {} |
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,18 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
import { PassportStrategy } from "@nestjs/passport"; | ||
import { ExtractJwt, Strategy } from "passport-jwt"; | ||
|
||
@Injectable() | ||
export class JwtStrategy extends PassportStrategy(Strategy) { | ||
constructor() { | ||
super({ | ||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | ||
ignoreExpiration: false, | ||
secretOrKey: process.env.JWT_SECRET, | ||
}); | ||
} | ||
|
||
async validate(payload: any) { | ||
return { userId: payload.sub, email: payload.email }; | ||
} | ||
} |
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,22 @@ | ||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; | ||
import { Document } from "mongoose"; | ||
import { nanoid } from "nanoid"; | ||
|
||
export type UserDocument = User & Document; | ||
|
||
@Schema() | ||
export class User { | ||
@Prop({ required: true, unique: true, default: () => nanoid() }) | ||
id: string; | ||
|
||
@Prop({ required: true, unique: true }) | ||
email: string; | ||
|
||
@Prop({ required: true }) | ||
password: string; | ||
|
||
@Prop({ required: true }) | ||
name: string; | ||
} | ||
|
||
export const UserSchema = SchemaFactory.createForClass(User); |
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