From 7a35bfe58c099d3bcba95243cac126dafbb8c529 Mon Sep 17 00:00:00 2001 From: yanweidong Date: Mon, 27 Jul 2026 14:03:45 +0800 Subject: [PATCH] test: define safe and audit resource rename contract --- .../task-1-report.md | 43 +++++++++++++++++++ .../internal/logic/platform/resource_test.go | 11 ++++- backend/api/internal/routers/platform_test.go | 8 ++-- .../scripts/audit-check.test.mjs | 8 ++++ 4 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 .superpowers/sdd/2026-07-27-safe-audit-model-prefix-rename/task-1-report.md diff --git a/.superpowers/sdd/2026-07-27-safe-audit-model-prefix-rename/task-1-report.md b/.superpowers/sdd/2026-07-27-safe-audit-model-prefix-rename/task-1-report.md new file mode 100644 index 0000000..cef9b39 --- /dev/null +++ b/.superpowers/sdd/2026-07-27-safe-audit-model-prefix-rename/task-1-report.md @@ -0,0 +1,43 @@ +# Task 1 Report: Safe and Audit Resource Rename Contracts + +## Scope + +Added only RED contract tests. No production source, database table, or data migration code was changed. + +## Contract Coverage + +- Backend resource catalogue: `safe_rule`, `safe_event`, `safe_inspection`, and `safe_event_disposal` under `safety`; `audit_operation_log`, `audit_export_log`, and `audit_approval` under `audit`. +- Backend routes: `/safety/safe_*`, the safe-event disposal action, `/audit/audit_*`, and the audit-approval action. +- Frontend resource definitions: required `safe_event` and `audit_approval` resource/path declarations. + +## RED Evidence + +### Backend + +Command run from `backend/api`: + +```powershell +$env:GIN_MODE='release'; go test ./internal/logic/platform ./internal/routers -run 'Test.*(Safe|Audit)' -v +``` + +Result: **failed as expected** (exit code 1). + +- `TestSafeAndAuditResourceContracts` reports the missing `safety/safe_rule` contract; the current catalogue still defines `saf_rule` (and the other historical `saf_*`/`aud_*` names). +- `TestPlatformDeviceSafetyCommerceAndDeliveryRoutesFollowTheirContracts` reports unregistered `/heqi/platform/v1/safety/safe_*` routes and the `safe_event` disposal route. +- `TestPlatformFinanceContentAndAuditRoutesFollowTheirContracts` reports unregistered `/heqi/platform/v1/audit/audit_*` routes and `/audit/audit_approval/:identity/approve`. + +### Frontend + +Command run from `frontend/platform_admin`: + +```powershell +node --test scripts/audit-check.test.mjs +``` + +Result: **failed as expected** (exit code 1; 6 passing, 1 failing). + +- The new `资源定义使用 safe 和 audit 前缀` test fails because `src/api/resources.ts` currently defines `saf_event` at `/safety/saf_event`; it therefore does not match the required `safe_event` declaration. The required `audit_approval` declaration remains absent as well. + +## Handoff + +The red baseline is intentional. The next task should rename production resource contracts, backend routes, and frontend definitions without preserving the historical public names. diff --git a/backend/api/internal/logic/platform/resource_test.go b/backend/api/internal/logic/platform/resource_test.go index 05e7146..f40ae77 100644 --- a/backend/api/internal/logic/platform/resource_test.go +++ b/backend/api/internal/logic/platform/resource_test.go @@ -25,12 +25,21 @@ import ( func TestExpectedResources(t *testing.T) { assertContract(t, ExpectedResources(), "gas", "gas_basic", Writable, "list") - assertContract(t, ExpectedResources(), "safety", "saf_event", Writable, "list") assertContract(t, ExpectedResources(), "ec", "ec_order_item", Writable, "list") assertContract(t, ExpectedResources(), "wallet", "wallet_ledger", ReadOnly, "list") assertContract(t, ExpectedResources(), "delivery", "delivery_track_point", ReadOnly, "list") } +func TestSafeAndAuditResourceContracts(t *testing.T) { + assertContract(t, ExpectedResources(), "safety", "safe_rule", Writable, "list") + assertContract(t, ExpectedResources(), "safety", "safe_event", Writable, "list") + assertContract(t, ExpectedResources(), "safety", "safe_inspection", Writable, "list") + assertContract(t, ExpectedResources(), "safety", "safe_event_disposal", AppendOnly, "list") + assertContract(t, ExpectedResources(), "audit", "audit_operation_log", ReadOnly, "list") + assertContract(t, ExpectedResources(), "audit", "audit_export_log", ReadOnly, "list") + assertContract(t, ExpectedResources(), "audit", "audit_approval", ReadOnly, "list") +} + func TestResourceDefinitionAllowsOnlySupportedMethods(t *testing.T) { if (ResourceDefinition{Mode: ReadOnly}).Allows(http.MethodPost) { t.Fatal("readonly allows POST") diff --git a/backend/api/internal/routers/platform_test.go b/backend/api/internal/routers/platform_test.go index 078edf4..41964d7 100644 --- a/backend/api/internal/routers/platform_test.go +++ b/backend/api/internal/routers/platform_test.go @@ -101,7 +101,7 @@ func TestPlatformDeviceSafetyCommerceAndDeliveryRoutesFollowTheirContracts(t *te for _, resource := range []string{ "/device/dev_smart_cylinder_valve", "/device/dev_device_binding", - "/safety/saf_rule", "/safety/saf_event", "/safety/saf_inspection", + "/safety/safe_rule", "/safety/safe_event", "/safety/safe_inspection", "/ec/ec_category", "/ec/ec_product", "/ec/ec_product_attribute", "/ec/ec_product_image", "/ec/ec_cart", "/ec/ec_order", "/ec/ec_order_item", "/ec/ec_review", "/delivery/delivery_task", "/delivery/delivery_track", } { @@ -126,7 +126,7 @@ func TestPlatformDeviceSafetyCommerceAndDeliveryRoutesFollowTheirContracts(t *te } } - disposal := "/heqi/platform/v1/safety/saf_event/:identity/disposals" + disposal := "/heqi/platform/v1/safety/safe_event/:identity/disposals" assertRouteMethods(t, routes, disposal, http.MethodPost) if routes[disposal][http.MethodDelete] { t.Fatal("safety event disposals must be append-only") @@ -157,7 +157,7 @@ func TestPlatformFinanceContentAndAuditRoutesFollowTheirContracts(t *testing.T) for _, resource := range []string{ "/wallet/wallet", "/wallet/wallet_ledger", "/wallet/wallet_recharge", "/wallet/wallet_withdrawal", "/report/report", "/report/report_item", "/report/report_metric_snapshot", - "/audit/aud_operation_log", "/audit/aud_export_log", "/audit/aud_approval", + "/audit/audit_operation_log", "/audit/audit_export_log", "/audit/audit_approval", } { path := "/heqi/platform/v1" + resource assertRouteMethods(t, routes, path, http.MethodGet) @@ -169,7 +169,7 @@ func TestPlatformFinanceContentAndAuditRoutesFollowTheirContracts(t *testing.T) } } - assertRouteMethods(t, routes, "/heqi/platform/v1/audit/aud_approval/:identity/approve", http.MethodPost) + assertRouteMethods(t, routes, "/heqi/platform/v1/audit/audit_approval/:identity/approve", http.MethodPost) } func assertRouteMethods(t *testing.T, routes map[string]map[string]bool, path string, methods ...string) { diff --git a/frontend/platform_admin/scripts/audit-check.test.mjs b/frontend/platform_admin/scripts/audit-check.test.mjs index 6cdfdb2..329ac8f 100644 --- a/frontend/platform_admin/scripts/audit-check.test.mjs +++ b/frontend/platform_admin/scripts/audit-check.test.mjs @@ -1,7 +1,15 @@ import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; import test from 'node:test'; import { auditPlatform, scanInternalIdLeaks } from './audit-check.mjs'; +const resourcesSource = readFileSync('src/api/resources.ts', 'utf8'); + +test('资源定义使用 safe 和 audit 前缀', () => { + assert.match(resourcesSource, /define\('safe_event', '\/safety\/safe_event'/); + assert.match(resourcesSource, /define\('audit_approval', '\/audit\/audit_approval'/); +}); + test('只读页面将状态变更视为违规写操作', () => { const failures = auditPlatform({ manifest: { resources: [], routes: [] },