服务器测试与本地数据库

本文件是 talent-card-server 的测试约定。目标是让**新会话**不需要重新摸索环境:一条命令跑测试,
一条命令重置库,并且不重蹈已知的坑。

一条命令跑测试

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,并在失败时列出已知基线失败。

必须走这个脚本(或等价地只跑一个 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 记录不一致,后续迁移报错更难查。

不要重建开发库

127.0.0.1:8134/talent_card 是**开发库**(后台管理页和两个小程序都连它),不是测试库。它里面有
一批**迁移从未创建**的表——当年是直接在库里手建的,例如 dependent_card_apply、merchant_benefit_rule。
所以「把测试库结构克隆到开发库」「reset-test-db.ps1 -Database talent_card」这类操作会把开发库里这些
表连同数据一起删掉:2026-09-13 就这样丢过一批,最后靠 127.0.0.1:6134/talent_card 上的同源快照才回灌回来。

注意开发库的 spring.flyway.enabled=false:它**不会**被迁移自动修正,缺表只能手工补。

需要让开发库结构跟上迁移时:

  • 只补缺失的 DDL(create table if not exists),**不要**删表重建;
  • 动结构之前先 dump:docker exec talent-card-mysql-1 mysqldump -uroot -p123456 talent_card > backup.sql;
  • 从旧快照回灌数据用 .scratch/automatic-withdrawal/tools/RestoreOldData.java(按列交集 INSERT IGNORE,
    只新增不删改)。该 MySQL 开了 binlog 且保留 30 天,误删后还能查证与补救。

迁移是只追加的

已经执行过的 V*__*.sql 不要改(改一个字符就会 checksum 不匹配,测试库直接起不来)。需要调整
就新增一个迁移文件,或用 reset-test-db.ps1 重建后再跑。历史上 scripts/repair-local-mysql-v*.sql
就是为此付出的代价。

已知基线失败(不是回归)

整套测试有 3 项在 HEAD 基线上就失败,与本轮改动无关;用 HEAD worktree + 同一测试库复现过:

测试 现象
MerchantWorkServiceTests.filtersOrdersByCategoryStatusAndDateWithoutExposingFullMobile stays.total() 期望 1 实得 0
TalentAuthServiceTests.movesWechatBindingWhenSameWechatLogsInWithAnotherMobile insert into talent_session BadSqlGrammar
MerchantSettlementStatisticsMapperTests.includesHotelFreeOfflineSubsidyWithoutIncreasingTransactionAmount 持久化断言失败

新会话不要为了证明它们"是既有的"而重建 worktree:直接看这张表。修好其中任何一项时,请删掉对应行。

测试库的状态是共享的

Spring 测试共用一个 schema,测试之间不做清理(没有 @Transactional 回滚)。因此:

  • 断言要按自己创建的 ID / 单号过滤,不要断言"全表只有一行"。
  • 断言"后台任务只处理了我这条"时,先把自己之外的相关行挪出时间窗口(例如把历史
    payment_transaction.created_at 置为 now()),否则前面的用例会漏进来。
  • 需要真实 MySQL 语义(当前所有 Spring 测试都是)时,H2 只作为没有 MySQL 时的兜底。