# 服务器测试与本地数据库 本文件是 `talent-card-server` 的测试约定。目标是让**新会话**不需要重新摸索环境:一条命令跑测试, 一条命令重置库,并且不重蹈已知的坑。 ## 一条命令跑测试 ```powershell 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`,再怀疑代码。 ## 重置测试库 ```powershell .\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 时的兜底。