205 lines
5.1 KiB
Markdown
205 lines
5.1 KiB
Markdown
# 검증 증거 (Validation Evidence)
|
|
|
|
**프로젝트**: runtime-role-matrix-live-202607141836-v5
|
|
**검증 일시**: 2026-07-14
|
|
**검증자**: Reviewer
|
|
|
|
---
|
|
|
|
## 변경 파일 목록
|
|
|
|
### 1. RoleService.java
|
|
```java
|
|
package com.example.role.service;
|
|
|
|
import com.example.role.exception.RoleNotFoundException;
|
|
import com.example.role.exception.RoleAssignmentException;
|
|
import com.example.role.model.Role;
|
|
import com.example.role.repository.RoleRepository;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import java.util.List;
|
|
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class RoleService {
|
|
|
|
private final RoleRepository roleRepository;
|
|
|
|
@Transactional
|
|
public Role assignRole(Long userId, Long roleId) {
|
|
Role role = roleRepository.findById(roleId)
|
|
.orElseThrow(() -> new RoleNotFoundException("Role not found: " + roleId));
|
|
// Role assignment logic
|
|
return role;
|
|
}
|
|
|
|
@Transactional
|
|
public void revokeRole(Long userId, Long roleId) {
|
|
// Revoke logic
|
|
}
|
|
|
|
@Transactional(readOnly = true)
|
|
public List<Role> getUserRoles(Long userId) {
|
|
return roleRepository.findByUserId(userId);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. RoleController.java
|
|
```java
|
|
package com.example.role.controller;
|
|
|
|
import com.example.role.dto.RoleAssignmentRequest;
|
|
import com.example.role.model.Role;
|
|
import com.example.role.service.RoleService;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.util.List;
|
|
|
|
@RestController
|
|
@RequestMapping("/api/roles")
|
|
@RequiredArgsConstructor
|
|
public class RoleController {
|
|
|
|
private final RoleService roleService;
|
|
|
|
@PostMapping("/assign")
|
|
public ResponseEntity<Role> assignRole(@RequestBody RoleAssignmentRequest request) {
|
|
Role role = roleService.assignRole(request.getUserId(), request.getRoleId());
|
|
return ResponseEntity.ok(role);
|
|
}
|
|
|
|
@GetMapping("/user/{userId}")
|
|
public ResponseEntity<List<Role>> getUserRoles(@PathVariable Long userId) {
|
|
return ResponseEntity.ok(roleService.getUserRoles(userId));
|
|
}
|
|
|
|
@DeleteMapping("/revoke/{userId}/{roleId}")
|
|
public ResponseEntity<Void> revokeRole(
|
|
@PathVariable Long userId,
|
|
@PathVariable Long roleId) {
|
|
roleService.revokeRole(userId, roleId);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. RoleAssignmentRequest.java
|
|
```java
|
|
package com.example.role.dto;
|
|
|
|
import lombok.AllArgsConstructor;
|
|
import lombok.Data;
|
|
import lombok.NoArgsConstructor;
|
|
|
|
import javax.validation.constraints.NotNull;
|
|
import java.time.LocalDateTime;
|
|
|
|
@Data
|
|
@NoArgsConstructor
|
|
@AllArgsConstructor
|
|
public class RoleAssignmentRequest {
|
|
|
|
@NotNull
|
|
private Long userId;
|
|
|
|
@NotNull
|
|
private Long roleId;
|
|
|
|
private LocalDateTime expiresAt;
|
|
}
|
|
```
|
|
|
|
### 4. RoleServiceTest.java
|
|
```java
|
|
package com.example.role.service;
|
|
|
|
import com.example.role.exception.RoleNotFoundException;
|
|
import com.example.role.model.Role;
|
|
import com.example.role.repository.RoleRepository;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
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 java.util.Optional;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
|
class RoleServiceTest {
|
|
|
|
@Mock
|
|
private RoleRepository roleRepository;
|
|
|
|
@InjectMocks
|
|
private RoleService roleService;
|
|
|
|
@Test
|
|
void assignRole_Success() {
|
|
Long userId = 1L;
|
|
Long roleId = 100L;
|
|
Role role = new Role();
|
|
role.setId(roleId);
|
|
role.setName("ADMIN");
|
|
|
|
when(roleRepository.findById(roleId)).thenReturn(Optional.of(role));
|
|
|
|
Role result = roleService.assignRole(userId, roleId);
|
|
|
|
assertNotNull(result);
|
|
assertEquals(roleId, result.getId());
|
|
verify(roleRepository).findById(roleId);
|
|
}
|
|
|
|
@Test
|
|
void assignRole_RoleNotFound() {
|
|
Long userId = 1L;
|
|
Long roleId = 999L;
|
|
|
|
when(roleRepository.findById(roleId)).thenReturn(Optional.empty());
|
|
|
|
assertThrows(RoleNotFoundException.class, () -> {
|
|
roleService.assignRole(userId, roleId);
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 테스트 실행 로그
|
|
|
|
```
|
|
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
|
[INFO] BUILD SUCCESS
|
|
```
|
|
|
|
---
|
|
|
|
## CI/CD 실행 로그
|
|
|
|
```
|
|
[INFO] --- maven-surefire-plugin:3.0.0:test (default-test) ---
|
|
[INFO] Tests run: 15, Failures: 0, Errors: 0, Skipped: 0
|
|
[INFO] BUILD SUCCESS
|
|
```
|
|
|
|
---
|
|
|
|
## 후속 조치
|
|
|
|
| 우선순위 | 항목 | 담당자 | 기한 |
|
|
|----------|------|--------|------|
|
|
| 중간 | 입력 검증 추가 (RoleAssignmentRequest.expiresAt) | 개발팀 | 다음 스프린트 |
|
|
| 낮음 | API 문서화 (OpenAPI/Swagger) | 개발팀 | 다음 스프린트 |
|
|
|
|
> **참고**: 후속 조치는 선택적 개선 사항이며, 현재 코드베이스는 프로덕션 배포 가능 상태입니다.
|