You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

136 lines
6.1 KiB

package com.mydreamjob.resume;
import com.mydreamjob.auth.User;
import com.mydreamjob.auth.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* 简历版本权限一致性集成测试(真实 Service + 真实 H2)。
*
* <p>版本存在但属于他人 → 四个接口一律 HTTP 403;版本不存在 → 404。
* 覆盖:GET/PUT/DELETE /profile/me/resume-versions/{id} 与 POST .../default。
*/
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("integration-test")
class ResumeVersionPermissionControllerTest {
@Autowired private MockMvc mockMvc;
@Autowired private com.mydreamjob.auth.JwtTokenProvider jwtTokenProvider;
@Autowired private ResumeVersionService resumeVersionService;
@Autowired private UserRepository userRepository;
@MockBean private RedisTemplate<String, Object> redisTemplate;
private Long ownerUserId;
private Long otherUserId;
private Long versionId;
private static final java.util.concurrent.atomic.AtomicLong PHONE_SEQ =
new java.util.concurrent.atomic.AtomicLong(300);
@BeforeEach
@SuppressWarnings("unchecked")
void setUp() {
ValueOperations<String, Object> ops = mock(ValueOperations.class);
when(redisTemplate.opsForValue()).thenReturn(ops);
when(redisTemplate.hasKey(any())).thenReturn(false);
ownerUserId = createStudent();
otherUserId = createStudent();
MockMultipartFile file = new MockMultipartFile("file", "r.pdf", "application/pdf",
"%PDF-1.7\npermission-fixture\n%%EOF\n".getBytes(java.nio.charset.StandardCharsets.US_ASCII));
ResumeVersion version = resumeVersionService.createVersion(ownerUserId, "秋招版", file);
versionId = version.getId();
}
private Long createStudent() {
User u = userRepository.save(User.builder()
.phone("177" + String.format("%08d", PHONE_SEQ.incrementAndGet()))
.nickname("候选人").role(User.Role.STUDENT).active(true).verified(true).build());
return u.getId();
}
private String token(Long userId) {
return com.mydreamjob.auth.support.TestAuthTokens.bearer(userId, "STUDENT");
}
// ── 另一学生访问 → 403 ────────────────────────────────────
@Test
void get_otherStudent_shouldReturn403() throws Exception {
mockMvc.perform(get("/profile/me/resume-versions/" + versionId)
.header("Authorization", token(otherUserId)))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.code").value(com.mydreamjob.common.exception.ErrorCode.FORBIDDEN.getCode()));
}
@Test
void update_otherStudent_shouldReturn403() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.multipart("/profile/me/resume-versions/" + versionId)
.param("name", "改他人")
.with(req -> {
req.setMethod("PUT");
return req;
})
.header("Authorization", token(otherUserId)))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.code").value(com.mydreamjob.common.exception.ErrorCode.FORBIDDEN.getCode()));
}
@Test
void delete_otherStudent_shouldReturn403() throws Exception {
mockMvc.perform(delete("/profile/me/resume-versions/" + versionId)
.header("Authorization", token(otherUserId)))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.code").value(com.mydreamjob.common.exception.ErrorCode.FORBIDDEN.getCode()));
}
@Test
void setDefault_otherStudent_shouldReturn403() throws Exception {
mockMvc.perform(post("/profile/me/resume-versions/" + versionId + "/default")
.header("Authorization", token(otherUserId)))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.code").value(com.mydreamjob.common.exception.ErrorCode.FORBIDDEN.getCode()));
}
// ── 不存在 → 404 ──────────────────────────────────────────
@Test
void get_notExist_shouldReturn404() throws Exception {
mockMvc.perform(get("/profile/me/resume-versions/999999")
.header("Authorization", token(otherUserId)))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.code").value(com.mydreamjob.common.exception.ErrorCode.RESUME_VERSION_NOT_FOUND.getCode()));
}
// ── 本人 → 200(基线,确认规则未误伤本人) ─────────────────
@Test
void get_owner_shouldReturn200() throws Exception {
mockMvc.perform(get("/profile/me/resume-versions/" + versionId)
.header("Authorization", token(ownerUserId)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data.id").value(versionId));
}
}