test: prohibit legacy safe and audit aliases
This commit is contained in:
@@ -41,3 +41,12 @@ Result: **failed as expected** (exit code 1; 6 passing, 1 failing).
|
|||||||
## Handoff
|
## 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.
|
The red baseline is intentional. The next task should rename production resource contracts, backend routes, and frontend definitions without preserving the historical public names.
|
||||||
|
|
||||||
|
## Review Follow-up
|
||||||
|
|
||||||
|
The RED tests now also reject legacy `saf_*` and `aud_*` resource contracts, routes, and frontend definitions. This prevents a dual-registration implementation from satisfying only the new-name assertions. Frontend static coverage now checks all seven renamed resources, and the filtered route suite requires both `GET` and `POST` for the append-only safe-event disposal history endpoint.
|
||||||
|
|
||||||
|
Focused verification was rerun after these additions:
|
||||||
|
|
||||||
|
- Backend: the filtered suite remains RED (exit code 1), reporting both absent `safe_*`/`audit_*` routes and currently registered legacy `saf_*`/`aud_*` routes.
|
||||||
|
- Frontend: the focused suite remains RED (exit code 1; 6 passing, 1 failing), first reporting the missing `define('safe_rule', '/safety/safe_rule'...)` declaration. Once the new declarations exist, the anti-alias assertions will also reject any retained legacy definitions.
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ func TestSafeAndAuditResourceContracts(t *testing.T) {
|
|||||||
assertContract(t, ExpectedResources(), "audit", "audit_operation_log", ReadOnly, "list")
|
assertContract(t, ExpectedResources(), "audit", "audit_operation_log", ReadOnly, "list")
|
||||||
assertContract(t, ExpectedResources(), "audit", "audit_export_log", ReadOnly, "list")
|
assertContract(t, ExpectedResources(), "audit", "audit_export_log", ReadOnly, "list")
|
||||||
assertContract(t, ExpectedResources(), "audit", "audit_approval", ReadOnly, "list")
|
assertContract(t, ExpectedResources(), "audit", "audit_approval", ReadOnly, "list")
|
||||||
|
|
||||||
|
for _, contract := range ExpectedResources() {
|
||||||
|
if (contract.Domain == "safety" && strings.HasPrefix(contract.Name, "saf_")) ||
|
||||||
|
(contract.Domain == "audit" && strings.HasPrefix(contract.Name, "aud_")) {
|
||||||
|
t.Fatalf("legacy resource contract %s/%s must not be registered", contract.Domain, contract.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceDefinitionAllowsOnlySupportedMethods(t *testing.T) {
|
func TestResourceDefinitionAllowsOnlySupportedMethods(t *testing.T) {
|
||||||
|
|||||||
@@ -127,10 +127,18 @@ func TestPlatformDeviceSafetyCommerceAndDeliveryRoutesFollowTheirContracts(t *te
|
|||||||
}
|
}
|
||||||
|
|
||||||
disposal := "/heqi/platform/v1/safety/safe_event/:identity/disposals"
|
disposal := "/heqi/platform/v1/safety/safe_event/:identity/disposals"
|
||||||
assertRouteMethods(t, routes, disposal, http.MethodPost)
|
assertRouteMethods(t, routes, disposal, http.MethodGet, http.MethodPost)
|
||||||
if routes[disposal][http.MethodDelete] {
|
if routes[disposal][http.MethodDelete] {
|
||||||
t.Fatal("safety event disposals must be append-only")
|
t.Fatal("safety event disposals must be append-only")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, resource := range []string{"/safety/saf_rule", "/safety/saf_event", "/safety/saf_inspection"} {
|
||||||
|
path := "/heqi/platform/v1" + resource
|
||||||
|
assertNoRouteMethods(t, routes, path, http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
|
assertNoRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
|
assertNoRouteMethods(t, routes, path+"/:identity/status", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
|
}
|
||||||
|
assertNoRouteMethods(t, routes, "/heqi/platform/v1/safety/saf_event/:identity/disposals", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPlatformFinanceContentAndAuditRoutesFollowTheirContracts(t *testing.T) {
|
func TestPlatformFinanceContentAndAuditRoutesFollowTheirContracts(t *testing.T) {
|
||||||
@@ -170,6 +178,13 @@ func TestPlatformFinanceContentAndAuditRoutesFollowTheirContracts(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertRouteMethods(t, routes, "/heqi/platform/v1/audit/audit_approval/:identity/approve", http.MethodPost)
|
assertRouteMethods(t, routes, "/heqi/platform/v1/audit/audit_approval/:identity/approve", http.MethodPost)
|
||||||
|
|
||||||
|
for _, resource := range []string{"/audit/aud_operation_log", "/audit/aud_export_log", "/audit/aud_approval"} {
|
||||||
|
path := "/heqi/platform/v1" + resource
|
||||||
|
assertNoRouteMethods(t, routes, path, http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
|
assertNoRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
|
}
|
||||||
|
assertNoRouteMethods(t, routes, "/heqi/platform/v1/audit/aud_approval/:identity/approve", http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertRouteMethods(t *testing.T, routes map[string]map[string]bool, path string, methods ...string) {
|
func assertRouteMethods(t *testing.T, routes map[string]map[string]bool, path string, methods ...string) {
|
||||||
|
|||||||
@@ -6,8 +6,18 @@ import { auditPlatform, scanInternalIdLeaks } from './audit-check.mjs';
|
|||||||
const resourcesSource = readFileSync('src/api/resources.ts', 'utf8');
|
const resourcesSource = readFileSync('src/api/resources.ts', 'utf8');
|
||||||
|
|
||||||
test('资源定义使用 safe 和 audit 前缀', () => {
|
test('资源定义使用 safe 和 audit 前缀', () => {
|
||||||
|
assert.match(resourcesSource, /define\('safe_rule', '\/safety\/safe_rule'/);
|
||||||
assert.match(resourcesSource, /define\('safe_event', '\/safety\/safe_event'/);
|
assert.match(resourcesSource, /define\('safe_event', '\/safety\/safe_event'/);
|
||||||
|
assert.match(resourcesSource, /define\('safe_inspection', '\/safety\/safe_inspection'/);
|
||||||
|
assert.match(resourcesSource, /define\('safe_event_disposal', '\/safety\/safe_event\/:identity\/disposals'/);
|
||||||
|
assert.match(resourcesSource, /define\('audit_operation_log', '\/audit\/audit_operation_log'/);
|
||||||
|
assert.match(resourcesSource, /define\('audit_export_log', '\/audit\/audit_export_log'/);
|
||||||
assert.match(resourcesSource, /define\('audit_approval', '\/audit\/audit_approval'/);
|
assert.match(resourcesSource, /define\('audit_approval', '\/audit\/audit_approval'/);
|
||||||
|
|
||||||
|
assert.doesNotMatch(resourcesSource, /define\('saf_(?:rule|event|inspection|event_disposal)',/);
|
||||||
|
assert.doesNotMatch(resourcesSource, /action\('saf_event_disposal',/);
|
||||||
|
assert.doesNotMatch(resourcesSource, /define\('aud_(?:operation_log|export_log|approval)',/);
|
||||||
|
assert.doesNotMatch(resourcesSource, /\/audit\/aud_approval\/:identity\/approve/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('只读页面将状态变更视为违规写操作', () => {
|
test('只读页面将状态变更视为违规写操作', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user