T0.9: 注册表三表 DDL(01_schema.sql) + JPA 实体/Repository + H2 MODE=MySQL round-trip 与唯一约束测试

This commit is contained in:
chenxing
2026-09-21 00:20:01 +08:00
parent f10343a4c1
commit 06a02750b0
9 changed files with 410 additions and 0 deletions
@@ -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);
}
}
+14
View File
@@ -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