diff --git a/backend/pom.xml b/backend/pom.xml index 1662a69..bac8aeb 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -99,6 +99,17 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*Test.java + **/*Tests.java + **/*IT.java + + + diff --git a/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java b/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java index 0e9be2b..d0b49ef 100644 --- a/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java +++ b/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java @@ -1,5 +1,6 @@ package com.xly.erp.common.security; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -10,7 +11,7 @@ public final class SecurityContextHelper { public static String currentUserNo() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth == null || auth.getPrincipal() == null) { + if (auth == null || auth instanceof AnonymousAuthenticationToken) { return null; } Object p = auth.getPrincipal(); diff --git a/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java b/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java index 782a57a..e43ba64 100644 --- a/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java +++ b/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java @@ -73,6 +73,104 @@ class ModuleControllerIT { assertThat(row.get("sCreatedBy")).isEqualTo("ADMIN001"); } + @Test + void postEmptyBody_returns40001_withFieldHint() throws Exception { + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + + ResponseEntity resp = rest.exchange( + url(), + HttpMethod.POST, + new HttpEntity<>("{}", headers), + String.class); + + JsonNode body = objectMapper.readTree(resp.getBody()); + assertThat(body.get("code").asInt()).isEqualTo(40001); + assertThat(body.get("msg").asText()).containsAnyOf( + "sProcedureName", "sDisplayType", "sModuleType", "sManageDeptEn", "sModuleNameZh"); + } + + @Test + void postInvalidDisplayType_returns40010() throws Exception { + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + Map bad = validBody("sp_test_bad_type", "枚举非法"); + bad.put("sDisplayType", "火星"); + + ResponseEntity resp = rest.exchange( + url(), HttpMethod.POST, new HttpEntity<>(bad, headers), String.class); + + JsonNode body = objectMapper.readTree(resp.getBody()); + assertThat(body.get("code").asInt()).isEqualTo(40010); + } + + @Test + void postDuplicateProcedureName_returns40020() throws Exception { + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + Map first = validBody("sp_test_dup", "首次"); + ResponseEntity r1 = rest.exchange( + url(), HttpMethod.POST, new HttpEntity<>(first, headers), String.class); + assertThat(objectMapper.readTree(r1.getBody()).get("code").asInt()).isZero(); + + Map dup = validBody("sp_test_dup", "重复"); + ResponseEntity r2 = rest.exchange( + url(), HttpMethod.POST, new HttpEntity<>(dup, headers), String.class); + JsonNode body = objectMapper.readTree(r2.getBody()); + assertThat(body.get("code").asInt()).isEqualTo(40020); + } + + @Test + void postWithMissingParent_returns40021() throws Exception { + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + Map orphan = validBody("sp_test_orphan", "缺父"); + orphan.put("iParentId", 99999999); + + ResponseEntity resp = rest.exchange( + url(), HttpMethod.POST, new HttpEntity<>(orphan, headers), String.class); + + JsonNode body = objectMapper.readTree(resp.getBody()); + assertThat(body.get("code").asInt()).isEqualTo(40021); + } + + @Test + void postWithoutJwt_permitAllStub_returns200_andCreatedBySTUBADMIN() throws Exception { + HttpHeaders headers = jsonHeaders(); + Map body = validBody("sp_test_nojwt", "无JWT"); + + ResponseEntity resp = rest.exchange( + url(), HttpMethod.POST, new HttpEntity<>(body, headers), String.class); + + JsonNode jb = objectMapper.readTree(resp.getBody()); + assertThat(jb.get("code").asInt()).isZero(); + int newId = jb.get("data").get("iIncrement").asInt(); + String createdBy = jdbcTemplate.queryForObject( + "SELECT sCreatedBy FROM tModule WHERE iIncrement = ?", String.class, newId); + assertThat(createdBy).isEqualTo("STUB_ADMIN"); + } + + @Test + void postWithTamperedJwt_returns20001() throws Exception { + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer not.a.real.jwt"); + Map body = validBody("sp_test_tampered", "伪JWT"); + + ResponseEntity resp = rest.exchange( + url(), HttpMethod.POST, new HttpEntity<>(body, headers), String.class); + + JsonNode jb = objectMapper.readTree(resp.getBody()); + assertThat(jb.get("code").asInt()).isEqualTo(20001); + } + + private String url() { + return "http://localhost:" + port + "/api/mod/modules"; + } + static HttpHeaders jsonHeaders() { HttpHeaders h = new HttpHeaders(); h.setContentType(MediaType.APPLICATION_JSON);