134 lines
6.5 KiB
Java
134 lines
6.5 KiB
Java
package com.baiyue.firstleg.controller;
|
|
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.core.io.ClassPathResource;
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
import org.yaml.snakeyaml.Yaml;
|
|
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.RecordComponent;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
@DisplayName("费用管理接口权限契约")
|
|
class FeeManagementContractTest {
|
|
@Test
|
|
@DisplayName("通过和驳回共用审核按钮权限")
|
|
void protectsReviewActions() {
|
|
for (String methodName : new String[] {"approve", "reject"}) {
|
|
Method method = Arrays.stream(PlatformInboundFeeDetailController.class.getDeclaredMethods())
|
|
.filter(candidate -> candidate.getName().equals(methodName))
|
|
.findFirst().orElseThrow();
|
|
assertThat(method.getAnnotation(PreAuthorize.class).value())
|
|
.isEqualTo("hasAuthority('firstleg:inbound:fee-detail:review')");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("费用项目增删改查权限彼此独立")
|
|
void separatesCrudPermissions() {
|
|
assertPermission("list", "firstleg:fee-item:view");
|
|
assertPermission("detail", "firstleg:fee-item:view");
|
|
assertPermission("create", "firstleg:fee-item:create");
|
|
assertPermission("update", "firstleg:fee-item:edit");
|
|
assertPermission("changeStatus", "firstleg:fee-item:edit");
|
|
assertPermission("delete", "firstleg:fee-item:delete");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("柜次费用新增和调整请求均携带往来单位快照")
|
|
void containerFeeRequestsCarryCounterpartySnapshot() {
|
|
for (Class<?> requestType : List.of(
|
|
com.baiyue.firstleg.dto.ContainerFeeDetailDtos.SaveRequest.class,
|
|
com.baiyue.firstleg.dto.ContainerFeeDetailDtos.AdjustRequest.class)) {
|
|
assertThat(Arrays.stream(requestType.getRecordComponents()).map(RecordComponent::getName))
|
|
.contains("partnerId", "partnerName");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("平台费用项目接口不接受客户作用域")
|
|
void platformFeeItemEndpointsDoNotExposeAccountId() {
|
|
Arrays.stream(PlatformFeeItemController.class.getDeclaredMethods())
|
|
.filter(method -> List.of("list", "detail", "create", "update", "changeStatus", "delete")
|
|
.contains(method.getName()))
|
|
.flatMap(method -> Arrays.stream(method.getParameters()))
|
|
.forEach(parameter -> assertThat(parameter.getName()).isNotEqualTo("accountId"));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("费用项目OpenAPI包含完整响应和空响应契约")
|
|
void openApiIsComplete() throws Exception {
|
|
String source = new ClassPathResource("openapi/fee-item-v1.yaml")
|
|
.getContentAsString(StandardCharsets.UTF_8);
|
|
Map<String, Object> document = stringMap(new Yaml().load(source));
|
|
assertThat(document.get("openapi")).isEqualTo("3.0.3");
|
|
assertThat(source).doesNotContain("accountId", "AccountId");
|
|
|
|
Map<String, Object> paths = stringMap(document.get("paths"));
|
|
assertThat(paths).containsOnlyKeys(
|
|
"/api/platform/v1/fee-items",
|
|
"/api/platform/v1/fee-items/{id}",
|
|
"/api/platform/v1/fee-items/{id}/status");
|
|
assertResponseSchema(paths, "/api/platform/v1/fee-items", "get", "FeeItemPageApiResponse");
|
|
assertResponseSchema(paths, "/api/platform/v1/fee-items", "post", "FeeItemApiResponse");
|
|
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}", "get", "FeeItemApiResponse");
|
|
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}", "put", "FeeItemApiResponse");
|
|
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}", "delete", "VoidApiResponse");
|
|
assertResponseSchema(paths, "/api/platform/v1/fee-items/{id}/status", "patch", "VoidApiResponse");
|
|
|
|
Map<String, Object> schemas = stringMap(stringMap(document.get("components")).get("schemas"));
|
|
assertThat(schemas).containsKeys("FeeItemSaveRequest", "FeeItemResource", "FeeItemPage",
|
|
"FeeItemApiResponse", "FeeItemPageApiResponse", "VoidApiResponse");
|
|
for (String response : List.of("FeeItemApiResponse", "FeeItemPageApiResponse", "VoidApiResponse")) {
|
|
assertThat(list(stringMap(schemas.get(response)).get("required"))).contains("timestamp");
|
|
assertThat(properties(schemas, response)).containsKeys(
|
|
"code", "message", "data", "requestId", "traceId", "timestamp");
|
|
}
|
|
assertThat(properties(schemas, "VoidApiResponse").get("data"))
|
|
.isInstanceOf(Map.class)
|
|
.satisfies(value -> assertThat(stringMap(value)).containsEntry("nullable", true));
|
|
assertThat(stringMap(properties(schemas, "FeeItemSaveRequest").get("feeCode")))
|
|
.containsEntry("maxLength", 50)
|
|
.containsEntry("pattern", "^[A-Za-z0-9_-]+$");
|
|
}
|
|
|
|
private static void assertPermission(String methodName, String permission) {
|
|
Method method = Arrays.stream(PlatformFeeItemController.class.getDeclaredMethods())
|
|
.filter(candidate -> candidate.getName().equals(methodName))
|
|
.findFirst().orElseThrow();
|
|
assertThat(method.getAnnotation(PreAuthorize.class).value())
|
|
.isEqualTo("hasAuthority('" + permission + "')");
|
|
}
|
|
|
|
private static void assertResponseSchema(Map<String, Object> paths, String path,
|
|
String operation, String schema) {
|
|
Map<String, Object> endpoint = stringMap(paths.get(path));
|
|
Map<String, Object> response = stringMap(stringMap(endpoint.get(operation)).get("responses"));
|
|
Map<String, Object> success = stringMap(response.get("200"));
|
|
Map<String, Object> content = stringMap(success.get("content"));
|
|
Map<String, Object> mediaType = stringMap(content.get("application/json"));
|
|
assertThat(stringMap(mediaType.get("schema")))
|
|
.containsEntry("$ref", "#/components/schemas/" + schema);
|
|
}
|
|
|
|
private static Map<String, Object> properties(Map<String, Object> schemas, String schema) {
|
|
return stringMap(stringMap(schemas.get(schema)).get("properties"));
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private static Map<String, Object> stringMap(Object value) {
|
|
return (Map<String, Object>) value;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private static List<String> list(Object value) {
|
|
return (List<String>) value;
|
|
}
|
|
}
|