T0.9: 注册表三表 DDL(01_schema.sql) + JPA 实体/Repository + H2 MODE=MySQL round-trip 与唯一约束测试
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package com.yunhai.platform.gateway.entity;
|
||||
|
||||
import com.yunhai.platform.gateway.repository.OpenApiLogRepository;
|
||||
import com.yunhai.platform.gateway.repository.OpenApiRouteRepository;
|
||||
import com.yunhai.platform.gateway.repository.OpenApiStatDailyRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* 注册表 DDL ↔ 实体一致性(T0.9 TDD):
|
||||
* H2 MODE=MySQL 直接执行交付的 docs/sql/01_schema.sql(非 Hibernate 生成表),
|
||||
* 三实体 round-trip 全字段一致(含 Java 侧默认值 = DDL DEFAULT 逐列对齐)+ 两个唯一键冲突。
|
||||
*/
|
||||
@DataJpaTest
|
||||
@ActiveProfiles("test")
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
class OpenApiRegistrySchemaTest {
|
||||
|
||||
@Autowired
|
||||
private OpenApiRouteRepository routeRepo;
|
||||
@Autowired
|
||||
private OpenApiLogRepository logRepo;
|
||||
@Autowired
|
||||
private OpenApiStatDailyRepository statRepo;
|
||||
|
||||
@Test
|
||||
void 三实体round_trip_默认值与DDL逐列一致() {
|
||||
OpenApiRoute route = new OpenApiRoute();
|
||||
route.setTargetCode("usermsg");
|
||||
route.setPathPattern("/oapi/message/**");
|
||||
routeRepo.saveAndFlush(route);
|
||||
OpenApiRoute loaded = routeRepo.findById(route.getId()).orElse(null);
|
||||
assertThat(loaded).isNotNull();
|
||||
assertThat(loaded.getTargetCode()).isEqualTo("usermsg");
|
||||
assertThat(loaded.getPathPattern()).isEqualTo("/oapi/message/**");
|
||||
assertThat(loaded.getHttpMethod()).isEqualTo("*");
|
||||
assertThat(loaded.getVisibility()).isEqualTo("INTERNAL");
|
||||
assertThat(loaded.getAuthMode()).isEqualTo("SIGNED");
|
||||
assertThat(loaded.getCacheTtlSeconds()).isZero();
|
||||
assertThat(loaded.getCacheScope()).isEqualTo("CALLER");
|
||||
assertThat(loaded.getRateLimitQps()).isZero();
|
||||
assertThat(loaded.getTimeoutMs()).isEqualTo(30000);
|
||||
assertThat(loaded.getRequestBodyLimitMb()).isEqualTo(10);
|
||||
assertThat(loaded.isEnabled()).isTrue();
|
||||
assertThat(loaded.getRemark()).isNull();
|
||||
assertThat(loaded.getGmtCreate()).isNotNull();
|
||||
assertThat(loaded.getGmtModified()).isNotNull();
|
||||
|
||||
OpenApiLog logRow = new OpenApiLog();
|
||||
logRow.setTs(LocalDateTime.now());
|
||||
logRow.setTargetCode("usermsg");
|
||||
logRow.setMethod("GET");
|
||||
logRow.setPath("/oapi/message/list");
|
||||
logRow.setStatus(200);
|
||||
logRow.setBackendCode(null);
|
||||
logRow.setCostMs(12);
|
||||
logRepo.saveAndFlush(logRow);
|
||||
OpenApiLog loadedLog = logRepo.findById(logRow.getId()).orElse(null);
|
||||
assertThat(loadedLog).isNotNull();
|
||||
assertThat(loadedLog.getCallerAppKey()).as("401 且无 appKey 记 NULL").isNull();
|
||||
assertThat(loadedLog.getBackendCode()).as("非信封响应记 NULL").isNull();
|
||||
assertThat(loadedLog.getStatus()).isEqualTo(200);
|
||||
assertThat(loadedLog.getCostMs()).isEqualTo(12);
|
||||
assertThat(loadedLog.getTs()).isNotNull();
|
||||
|
||||
OpenApiStatDaily stat = new OpenApiStatDaily();
|
||||
stat.setStatDate(LocalDate.now());
|
||||
stat.setTargetCode("usermsg");
|
||||
stat.setCallerAppKey("100617");
|
||||
statRepo.saveAndFlush(stat);
|
||||
OpenApiStatDaily loadedStat = statRepo.findById(stat.getId()).orElse(null);
|
||||
assertThat(loadedStat).isNotNull();
|
||||
assertThat(loadedStat.getTotal()).isZero();
|
||||
assertThat(loadedStat.getErrTotal()).isZero();
|
||||
assertThat(loadedStat.getAvgCostMs()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void uk_route_同三元组二次插入冲突() {
|
||||
OpenApiRoute a = new OpenApiRoute();
|
||||
a.setTargetCode("notice");
|
||||
a.setPathPattern("/oapi/notice/list");
|
||||
routeRepo.saveAndFlush(a);
|
||||
|
||||
OpenApiRoute b = new OpenApiRoute();
|
||||
b.setTargetCode("notice");
|
||||
b.setPathPattern("/oapi/notice/list");
|
||||
assertThatThrownBy(() -> routeRepo.saveAndFlush(b))
|
||||
.isInstanceOf(DataIntegrityViolationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void uk_stat_同三元组二次插入冲突() {
|
||||
OpenApiStatDaily a = new OpenApiStatDaily();
|
||||
a.setStatDate(LocalDate.of(2026, 9, 21));
|
||||
a.setTargetCode("notice");
|
||||
a.setCallerAppKey("100617");
|
||||
statRepo.saveAndFlush(a);
|
||||
|
||||
OpenApiStatDaily b = new OpenApiStatDaily();
|
||||
b.setStatDate(LocalDate.of(2026, 9, 21));
|
||||
b.setTargetCode("notice");
|
||||
b.setCallerAppKey("100617");
|
||||
assertThatThrownBy(() -> statRepo.saveAndFlush(b))
|
||||
.isInstanceOf(DataIntegrityViolationException.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:gatewaytest;MODE=MySQL;DATABASE_TO_LOWER=TRUE
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
sql:
|
||||
init:
|
||||
# 直接跑交付 DDL(docs/sql/01_schema.sql)——round-trip 钉死「DDL ↔ 实体」一致
|
||||
mode: always
|
||||
schema-locations: file:docs/sql/01_schema.sql
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
Reference in New Issue
Block a user