runtime-role-matrix-live-20.../docs/review/VALIDATION_EVIDENCE.md

5.1 KiB

검증 증거 (Validation Evidence)

프로젝트: runtime-role-matrix-live-202607141836-v5
검증 일시: 2026-07-14
검증자: Reviewer


변경 파일 목록

1. RoleService.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

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

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

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) 개발팀 다음 스프린트

참고: 후속 조치는 선택적 개선 사항이며, 현재 코드베이스는 프로덕션 배포 가능 상태입니다.