本文件是 talent-card-server 的测试约定。目标是让**新会话**不需要重新摸索环境:一条命令跑测试,
一条命令重置库,并且不重蹈已知的坑。
| 库 | 版本 | Flyway | 谁在连 | |
|---|---|---|---|---|
| 开发库 | 192.168.110.13:3306/talent_card(局域网共享) |
MySQL 5.7.44 | 关(flyway.enabled=false) |
后端、后台管理页、两个小程序 |
| 测试库 | 127.0.0.1:8134/talent_card_test(本机实例) |
MySQL 8.0.36 | 开 | 本文件的测试脚本 |
三条由此推出的硬约束:
V151 的存储过程写法8134 实例上另有 talent_card、talent_card_dryrun、talent_card_verify1 三个库:它们是.scratch/ 下的一次性探针(*Probe*.java、Inspect*.java、Apply*.java 等)大多把 URL127.0.0.1:8134/talent_card,指的就是这份旧副本;复用时要显式传开发库 URL,别照默认值跑。cd talent-card-server
.\scripts\run-server-tests.ps1 # 全量
.\scripts\run-server-tests.ps1 -Test PaymentSettlementTests # 单个类
.\scripts\run-server-tests.ps1 -Test A,B # 多个类
.\scripts\run-server-tests.ps1 -Clean -Test A # 构建产物可疑时先 clean
.\scripts\run-server-tests.ps1 -Reset # 重置库后再跑
脚本会设置 TEST_DATASOURCE_*(MySQL 127.0.0.1:8134、talent_card_test、root/123456,
也可用环境变量覆盖),把日志写到 target/server-tests.log,并在失败时列出已知基线失败。
用 pwsh 跑(本机装了 PowerShell 7:C:\Program Files\PowerShell\7\pwsh.exe)。脚本也已在
Windows PowerShell 5.1 下实测通过——但如果 5.1 里出现"构建在编译中途莫名中断",先换 pwsh 再怀疑代码:
5.1 会把 Maven 打到 stderr 的警告(例如 JVM 的 sun.misc.Unsafe)当成终止错误。
另注:mvnw.cmd 是 batch/PowerShell 混合的 wrapper,它自己**总是**用 powershell(即 5.1)做引导,
与你在哪个 shell 里敲命令无关——这是 Apache Maven Wrapper 3.3.4 的既有实现,不要改成 pwsh。
(zcode 的 bash 工具跑在 Git Bash 里,PowerShell 只在显式点名或这个 wrapper 内部才出现。)
必须走这个脚本(或等价地只跑一个 Maven 进程)。 同一个 checkout 上并发跑两个构建会写坏target/,症状是看起来不可能的报错——例如 No qualifying bean of type 'HuifuPaymentProperties'。
遇到这类"不可能"的 bean/classpath 错误,先 -Clean,再怀疑代码。
.\scripts\reset-test-db.ps1 # 默认 talent_card_test
.\scripts\reset-test-db.ps1 -Database talent_card
脚本用项目已有的 MySQL JDBC 驱动(本仓库没有 mysql 客户端、没有 Docker),删掉该库里的全部表;
下一次测试由 Flyway 从 db/migration 重建。
不要 drop database,也不要只删 flyway_schema_history 里的某一行。
- drop database 之后重建会触发 BankBranchDirectoryInitializer 导入 10 万行网点数据(几分钟,
且曾观察到卡死)。测试 profile 已关闭该导入,所以上面的重置脚本不会再有这个代价。
- 手工删 history 行会让已执行的 DDL 与 Flyway 记录不一致,后续迁移报错更难查。
开发库是局域网共享的那一个(192.168.110.13:3306/talent_card,见上表),不是测试库。它里面有
一批**迁移从未创建**的表——当年是直接在库里手建的,例如 dependent_card_apply、merchant_benefit_rule。
所以「把测试库结构克隆到开发库」「reset-test-db.ps1 -Database talent_card」这类操作会把开发库里这些
表连同数据一起删掉:2026-09-13 就这样丢过一批,最后靠一份同源快照才回灌回来(reset-test-db.ps1
对名为 talent_card 的库一律拒绝,正是为此)。
注意开发库的 spring.flyway.enabled=false:它**不会**被迁移自动修正,缺表缺列只能手工补。
需要让开发库结构跟上迁移时:
create table if not exists、add column),**不要**删表重建。手工补 DDL 正是Unknown column 'capability_sync_until'」的解药。.\scripts\apply-sql-file.ps1 -File <某个 .sql> -Yes 用项目已有的 JDBC 驱动-Database/-DatabaseHost/-Port 覆盖;不带 -Yes 只打印不执行)。DELIMITER,所以 db/migration/V*.sql 也能原样重放。默认指向**开发库**,别拿它去改测试库db/migration 重建,手工 DDL 会让迁移与 history 表对不上)。V152(支付流水两列)与 V153(手输核销码计数表)就是这样落地的,scripts/dev-schema-v152-payment-attempt.sql(写法可重复执行的那份在迁移里,两者等价)。D:\MySQL\... 和docker exec talent-card-mysql-1 的两条老做法都已失效),要备份就在 DB 主机上做,或照scripts/reset-test-db.ps1 的办法用项目已有的 JDBC 驱动写个单文件程序。alter table 等 DDL 元数据锁的上限只有 5 秒,等不到就记一条Merchant onboarding schema: could not add,那里会给出可直接执行的手工补救语句。已经执行过的 V*__*.sql 不要改(改一个字符就会 checksum 不匹配,测试库直接起不来)。需要调整
就新增一个迁移文件,或用 reset-test-db.ps1 重建后再跑。历史上 scripts/repair-local-mysql-v*.sql
就是为此付出的代价。
整套测试里有 5 项在基线上就失败,与本轮改动无关:前 3 项用 HEAD worktree + 同一测试库复现过,
后 2 行是连内存 H2 的用例(原因见表内)。
| 测试 | 现象 |
|---|---|
MerchantWorkServiceTests.filtersOrdersByCategoryStatusAndDateWithoutExposingFullMobile |
stays.total() 期望 1 实得 0 |
TalentAuthServiceTests.movesWechatBindingWhenSameWechatLogsInWithAnotherMobile |
insert into talent_session BadSqlGrammar |
MerchantSettlementStatisticsMapperTests.includesHotelFreeOfflineSubsidyWithoutIncreasingTransactionAmount |
持久化断言失败 |
ManualConsumptionServiceTests 2 项(childcareCurrentBenefitUsesTheScannedLevelsRightsDescription、cultureCurrentBenefitUsesOnlyTheScannedLevelsStoreConsumptionDescription) |
连的是内存 H2,H2 里没建 merchant 表 → Table "merchant" not found。与测试库无关,与支付域改动也无关 |
MerchantVerificationFreeStayTests.scanReturnsConfiguredHotelSubsidyAmount |
同上:H2 里 select merchant_type_code, merchant_rights_config from merchant 报表不存在 |
新会话不要为了证明它们"是既有的"而重建 worktree:直接看这张表。修好其中任何一项时,请删掉对应行。
最后两行是 2026-09-24 补的(跑全量时共 6 项失败:3 项基线 + 这 3 项):它们不在运行脚本打印的known baseline failures 清单里,但全量与单独跑那 2 个类都同样失败。原因是这几个用例自己连内存 H2
(jdbc:h2:mem:...)而不是共享测试库,而 H2 里没有建 merchant 表——判断一次改动是否引入回归时,
先把它们排除;要修就得给这几条 H2 路径补上建表(属权益/手工消费那一域,跨域改动请交给对应负责人)。
PaymentFundConfirmationTests.aPendingConfirmationOfAVerifiedOrderIsRetriedWithoutAnotherVerification
原先会随测试库累积而**必然**失败:补偿任务扫全库、order by o.id asc limit 50,用例新建的那单 id 最大、
排在窗口之外。
2026-09-17 已按本文件的建议修好——该用例先把**自己之外**的相关行移出扫描条件,于是它从「环境相关失败」
变成稳定通过;同一天还把同一处扫描改为「每单只看最新一笔成功支付」,并把它扩展到 reverting(撤销核销
的回退在途)状态。以后新写这类「后台任务只处理了我这条」的用例,照它的做法先腾窗口,不要再依赖测试库的
累积状况。
全量跑着跑着,**后加载**的那个用例类会整类报错,根因是:
Unable to obtain connection from database: ...
message from server: "Too many connections" (SQL State 08004)
每个 @SpringBootTest 的上下文都缓存到整个 JVM 结束、各自持有一个 Hikari 连接池,而 Hikari 的minimum-idle 默认等于 maximum-pool-size(10),**启动时就把 10 条连接建满**。一次全量会累积十几个
上下文(2026-10-12 实测 15 个),150 条空闲连接正好把测试库那个 8.0 实例的 max_connections=151
吃光。测试 profile 现在把 spring.datasource.hikari.minimum-idle 钉成 1(见src/test/resources/application-test.yml),空闲上下文只留一条、按需再涨,连接池语义不变。
别把它改回去:症状是「明明只加了几个测试,却有整整一个类的用例全挂」,很容易被误判成自己引入的回归。
判断依据:报错里出现 Too many connections 就是它,与代码无关(同一次全量里,挂的总是**靠后**加载的
上下文,而不是某个具体用例的逻辑)。
测试库那个 8.0.36 实例跑在 WSL 的 docker 里(发行版 Ubuntu-26.04,容器 talent-card-mysql-1,127.0.0.1:8134->3306)。WSL 虚拟机会在空闲时自己关掉,容器跟着停——于是测试全部报
Unable to obtain connection from database: Communications link failure
Caused by: java.net.ConnectException: Connection refused: getsockopt
或者更绕的一句(连接失败被 Hibernate 翻译过之后的表象):
Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' ...)
判断与恢复:
wsl.exe -d Ubuntu-26.04 -u root -- docker ps # 容器在不在
wsl.exe -d Ubuntu-26.04 -u root -- docker start talent-card-mysql-1 talent-card-redis-1
光起容器不够:跑测试期间要有东西把 VM 挂住,否则命令一返回、VM 又空闲关掉,测试照样撞在停机缝里
(现象是容器每次 docker ps 都显示 Up 2 seconds)。用一个长命进程持有它,跑完再结束:
wsl.exe -d Ubuntu-26.04 -u root -- bash -lc "sleep 1800" # 后台挂着,测试跑完再停
本机 Windows 侧没有 mysqld、没有 Docker——**别在 Windows 里找 8.0 实例**,它只在 WSL 里。
Spring 测试共用一个 schema,测试之间不做清理(没有 @Transactional 回滚)。因此:
payment_transaction.created_at 置为 now()),否则前面的用例会漏进来。