From 2b6de6df5fc7c7af255637f594fe00b729617dca Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Mon, 14 Sep 2026 16:32:35 +0800 Subject: [PATCH] docs(06-06): complete map unit test plan --- .planning/ROADMAP.md | 8 +- .planning/STATE.md | 19 +- .../phases/06-unit-tests/06-06-SUMMARY.md | 181 ++++++++++++++++++ .../phases/06-unit-tests/06-COVERAGE-MAP.md | 45 +++++ .../phases/06-unit-tests/06-TEST-FINDINGS.md | 20 ++ .planning/state.json | 2 +- 6 files changed, 263 insertions(+), 12 deletions(-) create mode 100644 .planning/phases/06-unit-tests/06-06-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 4ec7498..7a1caf0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -210,7 +210,7 @@ Plans: 3. 测试在本地可运行且全部通过 4. 测试由 AI 编写并运行,通过验证后可提交 -**Plans**: 5/9 plans executed replanned (D-28;旧 06-01/06-02 执行结果标记 superseded,按同号重跑;数据端切片,非数据 render/legacy 覆盖延后) +**Plans**: 6/9 plans executed replanned (D-28;旧 06-01/06-02 执行结果标记 superseded,按同号重跑;数据端切片,非数据 render/legacy 覆盖延后) Plans: @@ -219,7 +219,7 @@ Plans: - [x] 06-03-PLAN.md — Enemy data model full public surface except legacy (data-base/src/enemy;无 save/load) - [x] 06-04-PLAN.md — Replay focus ReplayArray ops + encode/decode + system/sandbox/decorators (data-common/src/replay;无 save/load,完整播放→06-07) - [x] 06-05-PLAN.md — Hero ALL files incl. rendering + async mover (data-base/src/hero;无 save/load) -- [ ] 06-06-PLAN.md — Map ALL interfaces, emphasis static/dynamic tiles + static arrays (data-base/src/map;无 save/load) +- [x] 06-06-PLAN.md — Map ALL interfaces, emphasis static/dynamic tiles + static arrays (data-base/src/map;无 save/load) - [ ] 06-07-PLAN.md — Top-level integration: damage combos + map+replay play + second-play re-record equality; error 2001–2008 (BLOCKED: pre-execution user confirmation) - [ ] 06-08-PLAN.md — Flag full surface + common (utils/indexer/faceManager+face/mover) (无 save/load) - [ ] 06-09-PLAN.md — Save/load independent system: every saveState/loadState class + CoreState top-level (BLOCKED: pre-execution user confirmation) @@ -230,7 +230,7 @@ Plans: - [x] 06-02-PLAN.md - [x] 06-03-PLAN.md - [x] 06-04-PLAN.md -- [ ] 06-05-PLAN.md +- [x] 06-05-PLAN.md - [ ] 06-06-PLAN.md - [ ] 06-08-PLAN.md @@ -251,4 +251,4 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 | 3. 数据端完成 | 19/19 | Complete | 2026-09-12 | | 4. 渲染适配与双布局 | 0/TBD | Not started | - | | 5. Legacy 移植 | 0/TBD | Not started | - | -| 6. 单元测试 | 5/9 | In Progress| | +| 6. 单元测试 | 6/9 | In Progress| | diff --git a/.planning/STATE.md b/.planning/STATE.md index 3e88967..f62bef1 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,16 +4,16 @@ milestone: v1.0 current_phase: 06 current_phase_name: unit-tests status: executing -stopped_at: Completed 06-05-PLAN.md -last_updated: "2026-09-14T07:59:26.958Z" +stopped_at: Completed 06-06-PLAN.md +last_updated: "2026-09-14T08:32:08.508Z" last_activity: 2026-09-14 last_activity_desc: Phase 06 execution started -state_head: 351111487a69afd5a8b921c99996ac5c98bd7872 +state_head: 235eb1a5c9e17c985a28cc582248664e56385762 progress: total_phases: 6 completed_phases: 0 total_plans: 46 - completed_plans: 42 + completed_plans: 43 milestone_name: milestone --- @@ -29,7 +29,7 @@ See: .planning/PROJECT.md (updated 2026-09-10) ## Current Position Phase: 06 (unit-tests) — EXECUTING -Plan: 8 of 9 +Plan: 9 of 9 Status: Ready to execute Last activity: 2026-09-14 — Phase 06 execution started @@ -83,6 +83,7 @@ Progress: [█████░░░░░] 50% | Phase 06 P03 | 13min | 3 tasks | 5 files | | Phase 06 P04 | 26min | 3 tasks | 6 files | | Phase 06 P05 | 22min | 3 tasks | 10 files | +| Phase 06 P06 | 24min | 3 tasks | 8 files | ## Accumulated Context @@ -161,6 +162,10 @@ Recent decisions affecting current work: - [Phase 06]: 06-05:勇士全部子系统按 D-43 三阶段(构件→组合/流水线→完整/集成)以 10 个同目录测试覆盖,每阶段聚焦跑绿并过 D-44 门禁后提交 - [Phase 06]: 06-05:HeroAttribute 无修饰器时 final 属性陈旧(#06-05-1)、HeroEquipment 字符串槽位空槽判断写反(#06-05-2)与码 147 不可达(#06-05-3),按 D-05 写正确预期 it.skip 只记录不修复 - [Phase 06]: 06-05:D-32 不测任何 saveState/loadState,equipStore 专属码 58/59 归 06-09;mover 异步用真实计时器 + await controller.onEnd +- [Phase 06]: 06-06:地图全部按 D-43 三阶段(构件 → 组合/流水线 → 完整/集成)以 8 个同目录测试覆盖,每阶段聚焦跑绿并过 D-44 门禁后提交 +- [Phase 06]: 06-06:transferToDynamic 越图实际发码 131(setEventLayer 专属)而 transferToStatic 发 128,按 D-05 以正确预期 it.skip 登记 #06-06-1 待用户确认;131 正常覆盖由 gameMap.setEventLayer 越权路径承担 +- [Phase 06]: 06-06:IMapState 并无 canPass/shouldHit(实现在 data-state/src/hero/predicate.ts),mapState.test 只覆盖谓词侧依赖的「活跃楼层 → 事件层」数据供给;计划中的 createLayerState 码 121 实为 MapState.createMap 重复注册告警 +- [Phase 06]: 06-06:D-32 不测任何 saveState/loadState(55/122/124 归 06-09);MapTileBase 抽象类经 StaticTile/DynamicTile 具体子类覆盖,mover protected 回调为 no-op 故经公开钩子观测生命周期 ### Pending Todos @@ -189,6 +194,6 @@ None yet. ## Session Continuity -Last session: 2026-09-14T07:59:26.677Z -Stopped at: Completed 06-05-PLAN.md +Last session: 2026-09-14T08:32:08.256Z +Stopped at: Completed 06-06-PLAN.md Resume file: None diff --git a/.planning/phases/06-unit-tests/06-06-SUMMARY.md b/.planning/phases/06-unit-tests/06-06-SUMMARY.md new file mode 100644 index 0000000..560fa2d --- /dev/null +++ b/.planning/phases/06-unit-tests/06-06-SUMMARY.md @@ -0,0 +1,181 @@ +--- +phase: 06-unit-tests +plan: 06 +subsystem: testing +tags: [vitest, map, data-base, gamemap, maplayer, dynamic-tile, mover] + +requires: + - phase: 06-unit-tests + provides: 06-01..06-05 既有测试基建(vi.hoisted 全局 stub、logger.catch、真实计时器模式) +provides: + - MapState 注册/原始数据校验/激活/分区/参考基准覆盖 + - GameMap 图层生命周期/别名/背景/事件层/脏标记/resize 覆盖 + - MapLayer 矩阵/静态数组/动态转换/点位事件/脏/异步门覆盖 + - MapTileBase/StaticTile/DynamicTile/LayerEventView/DynamicTileMover 单元覆盖 + - 06-COVERAGE-MAP.md 的 06-06 小节(21 个 code) +affects: [06-07, 06-09] + +actuals: + tokens: 18374 + tasks: 3 + commits: 3 +plan_head_before: 6a6f717b040d71aad7ddb3970f10ad870f60448e + +tech-stack: + added: [] + patterns: + - "同目录 inline fixture + vi.hoisted 全局 stub(沿用 06-05 静态 import 形态)" + - "抽象基类 MapTileBase 经具体子类 StaticTile/DynamicTile 覆盖" + - "异步移动生命周期经 IObjectMover 公开钩子观测(protected 回调为 no-op)" + - "疑似缺陷按正确预期编写并 it.skip + 中文注释锚定 #06-06-N(D-05)" + +key-files: + created: + - packages-user/data-base/src/map/mapState.test.ts + - packages-user/data-base/src/map/gameMap.test.ts + - packages-user/data-base/src/map/mapLayer.test.ts + - packages-user/data-base/src/map/tile.test.ts + - packages-user/data-base/src/map/staticTile.test.ts + - packages-user/data-base/src/map/dynamicTile.test.ts + - packages-user/data-base/src/map/mover.test.ts + modified: + - packages-user/data-base/src/map/eventView.test.ts + +key-decisions: + - "06-06:地图全部按 D-43 三阶段(构件 → 组合/流水线 → 完整/集成)执行,每阶段聚焦跑绿并过 D-44 门禁后提交" + - "06-06:transferToDynamic 越图实际发码 131(setEventLayer 专属)而 transferToStatic 发 128,按 D-05 以正确预期 it.skip 登记 #06-06-1 待用户确认;131 的正常覆盖由 gameMap.setEventLayer 越权路径承担" + - "06-06:IMapState 并无 canPass/shouldHit(实现在 data-state/src/hero/predicate.ts),mapState.test 只覆盖谓词侧依赖的「活跃楼层 → 事件层」数据供给" + - "06-06:计划中的 createLayerState 码 121 实为 MapState.createMap 重复注册告警,测试按真实接口覆盖" + - "06-06:不测任何 saveState/loadState,55/122/124 归 06-09(D-32);MapTileBase 抽象类经具体子类覆盖" + +patterns-established: + - "地图测试 fixture:TileStore(1/2) + FaceManager(Dir8) + RoleFaceBinder + DirectionMapper 组装 IDataCommon,经 MapState.fromRaw 建 2x2 小地图" + - "越界/非法参数调用经宽松签名(LooseMapDataArgs)绕过重载类型,保留单处 as 断言" + +requirements-completed: [TEST-01] + +coverage: + - id: D1 + description: "阶段 1(构件级):tile/staticTile/dynamicTile/eventView 单个类单元覆盖(含码 136/143)" + requirement: TEST-01 + verification: + - kind: unit + ref: "packages-user/data-base/src/map/tile.test.ts" + status: pass + - kind: unit + ref: "packages-user/data-base/src/map/staticTile.test.ts" + status: pass + - kind: unit + ref: "packages-user/data-base/src/map/dynamicTile.test.ts" + status: pass + - kind: unit + ref: "packages-user/data-base/src/map/eventView.test.ts" + status: pass + human_judgment: false + - id: D2 + description: "阶段 2(组合/流水线):MapState 注册/校验/激活/分区 + GameMap 图层 + MapLayer 矩阵/静态数组/动态转换(含码 8/9/46/60/61/62/63/64/80/81/84/121/123/127/129/130/131)" + requirement: TEST-01 + verification: + - kind: unit + ref: "packages-user/data-base/src/map/mapState.test.ts" + status: pass + - kind: unit + ref: "packages-user/data-base/src/map/gameMap.test.ts" + status: pass + - kind: unit + ref: "packages-user/data-base/src/map/mapLayer.test.ts" + status: pass + human_judgment: false + - id: D3 + description: "阶段 3(完整/集成):DynamicTileMover 异步移动生命周期(含码 126)" + requirement: TEST-01 + verification: + - kind: unit + ref: "packages-user/data-base/src/map/mover.test.ts" + status: pass + human_judgment: false + - id: D4 + description: "疑似缺陷 #06-06-1(transferToDynamic 越图误发 131 而非 128)经正确预期 it.skip 登记,待用户确认后修复并转回归" + requirement: TEST-01 + verification: [] + human_judgment: true + rationale: "这是被测实现的行为/语义判定,需用户确认属于缺陷还是有意设计;D-05 要求先记录不修复,人工签字后方可取消 skip" + +duration: 24min +completed: 2026-09-14 +status: complete +--- + +# Phase 06 Plan 06: 地图系统单元测试 Summary + +**地图系统(data-base/src/map)按 构件→组合/流水线→完整/集成 三阶段补齐 8 个行为测试文件,覆盖 21 个 warn/error 码,聚焦静态图块、动态图块转换与静态数组设置** + +## Performance + +- **Duration:** 24 min +- **Started:** 2026-09-14T16:06:00+08:00 +- **Completed:** 2026-09-14T16:30:00+08:00 +- **Tasks:** 3 +- **Files modified:** 8(7 新建 + 1 扩展) + +## Accomplishments + +- 阶段 1:`tile`/`staticTile`/`dynamicTile`/`eventView` 构件级单元 26 通过;覆盖默认事件恢复、`shouldSave`、`toDynamic`、缺 raw 告警 143、重复优先级告警 136。 +- 阶段 2:`mapState`/`gameMap`/`mapLayer` 组合/流水线 63 通过 / 1 跳过;覆盖 raw 校验码 60/61/62/63/64、矩阵读写、`getMapData` 80/81、`putMapData` 8/9、`setMapRef` 123、动态转换 127/128/129/130、事件层 131、别名 84、楼层重复 121、异步开关门 46。 +- 阶段 3:`DynamicTileMover` 完整异步移动 6 通过;覆盖成功/连续/斜向移动、图层索引更新、非法移动码告警 126、移动生命周期钩子顺序。 +- `pnpm test:ci` 全绿:53 文件 / 505 通过 / 13 跳过(本计划新增 1 条 skip)。 +- `06-COVERAGE-MAP.md` 06-06 小节写入 21 个 code;`06-TEST-FINDINGS.md` 写入 `#06-06-1`。 + +## Task Commits + +Each task was committed atomically: + +1. **阶段 1(构件级)** - `4cffe90` (test) +2. **阶段 2(组合/流水线)** - `b971e14` (test) +3. **阶段 3(完整/集成)** - `235eb1a` (test) + +**Plan metadata:** 本次 docs 提交(SUMMARY/STATE/ROADMAP/COVERAGE-MAP/TEST-FINDINGS) + +## Files Created/Modified + +- `packages-user/data-base/src/map/tile.test.ts` - MapTileBase 经静态/动态图块覆盖:num/raw/set、默认事件、逐实例事件视图、pointEvent、setFaceDirection +- `packages-user/data-base/src/map/staticTile.test.ts` - StaticTile:num/raw/set/shouldSave/toDynamic 与越图 raw 为空 +- `packages-user/data-base/src/map/dynamicTile.test.ts` - DynamicTile:构造/设置告警 143、setPos、getCurrentFaceDirection、toStatic/toStaticIfSafe、step、delete +- `packages-user/data-base/src/map/eventView.test.ts` - 扩展:逐优先级脏跟踪、clear 恢复、未知优先级删除、重复优先级告警 136(保留既有覆盖) +- `packages-user/data-base/src/map/mapState.test.ts` - createMap/getMap、setMapList、fromRaw 校验码 60/61/62/63/64、激活迭代、分区、compareWith +- `packages-user/data-base/src/map/gameMap.test.ts` - 图层增删/别名 84、背景、事件层 131、脏、resizeLayer、compareWith +- `packages-user/data-base/src/map/mapLayer.test.ts` - 矩阵、getMapData 80/81、putMapData 8/9、setMapRef 123、动态转换、点位事件、脏、异步门 46 +- `packages-user/data-base/src/map/mover.test.ts` - DynamicTileMover 异步移动生命周期与告警 126 + +## Decisions Made + +- 三阶段严格按 D-43 执行,每阶段聚焦跑绿并过 D-44(eslint / vue-tsc 文件级 / `pnpm test:ci`)后再提交。 +- `transferToDynamic` 越图实际发码 131(`setEventLayer` 专属码)而 `transferToStatic` 发 128,按 D-05 以正确预期 `it.skip` 登记 `#06-06-1`;131 的正常覆盖由 `gameMap.setEventLayer` 越权路径承担,保证 code 全覆盖。 +- `IMapState` 并无 `canPass`/`shouldHit`(实现在 `data-state/src/hero/predicate.ts`),`mapState.test.ts` 只覆盖谓词侧依赖的「活跃楼层 → 事件层」数据供给。 +- 计划中的 `createLayerState` 码 121 实为 `MapState.createMap` 的重复楼层注册告警(码表文案沿用旧名),测试按真实接口覆盖。 +- D-32:不测任何 `saveState`/`loadState`(55/122/124 归 06-09)。 +- `MapTileBase` 为抽象类,经 `StaticTile`/`DynamicTile` 具体子类覆盖;`DynamicTileMover` 的 protected 回调为 no-op,生命周期经 `IObjectMover` 公开钩子观测。 + +## Deviations from Plan + +None - 无生产/核心源码改动,未触发 Rule 1-3 自动修复。两处**计划措辞与实现不符**已按真实接口覆盖并记录(见 Decisions 与 `06-COVERAGE-MAP.md` 06-06 小节),非代码缺陷、不登记为 bug。 + +## Issues Encountered + +暂无阻断性问题;三阶段顺序执行均一次跑绿并通过 D-44 门禁。 + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- 地图系统的行为覆盖已就绪,可供 06-07(顶层集成:伤害组合 + 录像播放/二次录制)使用合成地图场景。 +- 存读档集中计划 06-09 仍需覆盖 55/122/124 与全部 `saveState`/`loadState` 往返。 +- `#06-06-1` 待用户确认:若判定为缺陷,修复后将 `mover`/`mapLayer` 的 `it.skip` 取消并转回归用例。 + +--- +*Phase: 06-unit-tests* +*Completed: 2026-09-14* + +## Self-Check: PASSED diff --git a/.planning/phases/06-unit-tests/06-COVERAGE-MAP.md b/.planning/phases/06-unit-tests/06-COVERAGE-MAP.md index b1b1e38..daf7c80 100644 --- a/.planning/phases/06-unit-tests/06-COVERAGE-MAP.md +++ b/.planning/phases/06-unit-tests/06-COVERAGE-MAP.md @@ -50,6 +50,27 @@ | 144 | hero/mover.ts | `warns code 144 and stops without a top implementation` | 06-05 | | 146 | hero/equipment.ts | `warns code 146 when the equipped instance is missing`、`warns code 146 when comparing an unknown uid` | 06-05 | | 147 | hero/equipment.ts | `warns code 147 when no equipment slot is available`(`it.skip`,当前不可达,见 `#06-05-3`) | 06-05 | +| 8 | map/mapLayer.ts | `warns code 8 for incomplete data and code 9 for an out-of-range area` | 06-06 | +| 9 | map/mapLayer.ts | `warns code 8 for incomplete data and code 9 for an out-of-range area` | 06-06 | +| 46 | map/mapLayer.ts | `closes a door on empty cells and warns 46 on occupied cells` | 06-06 | +| 60 | map/mapState.ts | `unequal layer lengths is rejected with code 60` | 06-06 | +| 61 | map/mapState.ts | `area not divisible by width is rejected with code 61` | 06-06 | +| 62 | map/mapState.ts | `non-numeric map layer key is rejected with code 62`、`non-numeric event priority is rejected with code 62` | 06-06 | +| 63 | map/mapState.ts | `missing map container is rejected with code 63`、`missing events container is rejected with code 63`、`missing alias container is rejected with code 63` | 06-06 | +| 64 | map/mapState.ts | `invalid width is rejected with code 64`、`non-integer map value is rejected with code 64`、`non-string layer alias is rejected with code 64`、`out of range event position is rejected with code 64`、`non-string event id is rejected with code 64`、`event layer without map layer is rejected with code 64` | 06-06 | +| 80 | map/mapLayer.ts | `warns code 80 for an illegal argument count` | 06-06 | +| 81 | map/mapLayer.ts | `warns code 81 for an out-of-range region` | 06-06 | +| 84 | map/gameMap.ts | `binds aliases and warns 84 on a duplicate alias` | 06-06 | +| 121 | map/mapState.ts | `registers floors and warns 121 when the floor already exists` | 06-06 | +| 123 | map/mapLayer.ts | `warns code 123 on a length mismatch and replaces the reference otherwise` | 06-06 | +| 126 | map/mover.ts | `warns code 126 for an unexpected move code and keeps the position` | 06-06 | +| 127 | map/mapLayer.ts | `warns code 127 when transferring an empty block` | 06-06 | +| 128 | map/mapLayer.ts | `warns code 128 when transferToStatic is out of bounds`(`transferToDynamic` 越图当前误发 131,另见 `#06-06-1` 的 `it.skip` 正确预期用例) | 06-06 | +| 129 | map/mapLayer.ts | `warns code 129 when transferToStatic overwrites a static block` | 06-06 | +| 130 | map/mapLayer.ts | `warns code 130 for a tile not managed by the layer` | 06-06 | +| 131 | map/gameMap.ts | `accepts own layers, warns 131 for a foreign layer and clears on null` | 06-06 | +| 136 | map/eventView.ts | `warns code 136 when the same priority is set twice` | 06-06 | +| 143 | map/dynamicTile.ts | `warns code 143 when the raw tile data is missing`、`reflects num and raw and warns 143 on an unknown set` | 06-06 | ## 06-01 战斗系统(packages-user/data-system/src/combat) @@ -133,3 +154,27 @@ D-30:排除名称含 legacy 的接口/方法;`HeroMover` 顶层实现用内 用例登记,详见 `06-TEST-FINDINGS.md`;其中 147 无法触发,故本表该行标注为跳过用例。 `HeroMover` 测试复用真实 `HeroLocation` 作为移动宿主(非自定义 `TestTile`),以零断言代价 覆盖真实 tile 写回路径。 + +## 06-06 地图全部(packages-user/data-base/src/map) + +模块归属:60 / 61 / 62 / 63 / 64 / 121 → `map/mapState.ts`(`validateRaw` / `createMap`); +84 / 131 → `map/gameMap.ts`(`setLayerAlias` / `setEventLayer`); +8 / 9 / 46 / 80 / 81 / 123 / 127 / 128 / 129 / 130 → `map/mapLayer.ts` +(`putMapData` / `closeDoor` / `getMapData` / `setMapRef` / 动态转换); +126 → `map/mover.ts`(`DynamicTileMover.onStepEnd` 非法移动码); +136 → `map/eventView.ts`(重复优先级);143 → `map/dynamicTile.ts`(缺图块原始数据)。 + +阶段 1(构件级)完成 `tile` / `staticTile` / `dynamicTile` / `eventView` 单元,观测 136/143; +阶段 2(组合/流水线)完成 `MapState` 注册/校验/激活/分区与 `GameMap` 图层、`MapLayer` 矩阵/ +静态数组/动态转换/点位/脏/异步门,观测 8/9/46/60/61/62/63/64/80/81/84/121/123/127/129/130/131; +阶段 3(完整/集成)完成 `DynamicTileMover` 异步移动生命周期,观测 126。 + +D-32:不测任何 `saveState`/`loadState`,其专属码 55/122/124 与存读档往返归 06-09。 +D-30:排除名称含 legacy 的接口/方法;`MapTileBase` 为抽象类,经 `StaticTile`/`DynamicTile` 具体子类覆盖。 +D-18/D-39 计划措辞与实现的两处偏差(非代码缺陷,不登记为 bug): +其一,`IMapState` 并无 `canPass`/`shouldHit`,通行谓词实现在 `data-state/src/hero/predicate.ts`, +故 `mapState.test.ts` 只覆盖谓词侧依赖的「活跃楼层 → 事件层」数据供给; +其二,计划中的 `createLayerState` 码 121 实为 `MapState.createMap` 的重复注册告警(码表文案沿用旧名)。 +一条疑似缺陷 `#06-06-1`(`transferToDynamic` 越图误发 131 而非 128)按 D-05 以 `it.skip` 的 +正确预期用例登记,详见 `06-TEST-FINDINGS.md`;码 131 的触发则由 `gameMap.test.ts` 的 +`setEventLayer` 越权路径正常覆盖。 diff --git a/.planning/phases/06-unit-tests/06-TEST-FINDINGS.md b/.planning/phases/06-unit-tests/06-TEST-FINDINGS.md index c2697ca..6cc44e3 100644 --- a/.planning/phases/06-unit-tests/06-TEST-FINDINGS.md +++ b/.planning/phases/06-unit-tests/06-TEST-FINDINGS.md @@ -78,3 +78,23 @@ equipStore 专属码 58/59 归 06-09。D-30:排除名称含 legacy 的接口/ | `attribute.ts` `HeroAttribute.recalculateAttribute` | 属性没有任何修饰器时,基础属性变化不会反映到最终属性 | `new HeroAttribute({ hp: 100 })` → `set('hp', 40)` → `add('hp', 5)`;`getBaseAttribute('hp')` 为 45,但 `getFinalAttribute('hp')` 仍为构造时的 100 | `recalculateAttribute` 在 `const modifierList = this.modifier.get(name); if (!modifierList) return;` 处提前返回,未在该分支把基础值写回 `finalAttribute` | 所有尚无修饰器的属性(基础数值、升级/金币/经验等)在 `set`/`add`/`mul`/`div` 后读取最终属性会得到旧值;只有挂上修饰器后才会刷新 | 在无 `modifierList` 时同样执行 `this.finalAttribute[name] = baseValue`(或初始化时同步),保证 final 与 base 在没有加成时一致 | `attribute.test.ts` `reflects base-only changes without any modifier`(#06-05-1) | 中 | | `equipment.ts` `HeroEquipment.equip`(字符串槽位分支) | 存在空的同名装备槽时仍替换第一个匹配槽位,而不是占用空槽 | `slots = ['weapon', 'weapon']`;先将 A 装备到 `'weapon'`,再装备 B 到 `'weapon'`;实际 B 替换了 slot 0 的 A,slot 1 仍为空(正确预期 A 在 slot 0、B 在 slot 1) | 空槽判断条件写反:`if (empty !== -1 && !this.equips.has(index))` 应为 `empty === -1`;因 `empty` 初值即 -1,条件恒假,`empty` 永远保持 -1,`empty !== -1` 的「占用空槽」分支成为死代码 | 多同名槽位(如双武器槽)时装错位置,可能覆盖已装备道具;同时 `equip` 的空槽直装分支永不执行 | 将条件改为 `empty === -1 && !this.equips.has(index)`,恢复「优先占用空槽、无空槽才替换」的语义 | `equipment.test.ts` `uses the first empty named slot instead of replacing an occupant`(#06-05-2) | 中 | | `equipment.ts` `HeroEquipment.equip` 码 147 | 无可用装备槽告警(147)在当前实现下不可达 | `slots = []`、装备支持名称槽 `'weapon'` 时调用 `equip(uid, 'weapon')`:`canEquipTo` 因 `hasSlot === false` 先返回 `CannotEquip`,`equip` 提前返回,永远不会进入 `first === -1` 的 147 分支 | `canEquipTo` 的名称槽校验(`hasSlot`)与 `equip` 的 `first === -1` 判定使用同一条件,前者已拦截所有会使后者成立的情形;叠加 `#06-05-2` 的 `empty` 死值,空槽分支同样不可达 | 码 147 属死码,D-31 的 code 全覆盖无法通过触发断言满足;非法名称槽只能得到静默 `undefined` 而无诊断 | 明确 147 的触发语义:或在 `canEquipTo` 放行后由 `equip` 补齐诊断,或在码表中标注该码保留未用并移除死分支 | `equipment.test.ts` `warns code 147 when no equipment slot is available`(#06-05-3) | 低 | + +## #06-06 地图全部(packages-user/data-base/src/map) + +本计划按 D-43 三阶段(构件 → 组合/流水线 → 完整/集成)执行,8 个测试文件全部跑绿 +(阶段 1:26 通过;阶段 2:63 通过 / 1 跳过;阶段 3:6 通过);`pnpm test:ci` 全绿 +(53 文件 / 505 通过 / 13 跳过,其中 1 条为本计划新增 skip)。 +D-32:不测任何 `saveState`/`loadState`,55/122/124 与存读档往返归 06-09。 +D-30:排除名称含 legacy 的接口/方法;两处**计划措辞与实现不符**(非代码缺陷,不登记为 bug): + +- `IMapState` 并没有 `canPass`/`shouldHit`;通行谓词实现在 `data-state/src/hero/predicate.ts` + (`DefaultPassPredicateImpl`,消费 `IMapState` 的活跃/普通楼层与事件层)。故 `mapState.test.ts` + 只覆盖谓词所依赖的「活跃楼层 → 事件层」数据供给,不测试并不存在的 MapState 谓词方法。 +- 计划中的「`createLayerState` 告警 121」实为 `MapState.createMap` 的重复楼层注册告警 + (码 121 的文案沿用了旧名 `MapStore.createLayerState`),测试按真实接口 `createMap` 覆盖。 + +发现 1 处疑似缺陷(`#06-06-1`),按 D-05 以正确预期的 `it.skip` 用例登记。 + +| 模块/接口 | 现象 | 最小复现 | 疑似原因 | 影响面 | 建议修复方向 | 关联 skip 用例 | 严重度 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `mapLayer.ts` `MapLayer.transferToDynamic`(越图分支) | 越图转换发出的诊断码与语义不符:发的是 setEventLayer 专属码 131,且返回 null | 2x2 图层上调用 `layer.transferToDynamic(9, 9)`;`logger.catch` 捕获到码 131(`Cannot set event layer since target map layer does not belongs to current GameMap instance.`),返回 null | `if (!this.inMap(x, y))` 分支写成 `logger.warn(131, x, y)`;对照同文件 `transferToStatic`/`transferToStaticIfSafe` 的越界分支发码 128(`Cannot transfer ... out of bounds.`),此处应为 128 | 越界转换的诊断码错误,人工/回放诊断可能误判为事件层绑定问题;行为(返回 null、不产生动态图块)本身正确 | 将该分支改发 128,与 `transferToStatic` 的越界语义保持一致 | `mapLayer.test.ts` `warns code 128 for an out-of-map transferToDynamic`(#06-06-1) | 低 | diff --git a/.planning/state.json b/.planning/state.json index 04b95c7..796312e 100644 --- a/.planning/state.json +++ b/.planning/state.json @@ -39,5 +39,5 @@ "label": "Advance to the next step", "reason": "Phase 06 of 6 · executing" }, - "updated_at": "2026-09-14T07:58:49.898Z" + "updated_at": "2026-09-14T08:31:22.162Z" }