test(p1r): 固化 Account 真实 API 覆盖门禁

This commit is contained in:
zizi 2026-05-30 02:21:06 +08:00
parent c4a8f4ea5d
commit 0dcaa9f581
3 changed files with 1356 additions and 217 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
# P1R API Coverage Report
- Generated at: `2026-05-25T18:47:44+00:00`
- Generated at: `2026-05-25T01:55:37+00:00`
- Total operations: `233`
- Needs verification: `67`
- Incomplete: `166`
- Needs verification: `100`
- Incomplete: `133`
- Catch-all: `0`
- Generic persistence: `164`
- Generic persistence: `131`
- SSE placeholder: `2`
- Missing: `0`
@ -13,39 +13,39 @@
| Domain | Side | Method | Path | Operation | Implementation | Completion | Target Stage |
|--------|------|--------|------|-----------|----------------|------------|--------------|
| account | admin | POST | `/admin-api/muse/account/call-attribution-jobs` | `adminCreateCallAttributionJob` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `adminGetCallAttributionJob` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `adminGetIntegrationCallByCorrelation` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/new-api-bindings` | `adminListNewApiBindings` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/purchase-records` | `adminListPurchaseRecords` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/usage-records` | `adminListUsageRecords` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users` | `adminListAccountUsers` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `adminGetBalanceSnapshots` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users/{userId}/entitlements` | `adminGetUserEntitlements` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | `adminCreateNewApiBinding` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `adminListQuotaAdjustments` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `adminCreateQuotaAdjustment` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | `adminCreateQuotaRequest` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/balance-snapshots` | `getAppBalanceSnapshots` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/downloads/{credentialId}` | `appDownloadExport` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/entitlements` | `getAppEntitlements` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/export-tasks` | `appCreateExportTask` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/export-tasks/{taskId}` | `appGetExportTask` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `appGetIntegrationCallByCorrelation` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/licenses` | `appListLicenses` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/new-api-binding` | `getAppNewApiBinding` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/new-api-binding/recheck` | `appRecheckNewApiBinding` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/publish-records` | `appListPublishRecords` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/purchases` | `appListPurchases` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/quota-requests` | `appCreateQuotaRequest` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/quota-requests/{requestId}` | `appGetQuotaRequest` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/security-events` | `appListSecurityEvents` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/security-events/{eventId}` | `appGetSecurityEvent` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `appAcknowledgeSecurityEvent` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/usage` | `getAppUsage` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/me` | `getCurrentUser` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/profile` | `getProfile` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | app | PATCH | `/app-api/muse/profile` | `updateProfile` | generic_persistence | incomplete | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/call-attribution-jobs` | `adminCreateCallAttributionJob` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `adminGetCallAttributionJob` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `adminGetIntegrationCallByCorrelation` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/new-api-bindings` | `adminListNewApiBindings` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/purchase-records` | `adminListPurchaseRecords` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/usage-records` | `adminListUsageRecords` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users` | `adminListAccountUsers` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `adminGetBalanceSnapshots` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users/{userId}/entitlements` | `adminGetUserEntitlements` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | `adminCreateNewApiBinding` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | GET | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `adminListQuotaAdjustments` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `adminCreateQuotaAdjustment` | dedicated | needs_verification | P1R-3 Account Real API |
| account | admin | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | `adminCreateQuotaRequest` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/balance-snapshots` | `getAppBalanceSnapshots` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/downloads/{credentialId}` | `appDownloadExport` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/entitlements` | `getAppEntitlements` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/export-tasks` | `appCreateExportTask` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/export-tasks/{taskId}` | `appGetExportTask` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `appGetIntegrationCallByCorrelation` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/licenses` | `appListLicenses` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/new-api-binding` | `getAppNewApiBinding` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/new-api-binding/recheck` | `appRecheckNewApiBinding` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/publish-records` | `appListPublishRecords` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/purchases` | `appListPurchases` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/quota-requests` | `appCreateQuotaRequest` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/quota-requests/{requestId}` | `appGetQuotaRequest` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/security-events` | `appListSecurityEvents` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/security-events/{eventId}` | `appGetSecurityEvent` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `appAcknowledgeSecurityEvent` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/account/usage` | `getAppUsage` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/me` | `getCurrentUser` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | GET | `/app-api/muse/profile` | `getProfile` | dedicated | needs_verification | P1R-3 Account Real API |
| account | app | PATCH | `/app-api/muse/profile` | `updateProfile` | dedicated | needs_verification | P1R-3 Account Real API |
| ai | admin | GET | `/admin-api/muse/ai/agents` | `adminListAgents` | generic_persistence | incomplete | P1R-4 AI Real API |
| ai | admin | POST | `/admin-api/muse/ai/agents` | `adminCreateAgent` | generic_persistence | incomplete | P1R-4 AI Real API |
| ai | admin | POST | `/admin-api/muse/ai/agents/{agentId}/versions` | `adminCreateAgentVersion` | generic_persistence | incomplete | P1R-4 AI Real API |
@ -249,39 +249,6 @@
## Incomplete Or Blocked
- `adminCreateCallAttributionJob` `POST /admin-api/muse/account/call-attribution-jobs`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminGetCallAttributionJob` `GET /admin-api/muse/account/call-attribution-jobs/{jobId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminGetIntegrationCallByCorrelation` `GET /admin-api/muse/account/integration-calls/by-correlation/{correlationId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminListNewApiBindings` `GET /admin-api/muse/account/new-api-bindings`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminListPurchaseRecords` `GET /admin-api/muse/account/purchase-records`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminListUsageRecords` `GET /admin-api/muse/account/usage-records`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminListAccountUsers` `GET /admin-api/muse/account/users`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminGetBalanceSnapshots` `GET /admin-api/muse/account/users/{userId}/balance-snapshots`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminGetUserEntitlements` `GET /admin-api/muse/account/users/{userId}/entitlements`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminCreateNewApiBinding` `POST /admin-api/muse/account/users/{userId}/new-api-binding`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminListQuotaAdjustments` `GET /admin-api/muse/account/users/{userId}/quota-adjustments`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminCreateQuotaAdjustment` `POST /admin-api/muse/account/users/{userId}/quota-adjustments`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminCreateQuotaRequest` `POST /admin-api/muse/account/users/{userId}/quota-requests`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `getAppBalanceSnapshots` `GET /app-api/muse/account/balance-snapshots`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appDownloadExport` `GET /app-api/muse/account/downloads/{credentialId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `getAppEntitlements` `GET /app-api/muse/account/entitlements`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appCreateExportTask` `POST /app-api/muse/account/export-tasks`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appGetExportTask` `GET /app-api/muse/account/export-tasks/{taskId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appGetIntegrationCallByCorrelation` `GET /app-api/muse/account/integration-calls/by-correlation/{correlationId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appListLicenses` `GET /app-api/muse/account/licenses`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `getAppNewApiBinding` `GET /app-api/muse/account/new-api-binding`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appRecheckNewApiBinding` `POST /app-api/muse/account/new-api-binding/recheck`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appListPublishRecords` `GET /app-api/muse/account/publish-records`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appListPurchases` `GET /app-api/muse/account/purchases`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appCreateQuotaRequest` `POST /app-api/muse/account/quota-requests`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appGetQuotaRequest` `GET /app-api/muse/account/quota-requests/{requestId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appListSecurityEvents` `GET /app-api/muse/account/security-events`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appGetSecurityEvent` `GET /app-api/muse/account/security-events/{eventId}`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `appAcknowledgeSecurityEvent` `POST /app-api/muse/account/security-events/{eventId}/acknowledge`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `getAppUsage` `GET /app-api/muse/account/usage`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `getCurrentUser` `GET /app-api/muse/me`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `getProfile` `GET /app-api/muse/profile`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `updateProfile` `PATCH /app-api/muse/profile`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminListAgents` `GET /admin-api/muse/ai/agents`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminCreateAgent` `POST /admin-api/muse/ai/agents`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.
- `adminCreateAgentVersion` `POST /admin-api/muse/ai/agents/{agentId}/versions`: generic_persistence - Handled by MuseContractPersistenceService generic persistence, which is not P1R completion.

View File

@ -0,0 +1,193 @@
package cn.iocoder.muse.server.framework.api;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import static java.util.Map.entry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
/**
* P1R-3 Account 真实 API 阶段覆盖报告门禁
*
* <p>这个 gate 只证明 P1R-3 coverage 已收口到 dedicated / needs_verification
* 不证明 Account 业务 runtime 已经 completed后续如果要推进 completed必须先补充真实运行态验收证据</p>
*/
class P1rAccountRealApiGateTest {
/** P1R-3 Account 合同固定 operation 数量。 */
private static final int EXPECTED_ACCOUNT_OPERATION_COUNT = 33;
/** Account 阶段不能继续使用的非真实实现状态。 */
private static final Set<String> NON_REAL_IMPLEMENTATION_STATUSES = Set.of(
"generic_persistence",
"catch_all",
"sse_placeholder",
"missing"
);
/** P1R-3 阶段不允许使用的完成状态。 */
private static final Set<String> DISALLOWED_COMPLETION_STATUSES = Set.of(
"blocked",
"completed"
);
/** P1R-3 计划第 1 节冻结的 Account operation、方法和路径清单。 */
private static final Map<String, String> P1R3_ACCOUNT_OPERATION_SIGNATURES = Map.ofEntries(
entry("adminListAccountUsers", "GET /admin-api/muse/account/users"),
entry("adminGetUserEntitlements", "GET /admin-api/muse/account/users/{userId}/entitlements"),
entry("adminCreateQuotaAdjustment", "POST /admin-api/muse/account/users/{userId}/quota-adjustments"),
entry("adminListQuotaAdjustments", "GET /admin-api/muse/account/users/{userId}/quota-adjustments"),
entry("adminListNewApiBindings", "GET /admin-api/muse/account/new-api-bindings"),
entry("adminCreateNewApiBinding", "POST /admin-api/muse/account/users/{userId}/new-api-binding"),
entry("adminCreateQuotaRequest", "POST /admin-api/muse/account/users/{userId}/quota-requests"),
entry("adminGetBalanceSnapshots", "GET /admin-api/muse/account/users/{userId}/balance-snapshots"),
entry("adminCreateCallAttributionJob", "POST /admin-api/muse/account/call-attribution-jobs"),
entry("adminGetCallAttributionJob", "GET /admin-api/muse/account/call-attribution-jobs/{jobId}"),
entry("adminGetIntegrationCallByCorrelation", "GET /admin-api/muse/account/integration-calls/by-correlation/{correlationId}"),
entry("adminListUsageRecords", "GET /admin-api/muse/account/usage-records"),
entry("adminListPurchaseRecords", "GET /admin-api/muse/account/purchase-records"),
entry("getCurrentUser", "GET /app-api/muse/me"),
entry("getProfile", "GET /app-api/muse/profile"),
entry("updateProfile", "PATCH /app-api/muse/profile"),
entry("getAppEntitlements", "GET /app-api/muse/account/entitlements"),
entry("getAppUsage", "GET /app-api/muse/account/usage"),
entry("getAppNewApiBinding", "GET /app-api/muse/account/new-api-binding"),
entry("appRecheckNewApiBinding", "POST /app-api/muse/account/new-api-binding/recheck"),
entry("getAppBalanceSnapshots", "GET /app-api/muse/account/balance-snapshots"),
entry("appCreateQuotaRequest", "POST /app-api/muse/account/quota-requests"),
entry("appGetQuotaRequest", "GET /app-api/muse/account/quota-requests/{requestId}"),
entry("appGetIntegrationCallByCorrelation", "GET /app-api/muse/account/integration-calls/by-correlation/{correlationId}"),
entry("appListPurchases", "GET /app-api/muse/account/purchases"),
entry("appListLicenses", "GET /app-api/muse/account/licenses"),
entry("appListPublishRecords", "GET /app-api/muse/account/publish-records"),
entry("appListSecurityEvents", "GET /app-api/muse/account/security-events"),
entry("appGetSecurityEvent", "GET /app-api/muse/account/security-events/{eventId}"),
entry("appAcknowledgeSecurityEvent", "POST /app-api/muse/account/security-events/{eventId}/acknowledge"),
entry("appCreateExportTask", "POST /app-api/muse/account/export-tasks"),
entry("appGetExportTask", "GET /app-api/muse/account/export-tasks/{taskId}"),
entry("appDownloadExport", "GET /app-api/muse/account/downloads/{credentialId}")
);
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* 验证 Account operation 数量operationId方法和路径与 P1R-3 计划第 1 节一致
*
* @throws IOException 读取覆盖报告失败时抛出
*/
@Test
void should_keep_account_operation_inventory_complete() throws IOException {
JsonNode accountOperations = readAccountOperations();
Map<String, String> actualSignatures = toOperationSignatures(accountOperations);
assertEquals(EXPECTED_ACCOUNT_OPERATION_COUNT, accountOperations.size(),
"Account operation 数量必须保持 33");
assertEquals(EXPECTED_ACCOUNT_OPERATION_COUNT, actualSignatures.size(),
"Account operationId 不能重复");
assertEquals(P1R3_ACCOUNT_OPERATION_SIGNATURES, actualSignatures,
"Account operation 清单必须与 P1R-3 计划第 1 节一致");
}
/**
* 验证 Account operation 不再落回通用持久化catch-allSSE 占位或缺入口
*
* @throws IOException 读取覆盖报告失败时抛出
*/
@Test
void should_not_allow_account_operations_to_use_non_real_entrypoints() throws IOException {
JsonNode accountOperations = readAccountOperations();
for (JsonNode operation : accountOperations) {
String operationId = operation.path("operationId").asText("<missing-operationId>");
String implementationStatus = operation.path("implementationStatus").asText();
assertFalse(NON_REAL_IMPLEMENTATION_STATUSES.contains(implementationStatus),
operationId + " 不能使用非真实 Account 实现入口: " + implementationStatus);
}
}
/**
* 验证 Account operation 不能在 P1R-3 阶段提前宣称 completed 或退回 blocked
*
* @throws IOException 读取覆盖报告失败时抛出
*/
@Test
void should_not_mark_account_operations_as_blocked_or_completed() throws IOException {
JsonNode accountOperations = readAccountOperations();
for (JsonNode operation : accountOperations) {
String operationId = operation.path("operationId").asText("<missing-operationId>");
String completionStatus = operation.path("completionStatus").asText();
assertFalse(DISALLOWED_COMPLETION_STATUSES.contains(completionStatus),
operationId + " 在 P1R-3 不能标记为 blocked 或 completed当前为: " + completionStatus);
}
}
/**
* 验证当前 P1R-3 收口口径Account 全部进入 dedicated但仍保持 needs_verification
*
* @throws IOException 读取覆盖报告失败时抛出
*/
@Test
void should_keep_current_account_operations_dedicated_and_needs_verification() throws IOException {
JsonNode accountOperations = readAccountOperations();
for (JsonNode operation : accountOperations) {
String operationId = operation.path("operationId").asText("<missing-operationId>");
assertEquals("dedicated", operation.path("implementationStatus").asText(),
operationId + " 当前 P1R-3 目标必须是 dedicated");
assertEquals("needs_verification", operation.path("completionStatus").asText(),
operationId + " 当前 P1R-3 目标必须是 needs_verification不能提前宣称 completed");
}
}
private JsonNode readAccountOperations() throws IOException {
JsonNode operations = objectMapper.readTree(findReportPath().toFile()).path("operations");
return filterAccountOperations(operations);
}
private JsonNode filterAccountOperations(JsonNode operations) {
ArrayNode accountOperations = objectMapper.createArrayNode();
for (JsonNode operation : operations) {
if ("P1R-3 Account Real API".equals(operation.path("targetStage").asText())
|| "account".equals(operation.path("domain").asText())) {
accountOperations.add(operation);
}
}
return accountOperations;
}
private static Map<String, String> toOperationSignatures(JsonNode operations) {
Map<String, String> operationSignatures = new LinkedHashMap<>();
for (JsonNode operation : operations) {
String operationId = operation.path("operationId").asText("<missing-operationId>");
String signature = operation.path("method").asText() + " " + operation.path("path").asText();
operationSignatures.put(operationId, signature);
}
return operationSignatures;
}
/**
* 从当前 Maven 执行目录逐级向上查找仓库根目录避免 surefire 在不同模块目录执行时路径失效
*/
private static Path findReportPath() {
Path current = Path.of("").toAbsolutePath();
for (Path candidate = current; candidate != null; candidate = candidate.getParent()) {
Path reportPath = candidate.resolve("docs/superpowers/reports/p1r-api-coverage.json");
if (Files.exists(reportPath)) {
return reportPath;
}
}
return current.resolve("docs/superpowers/reports/p1r-api-coverage.json");
}
}