mirror of
https://github.com/cna-bootcamp/lifesub.git
synced 2025-12-06 08:06:24 +00:00
add testcode
This commit is contained in:
parent
d7ca5994b4
commit
2f672a8ea5
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
7
.idea/compiler.xml
generated
7
.idea/compiler.xml
generated
@ -8,11 +8,16 @@
|
|||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.36/5a30490a6e14977d97d9c73c924c1f1b5311ea95/lombok-1.18.36.jar" />
|
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.36/5a30490a6e14977d97d9c73c924c1f1b5311ea95/lombok-1.18.36.jar" />
|
||||||
</processorPath>
|
</processorPath>
|
||||||
|
<module name="lifesub.recommend.test" />
|
||||||
|
<module name="lifesub.common.main" />
|
||||||
|
<module name="lifesub.mysub-infra.test" />
|
||||||
<module name="lifesub.mysub-infra.main" />
|
<module name="lifesub.mysub-infra.main" />
|
||||||
|
<module name="lifesub.common.test" />
|
||||||
|
<module name="lifesub.member.test" />
|
||||||
<module name="lifesub.mysub-biz.main" />
|
<module name="lifesub.mysub-biz.main" />
|
||||||
<module name="lifesub.recommend.main" />
|
<module name="lifesub.recommend.main" />
|
||||||
<module name="lifesub.common.main" />
|
|
||||||
<module name="lifesub.member.main" />
|
<module name="lifesub.member.main" />
|
||||||
|
<module name="lifesub.mysub-biz.test" />
|
||||||
</profile>
|
</profile>
|
||||||
</annotationProcessing>
|
</annotationProcessing>
|
||||||
<bytecodeTargetLevel target="17" />
|
<bytecodeTargetLevel target="17" />
|
||||||
|
|||||||
39
build.gradle
39
build.gradle
@ -20,19 +20,45 @@ subprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// Spring Boot Starters
|
||||||
implementation 'org.springframework.boot:spring-boot-starter'
|
implementation 'org.springframework.boot:spring-boot-starter'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-aop' // AOP: 로깅 처리 자동화를 위해 사용
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
implementation 'com.google.code.gson:gson'
|
||||||
|
|
||||||
// Lombok
|
// Lombok
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
|
||||||
// Test
|
// Test Dependencies
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||||
|
testImplementation 'org.mockito:mockito-core'
|
||||||
|
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||||
|
|
||||||
|
// Lombok for Tests
|
||||||
|
testCompileOnly 'org.projectlombok:lombok'
|
||||||
|
testAnnotationProcessor 'org.projectlombok:lombok'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Configuration
|
||||||
|
sourceSets {
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDirs = ['src/test/java']
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
|
include '**/*Test.class'
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +76,15 @@ configure(subprojects.findAll { !it.name.endsWith('-biz') && it.name != 'common'
|
|||||||
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
|
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
|
||||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
||||||
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
|
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
|
||||||
// AOP: 로깅 처리 자동화를 위해 사용
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-aop'
|
|
||||||
// Swagger
|
// Swagger
|
||||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
|
||||||
|
|
||||||
|
// Test Containers
|
||||||
|
testImplementation 'org.testcontainers:postgresql'
|
||||||
|
|
||||||
|
// WebFlux for WebMvc Testing
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
82
design/Common 클래스설계서
Normal file
82
design/Common 클래스설계서
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
!theme mono
|
||||||
|
title Common Module - Class Diagram
|
||||||
|
|
||||||
|
package "com.unicorn.lifesub.common" {
|
||||||
|
package "dto" {
|
||||||
|
class ApiResponse<T> {
|
||||||
|
-status: int
|
||||||
|
-message: String
|
||||||
|
-data: T
|
||||||
|
-timestamp: LocalDateTime
|
||||||
|
+ApiResponse(status: int, message: String, data: T)
|
||||||
|
+{static} success(data: T): ApiResponse<T>
|
||||||
|
+{static} error(errorCode: ErrorCode): ApiResponse<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
class JwtTokenDTO {
|
||||||
|
-accessToken: String
|
||||||
|
-refreshToken: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package "exception" {
|
||||||
|
class BusinessException {
|
||||||
|
-errorCode: ErrorCode
|
||||||
|
+BusinessException(errorCode: ErrorCode)
|
||||||
|
+getErrorCode(): ErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
class InfraException {
|
||||||
|
-errorCode: ErrorCode
|
||||||
|
+InfraException(errorCode: ErrorCode)
|
||||||
|
+getErrorCode(): ErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ErrorCode {
|
||||||
|
INVALID_INPUT_VALUE(100, "Invalid input value")
|
||||||
|
INTERNAL_SERVER_ERROR(110, "Internal server error")
|
||||||
|
MEMBER_NOT_FOUND(200, "Member not found")
|
||||||
|
INVALID_CREDENTIALS(210, "Invalid credentials")
|
||||||
|
TOKEN_EXPIRED(220, "Token expired")
|
||||||
|
SIGNATURE_VERIFICATION_EXCEPTION(230, "서명 검증 실패")
|
||||||
|
ALGORITHM_MISMATCH_EXCEPTION(240, "알고리즘 불일치")
|
||||||
|
INVALID_CLAIM_EXCEPTION(250, "유효하지 않은 클레임")
|
||||||
|
SUBSCRIPTION_NOT_FOUND(300, "Subscription not found")
|
||||||
|
ALREADY_SUBSCRIBED(310, "Already subscribed to this service")
|
||||||
|
NO_SPENDING_DATA(400, "No spending data found")
|
||||||
|
NO_RECOMMENDATION_DATA(410, "추천 구독 카테고리 없음")
|
||||||
|
UNDIFINED_ERROR(0, "정의되지 않은 에러")
|
||||||
|
--
|
||||||
|
-status: int
|
||||||
|
-message: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package "entity" {
|
||||||
|
abstract class BaseTimeEntity {
|
||||||
|
-createdAt: LocalDateTime
|
||||||
|
-updatedAt: LocalDateTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package "aop" {
|
||||||
|
class LoggingAspect {
|
||||||
|
-gson: Gson
|
||||||
|
+logMethodStart(joinPoint: JoinPoint): void
|
||||||
|
+logMethodEnd(joinPoint: JoinPoint, result: Object): void
|
||||||
|
+logMethodException(joinPoint: JoinPoint, exception: Exception): void
|
||||||
|
-getArgumentString(args: Object[]): String
|
||||||
|
-getResultString(result: Object): String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package "config" {
|
||||||
|
class JpaConfig {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
' Relationships
|
||||||
|
BusinessException --> ErrorCode
|
||||||
|
InfraException --> ErrorCode
|
||||||
|
LoggingAspect ..> ApiResponse : uses
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
|
||||||
|
<title>Test results - CustomUserDetailsServiceTest</title>
|
||||||
|
<link href="../css/base-style.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<script src="../js/report.js" type="text/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<h1>CustomUserDetailsServiceTest</h1>
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<a href="../index.html">all</a> >
|
||||||
|
<a href="../packages/com.unicorn.lifesub.member.test.unit.config.jwt.html">com.unicorn.lifesub.member.test.unit.config.jwt</a> > CustomUserDetailsServiceTest</div>
|
||||||
|
<div id="summary">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="summaryGroup">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="tests">
|
||||||
|
<div class="counter">3</div>
|
||||||
|
<p>tests</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="failures">
|
||||||
|
<div class="counter">0</div>
|
||||||
|
<p>failures</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="ignored">
|
||||||
|
<div class="counter">0</div>
|
||||||
|
<p>ignored</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="duration">
|
||||||
|
<div class="counter">1.064s</div>
|
||||||
|
<p>duration</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox success" id="successRate">
|
||||||
|
<div class="percent">100%</div>
|
||||||
|
<p>successful</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="tabs">
|
||||||
|
<ul class="tabLinks">
|
||||||
|
<li>
|
||||||
|
<a href="#tab0">Tests</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div id="tab0" class="tab">
|
||||||
|
<h2>Tests</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Test</th>
|
||||||
|
<th>Method name</th>
|
||||||
|
<th>Duration</th>
|
||||||
|
<th>Result</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td class="success">givenExistingUserId_whenLoadUser_thenReturnUserDetails</td>
|
||||||
|
<td class="success">givenExistingUserId_whenLoadUser_thenReturnUserDetails()</td>
|
||||||
|
<td class="success">0.008s</td>
|
||||||
|
<td class="success">passed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="success">givenNonExistentUserId_whenLoadUser_thenThrowException</td>
|
||||||
|
<td class="success">givenNonExistentUserId_whenLoadUser_thenThrowException()</td>
|
||||||
|
<td class="success">0.864s</td>
|
||||||
|
<td class="success">passed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="success">givenUserWithRoles_whenLoadUser_thenMapAuthoritiesCorrectly</td>
|
||||||
|
<td class="success">givenUserWithRoles_whenLoadUser_thenMapAuthoritiesCorrectly()</td>
|
||||||
|
<td class="success">0.192s</td>
|
||||||
|
<td class="success">passed</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<p>
|
||||||
|
<div>
|
||||||
|
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
|
||||||
|
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
|
||||||
|
</label>
|
||||||
|
</div>Generated by
|
||||||
|
<a href="http://www.gradle.org">Gradle 8.4</a> at 2025. 2. 14. 오후 4:50:22</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
179
member/build/reports/tests/test/css/base-style.css
Normal file
179
member/build/reports/tests/test/css/base-style.css
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, a, a:visited {
|
||||||
|
color: #303030;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
padding-left: 50px;
|
||||||
|
padding-right: 50px;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content h1 {
|
||||||
|
font-size: 160%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
margin-top: 100px;
|
||||||
|
font-size: 80%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer, #footer a {
|
||||||
|
color: #a0a0a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#line-wrapping-toggle {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#label-for-line-wrapping-toggle {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tabLinks {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
min-width: 800px;
|
||||||
|
width: auto !important;
|
||||||
|
width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tabLinks li {
|
||||||
|
float: left;
|
||||||
|
height: 100%;
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
-moz-border-radius: 7px;
|
||||||
|
border-radius: 7px;
|
||||||
|
margin-right: 25px;
|
||||||
|
border: solid 1px #d4d4d4;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tabLinks li:hover {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tabLinks li.selected {
|
||||||
|
background-color: #c5f0f5;
|
||||||
|
border-color: #c5f0f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tabLinks a {
|
||||||
|
font-size: 120%;
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tabLinks li h2 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab {
|
||||||
|
}
|
||||||
|
|
||||||
|
div.selected {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.deselected {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab table {
|
||||||
|
min-width: 350px;
|
||||||
|
width: auto !important;
|
||||||
|
width: 350px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab th, div.tab table {
|
||||||
|
border-bottom: solid #d0d0d0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab th {
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab td {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: 6em;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab td:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tab td.numeric, div.tab th.numeric {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.code {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 0em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.code pre {
|
||||||
|
font-size: 11pt;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border: solid 1px #d0d0d0;
|
||||||
|
min-width: 700px;
|
||||||
|
width: auto !important;
|
||||||
|
width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.wrapped pre {
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
84
member/build/reports/tests/test/css/style.css
Normal file
84
member/build/reports/tests/test/css/style.css
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
#summary {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#summary table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#summary td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs, .breadcrumbs a {
|
||||||
|
color: #606060;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoBox {
|
||||||
|
width: 110px;
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoBox p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter, .percent {
|
||||||
|
font-size: 120%;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#duration {
|
||||||
|
width: 125px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#successRate, .summaryGroup {
|
||||||
|
border: solid 2px #d0d0d0;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#successRate {
|
||||||
|
width: 140px;
|
||||||
|
margin-left: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#successRate .percent {
|
||||||
|
font-size: 180%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success, .success a {
|
||||||
|
color: #008000;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.success, #successRate.success {
|
||||||
|
background-color: #bbd9bb;
|
||||||
|
border-color: #008000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failures, .failures a {
|
||||||
|
color: #b60808;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skipped, .skipped a {
|
||||||
|
color: #c09853;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.failures, #successRate.failures {
|
||||||
|
background-color: #ecdada;
|
||||||
|
border-color: #b60808;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.linkList {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.linkList li {
|
||||||
|
list-style: none;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
133
member/build/reports/tests/test/index.html
Normal file
133
member/build/reports/tests/test/index.html
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
|
||||||
|
<title>Test results - Test Summary</title>
|
||||||
|
<link href="css/base-style.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<link href="css/style.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<script src="js/report.js" type="text/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<h1>Test Summary</h1>
|
||||||
|
<div id="summary">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="summaryGroup">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="tests">
|
||||||
|
<div class="counter">3</div>
|
||||||
|
<p>tests</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="failures">
|
||||||
|
<div class="counter">0</div>
|
||||||
|
<p>failures</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="ignored">
|
||||||
|
<div class="counter">0</div>
|
||||||
|
<p>ignored</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="duration">
|
||||||
|
<div class="counter">1.064s</div>
|
||||||
|
<p>duration</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox success" id="successRate">
|
||||||
|
<div class="percent">100%</div>
|
||||||
|
<p>successful</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="tabs">
|
||||||
|
<ul class="tabLinks">
|
||||||
|
<li>
|
||||||
|
<a href="#tab0">Packages</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#tab1">Classes</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div id="tab0" class="tab">
|
||||||
|
<h2>Packages</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Package</th>
|
||||||
|
<th>Tests</th>
|
||||||
|
<th>Failures</th>
|
||||||
|
<th>Ignored</th>
|
||||||
|
<th>Duration</th>
|
||||||
|
<th>Success rate</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="success">
|
||||||
|
<a href="packages/com.unicorn.lifesub.member.test.unit.config.jwt.html">com.unicorn.lifesub.member.test.unit.config.jwt</a>
|
||||||
|
</td>
|
||||||
|
<td>3</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>1.064s</td>
|
||||||
|
<td class="success">100%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="tab1" class="tab">
|
||||||
|
<h2>Classes</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Class</th>
|
||||||
|
<th>Tests</th>
|
||||||
|
<th>Failures</th>
|
||||||
|
<th>Ignored</th>
|
||||||
|
<th>Duration</th>
|
||||||
|
<th>Success rate</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="success">
|
||||||
|
<a href="classes/com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest.html">com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest</a>
|
||||||
|
</td>
|
||||||
|
<td>3</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>1.064s</td>
|
||||||
|
<td class="success">100%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<p>
|
||||||
|
<div>
|
||||||
|
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
|
||||||
|
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
|
||||||
|
</label>
|
||||||
|
</div>Generated by
|
||||||
|
<a href="http://www.gradle.org">Gradle 8.4</a> at 2025. 2. 14. 오후 4:50:22</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
194
member/build/reports/tests/test/js/report.js
Normal file
194
member/build/reports/tests/test/js/report.js
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
(function (window, document) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var tabs = {};
|
||||||
|
|
||||||
|
function changeElementClass(element, classValue) {
|
||||||
|
if (element.getAttribute("className")) {
|
||||||
|
element.setAttribute("className", classValue);
|
||||||
|
} else {
|
||||||
|
element.setAttribute("class", classValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClassAttribute(element) {
|
||||||
|
if (element.getAttribute("className")) {
|
||||||
|
return element.getAttribute("className");
|
||||||
|
} else {
|
||||||
|
return element.getAttribute("class");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addClass(element, classValue) {
|
||||||
|
changeElementClass(element, getClassAttribute(element) + " " + classValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeClass(element, classValue) {
|
||||||
|
changeElementClass(element, getClassAttribute(element).replace(classValue, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTabs() {
|
||||||
|
var container = document.getElementById("tabs");
|
||||||
|
|
||||||
|
tabs.tabs = findTabs(container);
|
||||||
|
tabs.titles = findTitles(tabs.tabs);
|
||||||
|
tabs.headers = findHeaders(container);
|
||||||
|
tabs.select = select;
|
||||||
|
tabs.deselectAll = deselectAll;
|
||||||
|
tabs.select(0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCheckBox() {
|
||||||
|
return document.getElementById("line-wrapping-toggle");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabelForCheckBox() {
|
||||||
|
return document.getElementById("label-for-line-wrapping-toggle");
|
||||||
|
}
|
||||||
|
|
||||||
|
function findCodeBlocks() {
|
||||||
|
var spans = document.getElementById("tabs").getElementsByTagName("span");
|
||||||
|
var codeBlocks = [];
|
||||||
|
for (var i = 0; i < spans.length; ++i) {
|
||||||
|
if (spans[i].className.indexOf("code") >= 0) {
|
||||||
|
codeBlocks.push(spans[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codeBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forAllCodeBlocks(operation) {
|
||||||
|
var codeBlocks = findCodeBlocks();
|
||||||
|
|
||||||
|
for (var i = 0; i < codeBlocks.length; ++i) {
|
||||||
|
operation(codeBlocks[i], "wrapped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLineWrapping() {
|
||||||
|
var checkBox = getCheckBox();
|
||||||
|
|
||||||
|
if (checkBox.checked) {
|
||||||
|
forAllCodeBlocks(addClass);
|
||||||
|
} else {
|
||||||
|
forAllCodeBlocks(removeClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initControls() {
|
||||||
|
if (findCodeBlocks().length > 0) {
|
||||||
|
var checkBox = getCheckBox();
|
||||||
|
var label = getLabelForCheckBox();
|
||||||
|
|
||||||
|
checkBox.onclick = toggleLineWrapping;
|
||||||
|
checkBox.checked = false;
|
||||||
|
|
||||||
|
removeClass(label, "hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchTab() {
|
||||||
|
var id = this.id.substr(1);
|
||||||
|
|
||||||
|
for (var i = 0; i < tabs.tabs.length; i++) {
|
||||||
|
if (tabs.tabs[i].id === id) {
|
||||||
|
tabs.select(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(i) {
|
||||||
|
this.deselectAll();
|
||||||
|
|
||||||
|
changeElementClass(this.tabs[i], "tab selected");
|
||||||
|
changeElementClass(this.headers[i], "selected");
|
||||||
|
|
||||||
|
while (this.headers[i].firstChild) {
|
||||||
|
this.headers[i].removeChild(this.headers[i].firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
var h2 = document.createElement("H2");
|
||||||
|
|
||||||
|
h2.appendChild(document.createTextNode(this.titles[i]));
|
||||||
|
this.headers[i].appendChild(h2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deselectAll() {
|
||||||
|
for (var i = 0; i < this.tabs.length; i++) {
|
||||||
|
changeElementClass(this.tabs[i], "tab deselected");
|
||||||
|
changeElementClass(this.headers[i], "deselected");
|
||||||
|
|
||||||
|
while (this.headers[i].firstChild) {
|
||||||
|
this.headers[i].removeChild(this.headers[i].firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = document.createElement("A");
|
||||||
|
|
||||||
|
a.setAttribute("id", "ltab" + i);
|
||||||
|
a.setAttribute("href", "#tab" + i);
|
||||||
|
a.onclick = switchTab;
|
||||||
|
a.appendChild(document.createTextNode(this.titles[i]));
|
||||||
|
|
||||||
|
this.headers[i].appendChild(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTabs(container) {
|
||||||
|
return findChildElements(container, "DIV", "tab");
|
||||||
|
}
|
||||||
|
|
||||||
|
function findHeaders(container) {
|
||||||
|
var owner = findChildElements(container, "UL", "tabLinks");
|
||||||
|
return findChildElements(owner[0], "LI", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTitles(tabs) {
|
||||||
|
var titles = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < tabs.length; i++) {
|
||||||
|
var tab = tabs[i];
|
||||||
|
var header = findChildElements(tab, "H2", null)[0];
|
||||||
|
|
||||||
|
header.parentNode.removeChild(header);
|
||||||
|
|
||||||
|
if (header.innerText) {
|
||||||
|
titles.push(header.innerText);
|
||||||
|
} else {
|
||||||
|
titles.push(header.textContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findChildElements(container, name, targetClass) {
|
||||||
|
var elements = [];
|
||||||
|
var children = container.childNodes;
|
||||||
|
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
var child = children.item(i);
|
||||||
|
|
||||||
|
if (child.nodeType === 1 && child.nodeName === name) {
|
||||||
|
if (targetClass && child.className.indexOf(targetClass) < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point.
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
initTabs();
|
||||||
|
initControls();
|
||||||
|
};
|
||||||
|
} (window, window.document));
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
|
||||||
|
<title>Test results - Package com.unicorn.lifesub.member.test.unit.config.jwt</title>
|
||||||
|
<link href="../css/base-style.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<script src="../js/report.js" type="text/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<h1>Package com.unicorn.lifesub.member.test.unit.config.jwt</h1>
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<a href="../index.html">all</a> > com.unicorn.lifesub.member.test.unit.config.jwt</div>
|
||||||
|
<div id="summary">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="summaryGroup">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="tests">
|
||||||
|
<div class="counter">3</div>
|
||||||
|
<p>tests</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="failures">
|
||||||
|
<div class="counter">0</div>
|
||||||
|
<p>failures</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="ignored">
|
||||||
|
<div class="counter">0</div>
|
||||||
|
<p>ignored</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox" id="duration">
|
||||||
|
<div class="counter">1.064s</div>
|
||||||
|
<p>duration</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="infoBox success" id="successRate">
|
||||||
|
<div class="percent">100%</div>
|
||||||
|
<p>successful</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="tabs">
|
||||||
|
<ul class="tabLinks">
|
||||||
|
<li>
|
||||||
|
<a href="#tab0">Classes</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div id="tab0" class="tab">
|
||||||
|
<h2>Classes</h2>
|
||||||
|
<table>
|
||||||
|
<thread>
|
||||||
|
<tr>
|
||||||
|
<th>Class</th>
|
||||||
|
<th>Tests</th>
|
||||||
|
<th>Failures</th>
|
||||||
|
<th>Ignored</th>
|
||||||
|
<th>Duration</th>
|
||||||
|
<th>Success rate</th>
|
||||||
|
</tr>
|
||||||
|
</thread>
|
||||||
|
<tr>
|
||||||
|
<td class="success">
|
||||||
|
<a href="../classes/com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest.html">CustomUserDetailsServiceTest</a>
|
||||||
|
</td>
|
||||||
|
<td>3</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>1.064s</td>
|
||||||
|
<td class="success">100%</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<p>
|
||||||
|
<div>
|
||||||
|
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
|
||||||
|
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
|
||||||
|
</label>
|
||||||
|
</div>Generated by
|
||||||
|
<a href="http://www.gradle.org">Gradle 8.4</a> at 2025. 2. 14. 오후 4:50:22</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuite name="com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest" tests="3" skipped="0" failures="0" errors="0" timestamp="2025-02-14T07:50:21" hostname="ihaegyeong-ui-MacBookAir.local" time="1.069">
|
||||||
|
<properties/>
|
||||||
|
<testcase name="givenNonExistentUserId_whenLoadUser_thenThrowException" classname="com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest" time="0.864"/>
|
||||||
|
<testcase name="givenUserWithRoles_whenLoadUser_thenMapAuthoritiesCorrectly" classname="com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest" time="0.192"/>
|
||||||
|
<testcase name="givenExistingUserId_whenLoadUser_thenReturnUserDetails" classname="com.unicorn.lifesub.member.test.unit.config.jwt.CustomUserDetailsServiceTest" time="0.008"/>
|
||||||
|
<system-out><![CDATA[]]></system-out>
|
||||||
|
<system-err><![CDATA[]]></system-err>
|
||||||
|
</testsuite>
|
||||||
0
member/build/test-results/test/binary/output.bin
Normal file
0
member/build/test-results/test/binary/output.bin
Normal file
BIN
member/build/test-results/test/binary/output.bin.idx
Normal file
BIN
member/build/test-results/test/binary/output.bin.idx
Normal file
Binary file not shown.
BIN
member/build/test-results/test/binary/results.bin
Normal file
BIN
member/build/test-results/test/binary/results.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
member/build/tmp/compileTestJava/previous-compilation-data.bin
Normal file
BIN
member/build/tmp/compileTestJava/previous-compilation-data.bin
Normal file
Binary file not shown.
@ -2,8 +2,10 @@ package com.unicorn.lifesub.member.dto;
|
|||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
public class LoginRequest {
|
public class LoginRequest {
|
||||||
@NotBlank(message = "사용자 ID는 필수입니다.")
|
@NotBlank(message = "사용자 ID는 필수입니다.")
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|||||||
@ -2,8 +2,10 @@ package com.unicorn.lifesub.member.dto;
|
|||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
public class LogoutRequest {
|
public class LogoutRequest {
|
||||||
@NotBlank(message = "사용자 ID는 필수입니다.")
|
@NotBlank(message = "사용자 ID는 필수입니다.")
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|||||||
@ -0,0 +1,122 @@
|
|||||||
|
package com.unicorn.lifesub.member.test.unit.config.jwt;
|
||||||
|
|
||||||
|
import com.unicorn.lifesub.member.config.jwt.CustomUserDetailsService;
|
||||||
|
import com.unicorn.lifesub.member.repository.entity.MemberEntity;
|
||||||
|
import com.unicorn.lifesub.member.repository.jpa.MemberRepository;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사용자 상세 정보 서비스 테스트 클래스
|
||||||
|
* Spring Security의 UserDetailsService 구현체 검증
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class CustomUserDetailsServiceTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private CustomUserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MemberRepository memberRepository;
|
||||||
|
|
||||||
|
// 테스트용 상수 정의
|
||||||
|
private static final String TEST_USER_ID = "testUser";
|
||||||
|
private static final String TEST_PASSWORD = "testPassword";
|
||||||
|
private static final String TEST_USER_NAME = "Test User";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사용자 조회 성공 케이스 테스트
|
||||||
|
* 존재하는 사용자 ID로 조회 시 UserDetails 객체가 정상적으로 반환되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenExistingUserId_whenLoadUser_thenReturnUserDetails")
|
||||||
|
void givenExistingUserId_whenLoadUser_thenReturnUserDetails() {
|
||||||
|
// Given
|
||||||
|
MemberEntity memberEntity = createTestMemberEntity();
|
||||||
|
given(memberRepository.findByUserId(TEST_USER_ID)).willReturn(Optional.of(memberEntity));
|
||||||
|
|
||||||
|
// When
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(TEST_USER_ID);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(userDetails).isNotNull();
|
||||||
|
assertThat(userDetails.getUsername()).isEqualTo(TEST_USER_ID);
|
||||||
|
assertThat(userDetails.getPassword()).isEqualTo(TEST_PASSWORD);
|
||||||
|
assertThat(userDetails.getAuthorities()).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사용자 조회 실패 케이스 테스트
|
||||||
|
* 존재하지 않는 사용자 ID로 조회 시 적절한 예외가 발생하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenNonExistentUserId_whenLoadUser_thenThrowException")
|
||||||
|
void givenNonExistentUserId_whenLoadUser_thenThrowException() {
|
||||||
|
// Given
|
||||||
|
String nonExistentUserId = "nonexistent";
|
||||||
|
given(memberRepository.findByUserId(nonExistentUserId)).willReturn(Optional.empty());
|
||||||
|
|
||||||
|
// When & Then
|
||||||
|
assertThrows(UsernameNotFoundException.class, () ->
|
||||||
|
userDetailsService.loadUserByUsername(nonExistentUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사용자 권한 매핑 테스트
|
||||||
|
* 사용자의 역할이 Spring Security 권한으로 올바르게 매핑되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenUserWithRoles_whenLoadUser_thenMapAuthoritiesCorrectly")
|
||||||
|
void givenUserWithRoles_whenLoadUser_thenMapAuthoritiesCorrectly() {
|
||||||
|
// Given
|
||||||
|
MemberEntity memberEntity = createTestMemberEntityWithMultipleRoles();
|
||||||
|
given(memberRepository.findByUserId(TEST_USER_ID)).willReturn(Optional.of(memberEntity));
|
||||||
|
|
||||||
|
// When
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(TEST_USER_ID);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(userDetails.getAuthorities()).hasSize(2);
|
||||||
|
assertThat(userDetails.getAuthorities())
|
||||||
|
.extracting("authority")
|
||||||
|
.containsExactlyInAnyOrder("USER", "ADMIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 테스트 헬퍼 메서드
|
||||||
|
private MemberEntity createTestMemberEntity() {
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
return MemberEntity.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MemberEntity createTestMemberEntityWithMultipleRoles() {
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
roles.add("ADMIN");
|
||||||
|
return MemberEntity.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
package com.unicorn.lifesub.member.test.unit.config.jwt;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.unicorn.lifesub.common.dto.JwtTokenDTO;
|
||||||
|
import com.unicorn.lifesub.common.exception.InfraException;
|
||||||
|
import com.unicorn.lifesub.member.config.jwt.JwtTokenProvider;
|
||||||
|
import com.unicorn.lifesub.member.repository.entity.MemberEntity;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT 토큰 제공자 테스트 클래스
|
||||||
|
* 토큰 생성, 검증, 파싱 등의 기능을 검증
|
||||||
|
*/
|
||||||
|
class JwtTokenProviderTest {
|
||||||
|
|
||||||
|
private JwtTokenProvider jwtTokenProvider;
|
||||||
|
private static final String SECRET_KEY = "test-secret-key";
|
||||||
|
private static final long ACCESS_TOKEN_VALIDITY = 3600000;
|
||||||
|
private static final long REFRESH_TOKEN_VALIDITY = 86400000;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jwtTokenProvider = new JwtTokenProvider(SECRET_KEY, ACCESS_TOKEN_VALIDITY, REFRESH_TOKEN_VALIDITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 토큰 생성 테스트
|
||||||
|
* 유효한 사용자 정보로 JWT 토큰이 정상적으로 생성되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenValidMember_whenCreateToken_thenSuccess")
|
||||||
|
void givenValidMember_whenCreateToken_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
MemberEntity member = createTestMemberEntity();
|
||||||
|
Set<SimpleGrantedAuthority> authorities = Collections.singleton(
|
||||||
|
new SimpleGrantedAuthority("ROLE_USER"));
|
||||||
|
|
||||||
|
// When
|
||||||
|
JwtTokenDTO tokens = jwtTokenProvider.createToken(member, authorities);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(tokens).isNotNull();
|
||||||
|
assertThat(tokens.getAccessToken()).isNotNull();
|
||||||
|
assertThat(tokens.getRefreshToken()).isNotNull();
|
||||||
|
assertThat(jwtTokenProvider.validateToken(tokens.getAccessToken())).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 토큰 검증 테스트
|
||||||
|
* 유효한 토큰과 유효하지 않은 토큰에 대한 검증이 정상적으로 동작하는지 확인
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenToken_whenValidate_thenSuccess")
|
||||||
|
void givenToken_whenValidate_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
String token = createValidToken();
|
||||||
|
|
||||||
|
// When & Then
|
||||||
|
assertThat(jwtTokenProvider.validateToken(token)).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 인증 정보 추출 테스트
|
||||||
|
* JWT 토큰에서 인증 정보가 정상적으로 추출되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenValidToken_whenGetAuthentication_thenSuccess")
|
||||||
|
void givenValidToken_whenGetAuthentication_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
String token = createValidToken();
|
||||||
|
|
||||||
|
// When
|
||||||
|
Authentication authentication = jwtTokenProvider.getAuthentication(token);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(authentication).isNotNull();
|
||||||
|
assertThat(authentication.isAuthenticated()).isTrue();
|
||||||
|
assertThat(authentication.getAuthorities()).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 토큰 추출 테스트
|
||||||
|
* HTTP 요청 헤더에서 토큰이 정상적으로 추출되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenRequest_whenResolveToken_thenSuccess")
|
||||||
|
void givenRequest_whenResolveToken_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||||
|
String token = "test-token";
|
||||||
|
when(request.getHeader("Authorization")).thenReturn("Bearer " + token);
|
||||||
|
|
||||||
|
// When
|
||||||
|
String resolvedToken = jwtTokenProvider.resolveToken(request);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(resolvedToken).isEqualTo(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 유효하지 않은 토큰 검증 테스트
|
||||||
|
* 잘못된 형식의 토큰에 대해 적절한 예외가 발생하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenInvalidToken_whenValidate_thenThrowException")
|
||||||
|
void givenInvalidToken_whenValidate_thenThrowException() {
|
||||||
|
// Given
|
||||||
|
String invalidToken = "invalid-token";
|
||||||
|
|
||||||
|
// When & Then
|
||||||
|
assertThrows(InfraException.class, () -> jwtTokenProvider.validateToken(invalidToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 테스트 헬퍼 메서드
|
||||||
|
private MemberEntity createTestMemberEntity() {
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
return MemberEntity.builder()
|
||||||
|
.userId("testUser")
|
||||||
|
.userName("Test User")
|
||||||
|
.password("password")
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createValidToken() {
|
||||||
|
Algorithm algorithm = Algorithm.HMAC512(SECRET_KEY);
|
||||||
|
return JWT.create()
|
||||||
|
.withSubject("testUser")
|
||||||
|
.withClaim("auth", Collections.singletonList("ROLE_USER"))
|
||||||
|
.sign(algorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
package com.unicorn.lifesub.member.test.unit.domain;
|
||||||
|
|
||||||
|
import com.unicorn.lifesub.member.domain.Member;
|
||||||
|
import com.unicorn.lifesub.member.repository.entity.MemberEntity;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Member 도메인 객체 테스트 클래스
|
||||||
|
* 도메인 객체의 생성 및 엔티티 변환 로직을 검증
|
||||||
|
*/
|
||||||
|
class MemberTest {
|
||||||
|
|
||||||
|
// 테스트용 상수 정의
|
||||||
|
private static final String TEST_USER_ID = "testUser";
|
||||||
|
private static final String TEST_USER_NAME = "Test User";
|
||||||
|
private static final String TEST_PASSWORD = "testPassword";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Member 객체 생성 테스트
|
||||||
|
* Builder 패턴을 사용한 Member 객체 생성이 정상적으로 동작하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenMemberInfo_whenBuildMember_thenSuccess")
|
||||||
|
void givenMemberInfo_whenBuildMember_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
|
||||||
|
// When
|
||||||
|
Member member = Member.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(member).isNotNull();
|
||||||
|
assertThat(member.getUserId()).isEqualTo(TEST_USER_ID);
|
||||||
|
assertThat(member.getUserName()).isEqualTo(TEST_USER_NAME);
|
||||||
|
assertThat(member.getPassword()).isEqualTo(TEST_PASSWORD);
|
||||||
|
assertThat(member.getRoles()).containsExactly("USER");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity에서 Domain 객체로의 변환 테스트
|
||||||
|
* MemberEntity.toDomain() 메서드가 정상적으로 동작하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenMemberEntity_whenConvertToDomain_thenSuccess")
|
||||||
|
void givenMemberEntity_whenConvertToDomain_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
MemberEntity entity = createTestMemberEntity();
|
||||||
|
|
||||||
|
// When
|
||||||
|
Member member = entity.toDomain();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(member).isNotNull();
|
||||||
|
assertThat(member.getUserId()).isEqualTo(TEST_USER_ID);
|
||||||
|
assertThat(member.getUserName()).isEqualTo(TEST_USER_NAME);
|
||||||
|
assertThat(member.getPassword()).isEqualTo(TEST_PASSWORD);
|
||||||
|
assertThat(member.getRoles()).containsExactly("USER");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domain 객체에서 Entity로의 변환 테스트
|
||||||
|
* MemberEntity.fromDomain() 메서드가 정상적으로 동작하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenMemberDomain_whenConvertToEntity_thenSuccess")
|
||||||
|
void givenMemberDomain_whenConvertToEntity_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
Member member = createTestMember();
|
||||||
|
|
||||||
|
// When
|
||||||
|
MemberEntity entity = MemberEntity.fromDomain(member);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(entity).isNotNull();
|
||||||
|
assertThat(entity.getUserId()).isEqualTo(TEST_USER_ID);
|
||||||
|
assertThat(entity.getUserName()).isEqualTo(TEST_USER_NAME);
|
||||||
|
assertThat(entity.getPassword()).isEqualTo(TEST_PASSWORD);
|
||||||
|
assertThat(entity.getRoles()).containsExactly("USER");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 다중 역할을 가진 Member 객체 생성 테스트
|
||||||
|
* 여러 역할을 가진 Member 객체가 정상적으로 생성되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenMultipleRoles_whenBuildMember_thenSuccess")
|
||||||
|
void givenMultipleRoles_whenBuildMember_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
roles.add("ADMIN");
|
||||||
|
|
||||||
|
// When
|
||||||
|
Member member = Member.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(member).isNotNull();
|
||||||
|
assertThat(member.getRoles()).hasSize(2);
|
||||||
|
assertThat(member.getRoles()).containsExactlyInAnyOrder("USER", "ADMIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 테스트 헬퍼 메서드
|
||||||
|
private MemberEntity createTestMemberEntity() {
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
return MemberEntity.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Member createTestMember() {
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
return Member.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
package com.unicorn.lifesub.member.test.unit.service;
|
||||||
|
|
||||||
|
import com.unicorn.lifesub.common.dto.JwtTokenDTO;
|
||||||
|
import com.unicorn.lifesub.common.exception.BusinessException;
|
||||||
|
import com.unicorn.lifesub.common.exception.ErrorCode;
|
||||||
|
import com.unicorn.lifesub.common.exception.InfraException;
|
||||||
|
import com.unicorn.lifesub.member.config.jwt.JwtTokenProvider;
|
||||||
|
import com.unicorn.lifesub.member.dto.LoginRequest;
|
||||||
|
import com.unicorn.lifesub.member.dto.LogoutRequest;
|
||||||
|
import com.unicorn.lifesub.member.repository.entity.MemberEntity;
|
||||||
|
import com.unicorn.lifesub.member.repository.jpa.MemberRepository;
|
||||||
|
import com.unicorn.lifesub.member.service.MemberServiceImpl;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 멤버 서비스 테스트 클래스
|
||||||
|
* 주요 비즈니스 로직인 로그인/로그아웃 기능을 검증
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class MemberServiceImplTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private MemberServiceImpl memberService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MemberRepository memberRepository;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private JwtTokenProvider jwtTokenProvider;
|
||||||
|
|
||||||
|
// 테스트용 상수 정의
|
||||||
|
private static final String TEST_USER_ID = "testUser";
|
||||||
|
private static final String TEST_PASSWORD = "testPassword";
|
||||||
|
private static final String TEST_USER_NAME = "Test User";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 로그인 성공 케이스 테스트
|
||||||
|
* 올바른 사용자 ID와 비밀번호로 로그인 시 JWT 토큰이 정상적으로 발급되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenValidCredentials_whenLogin_thenSuccess")
|
||||||
|
void givenValidCredentials_whenLogin_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
LoginRequest request = new LoginRequest();
|
||||||
|
request.setUserId(TEST_USER_ID);
|
||||||
|
request.setPassword(TEST_PASSWORD);
|
||||||
|
|
||||||
|
MemberEntity memberEntity = createTestMemberEntity();
|
||||||
|
JwtTokenDTO expectedToken = createTestJwtTokenDTO();
|
||||||
|
|
||||||
|
given(memberRepository.findByUserId(TEST_USER_ID)).willReturn(Optional.of(memberEntity));
|
||||||
|
given(passwordEncoder.matches(TEST_PASSWORD, memberEntity.getPassword())).willReturn(true);
|
||||||
|
given(jwtTokenProvider.createToken(any(), any())).willReturn(expectedToken);
|
||||||
|
|
||||||
|
// When
|
||||||
|
JwtTokenDTO result = memberService.login(request);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.getAccessToken()).isEqualTo(expectedToken.getAccessToken());
|
||||||
|
assertThat(result.getRefreshToken()).isEqualTo(expectedToken.getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 로그인 실패 케이스 테스트 - 사용자가 존재하지 않는 경우
|
||||||
|
* 존재하지 않는 사용자 ID로 로그인 시도 시 적절한 예외가 발생하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenNonExistentUser_whenLogin_thenThrowException")
|
||||||
|
void givenNonExistentUser_whenLogin_thenThrowException() {
|
||||||
|
// Given
|
||||||
|
LoginRequest request = new LoginRequest();
|
||||||
|
request.setUserId("nonexistent");
|
||||||
|
request.setPassword(TEST_PASSWORD);
|
||||||
|
|
||||||
|
when(memberRepository.findByUserId("nonexistent")).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
// When & Then
|
||||||
|
InfraException exception = assertThrows(InfraException.class,
|
||||||
|
() -> memberService.login(request));
|
||||||
|
assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.MEMBER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 로그인 실패 케이스 테스트 - 잘못된 비밀번호
|
||||||
|
* 올바른 사용자 ID와 잘못된 비밀번호로 로그인 시도 시 적절한 예외가 발생하는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenInvalidPassword_whenLogin_thenThrowException")
|
||||||
|
void givenInvalidPassword_whenLogin_thenThrowException() {
|
||||||
|
// Given
|
||||||
|
LoginRequest request = new LoginRequest();
|
||||||
|
request.setUserId(TEST_USER_ID);
|
||||||
|
request.setPassword("wrongPassword");
|
||||||
|
|
||||||
|
MemberEntity memberEntity = createTestMemberEntity();
|
||||||
|
|
||||||
|
given(memberRepository.findByUserId(TEST_USER_ID)).willReturn(Optional.of(memberEntity));
|
||||||
|
given(passwordEncoder.matches("wrongPassword", memberEntity.getPassword())).willReturn(false);
|
||||||
|
|
||||||
|
// When & Then
|
||||||
|
BusinessException exception = assertThrows(BusinessException.class,
|
||||||
|
() -> memberService.login(request));
|
||||||
|
assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.INVALID_CREDENTIALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 로그아웃 테스트
|
||||||
|
* 로그아웃 요청 시 정상적으로 처리되는지 검증
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("givenLogoutRequest_whenLogout_thenSuccess")
|
||||||
|
void givenLogoutRequest_whenLogout_thenSuccess() {
|
||||||
|
// Given
|
||||||
|
LogoutRequest request = new LogoutRequest();
|
||||||
|
request.setUserId(TEST_USER_ID);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var response = memberService.logout(request);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertThat(response).isNotNull();
|
||||||
|
assertThat(response.getMessage()).contains("로그아웃이 완료되었습니다");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 테스트 헬퍼 메서드
|
||||||
|
private MemberEntity createTestMemberEntity() {
|
||||||
|
Set<String> roles = new HashSet<>();
|
||||||
|
roles.add("USER");
|
||||||
|
return MemberEntity.builder()
|
||||||
|
.userId(TEST_USER_ID)
|
||||||
|
.userName(TEST_USER_NAME)
|
||||||
|
.password(TEST_PASSWORD)
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtTokenDTO createTestJwtTokenDTO() {
|
||||||
|
return JwtTokenDTO.builder()
|
||||||
|
.accessToken("test-access-token")
|
||||||
|
.refreshToken("test-refresh-token")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user