外包风险评估系统:领域引擎+前端+服务端持久化与生产部署
- 确定性领域引擎(分类/评分/分级/红线/费用/裁决)+LLM(通义千问)语言理解 - 6步评估向导、服务端草稿持久化(跨设备/编辑草稿保护) - 工作流(草稿→风控→管理层)、RBAC、报告导出、校准、客户/费率/红线/最低工资管理 - 专业图标体系替换全部emoji、看板美化 - 生产化:API_BASE可配置(同源反代)、auth密钥惰性读取修复RBAC - 444单测+204前端测试+51 e2e
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Property 45: 非管理员配置修改一律拒绝且配置不变 的属性化测试(RBAC,Req 12.1, 12.3)。
|
||||
*
|
||||
* 属性陈述:对任意非 Administrator 角色(含 Assessor、未认证、未授权)发起的
|
||||
* Risk_Model 配置修改请求,System 必拒绝该请求(status='rejected')、保持当前配置
|
||||
* 不变(返回与请求中 currentConfig 同一引用且值相等),并返回权限不足错误
|
||||
* (AuthorizationError,userMessage 含「权限不足」)。
|
||||
*
|
||||
* 测试中 `apply` 故意产出一个不同于当前配置的新值;若拒绝逻辑被绕过而调用了
|
||||
* `apply`,返回的配置将发生变化,断言即会失败——以此真实校验"配置不变"。
|
||||
*
|
||||
* Feature: outsourcing-risk-assessment, Property 45: 非管理员配置修改一律拒绝且配置不变
|
||||
* Validates: Requirements 12.1, 12.3
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fc from 'fast-check';
|
||||
import {
|
||||
applyConfigChange,
|
||||
AuthorizationError,
|
||||
InMemoryConfigAuditStore,
|
||||
type Actor,
|
||||
type AuthRole,
|
||||
} from '../index.js';
|
||||
|
||||
// 非 Administrator 角色全集(Req 12.1, 12.3)。
|
||||
const NON_ADMIN_ROLES = ['Assessor', '未认证', '未授权'] as const satisfies readonly AuthRole[];
|
||||
|
||||
const nonAdminActorArb: fc.Arbitrary<Actor> = fc.record({
|
||||
id: fc.string({ minLength: 0, maxLength: 12 }),
|
||||
role: fc.constantFrom<AuthRole>(...NON_ADMIN_ROLES),
|
||||
});
|
||||
|
||||
/** 任意配置值(以简单可比较对象代表 Risk_Model 配置形态)。 */
|
||||
interface TestConfig {
|
||||
readonly version: number;
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
const configArb: fc.Arbitrary<TestConfig> = fc.record({
|
||||
version: fc.integer({ min: 0, max: 1_000 }),
|
||||
name: fc.string({ minLength: 0, maxLength: 10 }),
|
||||
});
|
||||
|
||||
const changedKeysArb: fc.Arbitrary<string[]> = fc.array(
|
||||
fc.string({ minLength: 1, maxLength: 8 }),
|
||||
{ minLength: 0, maxLength: 4 },
|
||||
);
|
||||
|
||||
describe('Property 45: 非管理员配置修改一律拒绝且配置不变 (Req 12.1, 12.3)', () => {
|
||||
it('非 Administrator 请求被拒绝、配置不变并返回权限不足错误', () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
nonAdminActorArb,
|
||||
configArb,
|
||||
changedKeysArb,
|
||||
(actor, currentConfig, changedConfigKeys) => {
|
||||
const auditStore = new InMemoryConfigAuditStore();
|
||||
let applyCalled = false;
|
||||
|
||||
const result = applyConfigChange<TestConfig>(
|
||||
{
|
||||
actor,
|
||||
currentConfig,
|
||||
changedConfigKeys,
|
||||
// 故意产出与当前配置不同的值:若被调用,配置必然改变。
|
||||
apply: (current) => {
|
||||
applyCalled = true;
|
||||
return { version: current.version + 1, name: `${current.name}!` };
|
||||
},
|
||||
},
|
||||
auditStore,
|
||||
);
|
||||
|
||||
// 1) 一律拒绝(Req 12.1, 12.3)。
|
||||
expect(result.status).toBe('rejected');
|
||||
|
||||
// 2) 鉴权失败时绝不调用 apply(保证配置不会被改动)。
|
||||
expect(applyCalled).toBe(false);
|
||||
|
||||
// 3) 配置不变:同一引用且值相等(Req 12.1, 12.3)。
|
||||
expect(result.config).toBe(currentConfig);
|
||||
expect(result.config).toEqual(currentConfig);
|
||||
|
||||
// 4) 返回权限不足错误(Req 12.1, 12.3)。
|
||||
if (result.status === 'rejected') {
|
||||
expect(result.error).toBeInstanceOf(AuthorizationError);
|
||||
expect(result.error.userMessage).toContain('权限不足');
|
||||
expect(result.error.requiredRole).toBe('Administrator');
|
||||
expect(result.error.actualRole).toBe(actor.role);
|
||||
expect(result.error.actorId).toBe(actor.id);
|
||||
}
|
||||
},
|
||||
),
|
||||
{ numRuns: 100 },
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user