新增门禁:实现访问管理界面与Mock端到端测试

This commit is contained in:
wangzhibiao committed 2026-09-16 11:43:58 +08:00
1 parent 20c5d494cf
commit d976fcfb4d
29 files changed
+2423 -281

No files matched your search

@@ -242,3 +242,33 @@ flowchart TD
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `009_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`009_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`。
### 3.6 Wave 3B Implementation Result (Admin UI / Mock E2E 闭环验收)
- **阶段目标**:Admin UI 与 Mock DMS 15 大场景端到端验收闭环
- **执行状态**:Wave 3B implemented and verified against Mock DMS
- **核心交付清单**:
1. 权限迁移脚本:`RuoYi-Vue/sql/system/kingbase/010_dms_access_management_permissions.sql`(安全编号 010,幂等插入 `train:access:query`、`train:access:manage`、`train:access:reconcile` 权限)。
2. 控制器与脱敏 API:`TrainAccessManagementController.java`(提供外部资源映射查询/管理、门禁权限列表、脱敏操作流水、受前置条件约束的手动对账)。
3. 权限契约与接口测试:`DmsAccessManagementPermissionMigrationContractTest.java` (3 项)、`TrainAccessManagementControllerTest.java` (5 项)。
4. 完整 15 场景 Mock E2E 套件:`DmsMockE2EScenariosSuiteTest.java` (15 项全部通过)。
5. 前端 API 封装:`build_ui/src/api/train/access.js`。
6. 宿舍房间外部映射 UI:`build_ui/src/views/train/dormitory/index.vue`(集成主锁健康度诊断 `HEALTHY`、`MISSING_PRIMARY`、`MULTIPLE_PRIMARIES`)。
7. 门禁权限管理 UI:`build_ui/src/views/train/auth/index.vue`(生命周期状态徽章、脱敏流水弹窗、手动对账反馈)。
8. 系统名称规约统一:`SYFJGYD学员管理系统`(`.env.development`、`.env.production`、`Logo.vue`)。
9. 交互证据沉淀:`docs/reports/dms-r3/mock-e2e/20260916_033500/`(含 README、report、run-data、scenario-results、security-results 及 10 张真实截图)。
- **全量构建与回归验收**:
- `ruoyi-train`: 234 tests
- `ruoyi-admin`: 37 tests
- 总计:271 tests, Failures = 0, Errors = 0, Skipped = 0,全部通过。
- Maven package: SUCCESS。
- 前端构建与代码风格:`npm run build:prod` PASS,ESLint errors = 0, warnings = 0。
- 静态检查:`git diff --check` PASS。
- **环境纪律与准入门禁**:
- `LIVE_DMS_REQUEST_COUNT = 0`。
- `PASSWORD_PLAINTEXT_PERSISTENCE_COUNT = 0`。
- `SECRET_UI_EXPOSURE_COUNT = 0`。
- `REMOTE_UNLOCK_CALLABLE_ENTRY_COUNT = 0`。
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `009_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`009_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `010_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`010_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`。
@@ -0,0 +1,42 @@
# RailwaySystem R.3B Wave 3B Mock E2E 验收报告目录
## 1. 运行环境说明
- **测试环境**: MOCK DMS ONLY
- **运行编号 (Run ID)**: `20260916_033500`
- **Worktree**: `/Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3`
- **分支 (Branch)**: `feature/dms-r3`
- **Wave 3A Commit**: `fc4898ca2f40d8ea2cd5f8914f815ae8be3faa36`
- **系统名称**: `SYFJGYD学员管理系统`
- **绝对禁止**:
- 严禁直连真实 DMS 生产/测试网关 (`LIVE_DMS_REQUEST_COUNT = 0`)
- 严禁在生产或 Kingbase 运行时执行 Migration (`008/009/010_RUNTIME = NOT_EXECUTED`)
- 严禁执行脚本 007 (`007 DO NOT EXECUTE`)
- 严禁 push、严禁 deploy
---
## 2. 交付证据文件索引
| 文件名 | 说明 |
| :--- | :--- |
| [`report.md`](file:///Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3/docs/reports/dms-r3/mock-e2e/20260916_033500/report.md) | Wave 3B 完整端到端验收与安全性审查报告 |
| [`run-data.json`](file:///Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3/docs/reports/dms-r3/mock-e2e/20260916_033500/run-data.json) | 本次 E2E 执行环境、指标与元数据配置 |
| [`scenario-results.json`](file:///Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3/docs/reports/dms-r3/mock-e2e/20260916_033500/scenario-results.json) | 15 项核心编排与边界场景测试结果明细 (100% PASS) |
| [`security-results.json`](file:///Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3/docs/reports/dms-r3/mock-e2e/20260916_033500/security-results.json) | 安全合规性审查 (明文密码持久化=0, 敏感暴露=0, 远程开门=0) |
| `screenshots/` | 10 张真实前端界面与交互截图 |
---
## 3. UI 交互截图清单 (10 张)
1. `01-dorm-mapping.png`: 宿舍管理列表及房间外部资源映射配置
2. `02-lock-mapping.png`: 房间外部门锁映射列表及主锁/次锁展示
3. `03-auth-pending.png`: 门禁权限管理 - 待授权 (PENDING_GRANT) 状态展示
4. `04-auth-active.png`: 门禁权限管理 - 已生效 (ACTIVE) 状态展示
5. `05-operation-history.png`: 门禁设备操作流水弹窗 (脱敏展示,无密钥泄露)
6. `06-manual-reconcile.png`: 手动对账交互入口与前置业务校验反馈
7. `07-config-error-no-primary.png`: 缺少主锁 (0 Primary Lock) 异常阻断与诊断提示
8. `08-transfer-new-active.png`: 调房场景 - 新房门禁授权生效 (New ACTIVE)
9. `09-transfer-old-revoked.png`: 调房场景 - 旧房门禁授权注销 (Old REVOKED)
10. `10-grant-failure-recoverable.png`: 授权下发失败 (ERROR) 状态保留与可重试对账
@@ -0,0 +1,120 @@
# RailwaySystem R.3B Wave 3B 验收审查报告 (Mock E2E)
**执行环境**: MOCK DMS ONLY (严禁生产与真实Sandbox调用)
**运行编号 (RUN_ID)**: `20260916_033500`
**基线 Worktree**: `/Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3`
**基线分支**: `feature/dms-r3`
**Wave 3A Commit**: `fc4898ca2f40d8ea2cd5f8914f815ae8be3faa36`
**系统名称**: `SYFJGYD学员管理系统`
**状态**: **PASS**
---
## 1. 目标与执行范围
本阶段任务为 RailwaySystem R.3B 的 Wave 3B(Admin UI 与 Mock E2E 完整闭环)。在已冻结的领域模型(Accommodation、DoorAuthorization、ExternalResourceMapping、ExternalOperation)与 DMS 适配器及编排逻辑之上,实现了:
1. 管理员 DMS/门禁配置与状态查看 UI(宿舍房间映射管理、门禁权限查看、操作流水审计)
2. 房间外部资源与门锁映射诊断(主锁唯一性校验与告警:0 主锁或 >1 主锁均明确阻断并告警)
3. 门禁状态完整生命周期展示 (`PENDING_GRANT`, `GRANTING`, `ACTIVE`, `PENDING_REVOKE`, `REVOKING`, `REVOKED`, `ERROR`)
4. 操作流水脱敏审计(绝不向前端暴露 token、appsecret、密码明文或原始敏感请求报文)
5. 手动对账入口(管理员对账必须严格遵循业务前置校验,不强行绕过规则)
6. 覆盖 15 项核心业务与边界场景的端到端自动化测试
7. 严格的静态代码与安全规约审查
---
## 2. 核心业务策略与冻结语义
### 2.1 门锁选择策略 (PRIMARY_LOCK_ONLY)
- 外部设备仅对“主门锁”(`is_primary = 1`)下发临时入驻密码凭证。
- 当房间配置 0 个主门锁时:UI 呈现诊断告警 `MISSING_PRIMARY`,编排服务拒绝下发并标记配置错误。
- 当房间配置 >1 个主门锁时:UI 呈现诊断告警 `MULTIPLE_PRIMARIES`,编排服务拒绝下发并标记配置错误。
- 当房间配置 1 个主锁与多个副锁时:仅对主锁执行密码下发。
### 2.2 调房编排安全原则 (NEW_ACTIVE_THEN_REVOKE_OLD)
- 学员调房时,先对新房间执行门禁下发。
- 只有新房间门锁授权确认生效(`ACTIVE`)后,才触发旧房间门禁权限注销(`REVOKING` -> `REVOKED`)。
- 若新房间门锁下发失败,则旧房间门锁授权继续保持 `ACTIVE`,绝不提前吊销旧房授权,保障学员住宿安全。
### 2.3 业务事务独立性 (TRANSACTION_INDEPENDENCE)
- 学员报到确认(Report Confirm)与门禁授权下发解耦:DMS 网络故障或重试不导致报到业务事务回滚。
- 学员结训退房(Checkout/Plan Complete)与门禁吊销解耦:DMS 吊销失败或网络异常不导致结训退房事务回滚。
### 2.4 开放性业务问题保持 (OPEN QUESTIONS)
- `CREDENTIAL_DELIVERY_POLICY`: `OPEN_QUESTION`(前端统一提示“凭证待业务侧安全交付方案确认”,无明文密码展示)。
- `LEAVE_AUTO_REVOKE_POLICY`: `OPEN_QUESTION`(学员请假不自动吊销门禁权限)。
- `VALIDITY_WINDOW_SOURCE`: `BUSINESS_POLICY_REQUIRED`(无真实业务起止时间时展示 WAITING_FOR_PREREQUISITE,不伪造假日期)。
---
## 3. Mock E2E 15 大场景自动化测试验证
自动化测试套件 `com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest` 完整覆盖以下 15 个场景:
| 序号 | 测试场景 | 预期行为与状态转移 | 测试结果 |
| :---: | :--- | :--- | :---: |
| 1 | 先 Accommodation 后 Report | Grant -> accepted -> poll success -> `ACTIVE` | **PASS** |
| 2 | 先 Report 后 Accommodation | WAITING -> Accommodation 后 Grant -> `ACTIVE` | **PASS** |
| 3 | 重复 Report + Reconcile x5 | 幂等控制:生成 1 条 DoorAuthorization,仅调用 1 次凭证生成 | **PASS** |
| 4 | Grant 外部调用失败 | Authorization 状态非 ACTIVE,生成 ERROR 操作并支持重试生成新 Operation | **PASS** |
| 5 | Grant 轮询超时 | 状态非 ACTIVE,保留流水记录与尝试次数 | **PASS** |
| 6 | 学员退训 | Revoke -> poll success -> `REVOKED` | **PASS** |
| 7 | 培训计划完成 | Revoke -> poll success -> `REVOKED` | **PASS** |
| 8 | 学员请假 | 保持现有授权,不自动触发 Revoke | **PASS** |
| 9 | 调房成功 | 先 Grant 新房 -> 新房 `ACTIVE` -> Revoke 旧房 -> 旧房 `REVOKED` | **PASS** |
| 10 | 调房新房 Grant 失败 | 旧房依然保持 `ACTIVE`,旧房 revoke count = 0 | **PASS** |
| 11 | 房间 0 主锁 | 阻断 Grant,记录配置异常 | **PASS** |
| 12 | 房间 2 主锁 | 阻断 Grant,记录配置异常 | **PASS** |
| 13 | 1 主锁 + 多副锁 | 仅对主门锁执行凭证下发 (PRIMARY_LOCK_ONLY) | **PASS** |
| 14 | DMS 网络故障 (报到) | 报到业务确认成功提交,DMS 异常进入重试流水,报到不回滚 | **PASS** |
| 15 | DMS 吊销故障 (结训) | 结训与退房业务成功提交,吊销流水记录失败,结训不回滚 | **PASS** |
---
## 4. UI 界面与交互验证 (10 项关键截图)
截图保存路径: `docs/reports/dms-r3/mock-e2e/20260916_033500/screenshots/`
1. **`01-dorm-mapping.png`**: 宿舍房间管理列表与外部房间资源映射配置弹窗。
2. **`02-lock-mapping.png`**: 房间外部门锁映射列表,清晰标明主锁(Primary Lock)状态。
3. **`03-auth-pending.png`**: 门禁权限管理列表中筛选待授权(`PENDING_GRANT`)状态。
4. **`04-auth-active.png`**: 门禁权限管理列表中展示已生效(`ACTIVE`)状态。
5. **`05-operation-history.png`**: 门禁操作流水详情弹窗,严格脱敏审计字段。
6. **`06-manual-reconcile.png`**: 手动对账入口,校验业务前置条件及反馈。
7. **`07-config-error-no-primary.png`**: 缺少主锁时的红色警告诊断提示与阻断信息。
8. **`08-transfer-new-active.png`**: 调房操作后新房间门禁授权即刻生效展示。
9. **`09-transfer-old-revoked.png`**: 调房操作后旧房间门禁权限完成注销展示。
10. **`10-grant-failure-recoverable.png`**: 授权下发失败异常记录展示,允许管理员发起对账重试。
---
## 5. 安全合规审计结论
| 审查项 | 规约要求 | 实测结果 | 结论 |
| :--- | :--- | :--- | :---: |
| 明文密码持久化 | `password plaintext persistence = 0` | 数据库表结构与 Mapper 无任何明文密码字段 | **PASS** |
| 界面敏感信息暴露 | `secret UI exposure = 0` | 页面无 token、appsecret、password 展示 | **PASS** |
| 远程开门接口 | `remote unlock callable entry = 0` | 后端无 `/train/dms/remote-unlock`,前端无开门按钮 | **PASS** |
| 教师/学员越权访问 | Teacher/Student access = denied | `@PreAuthorize` 严格绑定系统管理独立权限 | **PASS** |
| 手动对账前置绕过 | Cannot bypass prerequisites | 未报到/无住宿/锁配置异常均被严格拦截 | **PASS** |
| 操作流水脱敏 | Operation history sanitized | 仅暴露脱敏 Operation 结构体 | **PASS** |
| 真实 DMS 网络调用 | `LIVE_DMS_REQUEST_COUNT = 0` | 默认配置 `dms.enabled=false`,全量执行 Mock | **PASS** |
---
## 6. Runtime Migration 门禁状态
- `008_RUNTIME_FIRST_RUN`: `NOT_EXECUTED`
- `008_RUNTIME_SECOND_RUN`: `NOT_EXECUTED`
- `009_RUNTIME_FIRST_RUN`: `NOT_EXECUTED`
- `009_RUNTIME_SECOND_RUN`: `NOT_EXECUTED`
- `010_RUNTIME_FIRST_RUN`: `NOT_EXECUTED`
- `010_RUNTIME_SECOND_RUN`: `NOT_EXECUTED`
- **规约声明**: 严格遵守禁止在测试环境执行真实 Kingbase 生产变更;已通过对应 Migration 契约单元测试保障语法与幂等性。
---
## 7. 最终审查结论
Wave 3B 所有后端功能、前端页面、权限控制、自动化单测、15 个 Mock E2E 场景、10 张交互截图及安全审计均已 **100% 达成**。
`R3_WAVE3B_STATUS = PASS`
@@ -0,0 +1,36 @@
{
"run_id": "20260916_033500",
"worktree": "/Users/wzb/Desktop/项目/RailwaySystem-worktrees/dms-r3",
"branch": "feature/dms-r3",
"environment": "MOCK DMS ONLY",
"system_title": "SYFJGYD学员管理系统",
"wave3a_commit": "fc4898ca2f40d8ea2cd5f8914f815ae8be3faa36",
"timestamp": "2026-09-16T11:35:00+08:00",
"author": "Antigravity Agent",
"metrics": {
"live_dms_request_count": 0,
"password_plaintext_persistence_count": 0,
"secret_ui_exposure_count": 0,
"remote_unlock_callable_entry_count": 0,
"backend_total_tests": 271,
"backend_failures": 0,
"backend_errors": 0,
"frontend_build": "PASS",
"eslint_errors": 0,
"eslint_warnings": 0,
"screenshot_count": 10
},
"runtime_migration_gates": {
"008_RUNTIME_FIRST_RUN": "NOT_EXECUTED",
"008_RUNTIME_SECOND_RUN": "NOT_EXECUTED",
"009_RUNTIME_FIRST_RUN": "NOT_EXECUTED",
"009_RUNTIME_SECOND_RUN": "NOT_EXECUTED",
"010_RUNTIME_FIRST_RUN": "NOT_EXECUTED",
"010_RUNTIME_SECOND_RUN": "NOT_EXECUTED"
},
"open_questions": {
"CREDENTIAL_DELIVERY_POLICY": "OPEN_QUESTION",
"LEAVE_AUTO_REVOKE_POLICY": "OPEN_QUESTION",
"VALIDITY_WINDOW_SOURCE": "BUSINESS_POLICY_REQUIRED"
}
}
@@ -0,0 +1,115 @@
{
"run_id": "20260916_033500",
"environment": "MOCK DMS ONLY",
"total_scenarios": 15,
"passed_scenarios": 15,
"failed_scenarios": 0,
"overall_status": "PASS",
"scenarios": [
{
"index": 1,
"name": "先Accommodation后Report",
"expectation": "Grant -> accepted -> poll success -> ACTIVE",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario01_accommodationFirstThenReport_grantsAndBecomesActive",
"status": "PASS"
},
{
"index": 2,
"name": "先Report后Accommodation",
"expectation": "WAITING -> Accommodation后Grant -> ACTIVE",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario02_reportFirstThenAccommodation_grantsAndBecomesActive",
"status": "PASS"
},
{
"index": 3,
"name": "重复Report + reconcile x5",
"expectation": "authorization 1条 -> credential create 1次 (幂等)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario03_duplicateReportAndReconcile_idempotentSingleAuthAndCredential",
"status": "PASS"
},
{
"index": 4,
"name": "Grant failed",
"expectation": "Authorization非ACTIVE -> retry生成新Operation",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario04_grantFailed_nonActiveAndRetryCreatesNewOperation",
"status": "PASS"
},
{
"index": 5,
"name": "Grant timeout",
"expectation": "不ACTIVE -> 历史保留",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario05_grantTimeout_retainsHistoryAndNonActive",
"status": "PASS"
},
{
"index": 6,
"name": "退训",
"expectation": "Revoke -> poll success -> REVOKED",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario06_dropOut_revokesAndBecomesRevoked",
"status": "PASS"
},
{
"index": 7,
"name": "完成培训",
"expectation": "Revoke -> REVOKED",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario07_planComplete_revokesAndBecomesRevoked",
"status": "PASS"
},
{
"index": 8,
"name": "请假",
"expectation": "不自动Revoke (保持OPEN_QUESTION,保留现有授权)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario08_studentLeave_doesNotAutoRevoke",
"status": "PASS"
},
{
"index": 9,
"name": "调房成功",
"expectation": "Grant new first -> New ACTIVE -> Revoke old -> Old REVOKED",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario09_transferRoomSuccess_newActiveThenOldRevoked",
"status": "PASS"
},
{
"index": 10,
"name": "调房新房Grant失败",
"expectation": "Old仍ACTIVE -> Old revoke count = 0",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario10_transferRoomNewGrantFailure_oldRemainsActiveAndNotRevoked",
"status": "PASS"
},
{
"index": 11,
"name": "0 primary lock",
"expectation": "no grant (配置异常提示)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario11_zeroPrimaryLock_noGrant",
"status": "PASS"
},
{
"index": 12,
"name": "2 primary locks",
"expectation": "config error / no grant (配置异常提示)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario12_twoPrimaryLocks_configErrorAndNoGrant",
"status": "PASS"
},
{
"index": 13,
"name": "1 primary + multiple secondary locks",
"expectation": "只 grant primary (PRIMARY_LOCK_ONLY)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario13_onePrimaryAndMultipleSecondary_grantsOnlyPrimary",
"status": "PASS"
},
{
"index": 14,
"name": "DMS network failure on Report",
"expectation": "Report Confirm 不回滚 (事务解耦,独立记录重试状态)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario14_dmsNetworkFailure_reportConfirmDoesNotRollback",
"status": "PASS"
},
{
"index": 15,
"name": "DMS revoke failure on Checkout",
"expectation": "checkout / plan complete 不回滚 (事务解耦,业务终态不受外部依赖影响)",
"test_method": "com.ruoyi.train.dms.DmsMockE2EScenariosSuiteTest#scenario15_dmsRevokeFailure_checkoutDoesNotRollback",
"status": "PASS"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

@@ -0,0 +1,46 @@
{
"run_id": "20260916_033500",
"environment": "MOCK DMS ONLY",
"audit_timestamp": "2026-09-16T11:35:00+08:00",
"security_verdict": "PASS",
"checks": {
"password_plaintext_persistence_count": {
"count": 0,
"status": "PASS",
"detail": "No plaintext passcodes stored in train_dorm_auth, dms_external_operation, or any persistent table."
},
"secret_ui_exposure_count": {
"count": 0,
"status": "PASS",
"detail": "No token, appsecret, password, or raw request/response payloads exposed in frontend views or API responses."
},
"remote_unlock_callable_entry_count": {
"count": 0,
"status": "PASS",
"detail": "No /train/dms/remote-unlock API endpoint or UI trigger implemented or permitted."
},
"teacher_access_to_admin_dms_api": {
"status": "DENIED",
"detail": "Strictly restricted by Spring Security @PreAuthorize('hasPermi(...)') with dedicated train:access permissions."
},
"student_access_to_admin_dms_api": {
"status": "DENIED",
"detail": "Students have no access privileges to /train/access/** endpoints."
},
"manual_reconcile_prerequisite_bypass": {
"bypassed": false,
"status": "PASS",
"detail": "reconcile(enrollmentId) strictly enforces enrollment status, accommodation presence, room mapping, and exactly one primary lock."
},
"operation_history_sanitized": {
"sanitized": true,
"status": "PASS",
"detail": "DmsSanitizedOperationVO only returns non-sensitive fields (id, type, status, attempt, timestamps, sanitized error message)."
},
"live_dms_request_count": {
"count": 0,
"status": "PASS",
"detail": "dms.enabled=false by default in application.yml. All E2E tests target MockDmsServer exclusively."
}
}
}