feat: add tenant-scoped rule and permission management
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from .helpers import ReleaseApiClient
|
||||
|
||||
|
||||
def _select_rule_set(client: ReleaseApiClient) -> dict[str, Any]:
|
||||
preferred_rule_type = os.getenv("LEAUDIT_TEST_RULE_TYPE", "contract.entrust").strip()
|
||||
rule_sets = ReleaseApiClient.json_data(client.get("/api/rule-sets"))
|
||||
assert isinstance(rule_sets, list)
|
||||
assert rule_sets, "当前环境没有规则集,无法测试版本管理"
|
||||
|
||||
for item in rule_sets:
|
||||
if str(item.get("ruleType") or "") == preferred_rule_type and item.get("currentVersionId"):
|
||||
return item
|
||||
|
||||
for item in rule_sets:
|
||||
if item.get("currentVersionId"):
|
||||
return item
|
||||
|
||||
pytest.skip("当前环境没有带 currentVersionId 的规则集,无法测试发布/回滚闭环")
|
||||
|
||||
|
||||
def _set_metadata_version(yaml_text: str, version_no: str) -> str:
|
||||
lines = yaml_text.splitlines()
|
||||
metadata_index: int | None = None
|
||||
version_pattern = re.compile(r"^(\s*)version\s*:")
|
||||
|
||||
for index, line in enumerate(lines):
|
||||
if line.strip() == "metadata:":
|
||||
metadata_index = index
|
||||
continue
|
||||
if metadata_index is None:
|
||||
continue
|
||||
if line and not line.startswith((" ", "\t")):
|
||||
break
|
||||
match = version_pattern.match(line)
|
||||
if match:
|
||||
lines[index] = f"{match.group(1)}version: '{version_no}'"
|
||||
return "\n".join(lines) + ("\n" if yaml_text.endswith("\n") else "")
|
||||
|
||||
if metadata_index is None:
|
||||
return f"metadata:\n version: '{version_no}'\n{yaml_text}"
|
||||
|
||||
lines.insert(metadata_index + 1, f" version: '{version_no}'")
|
||||
return "\n".join(lines) + ("\n" if yaml_text.endswith("\n") else "")
|
||||
|
||||
|
||||
def _rule_set_by_type(client: ReleaseApiClient, rule_type: str) -> dict[str, Any]:
|
||||
rule_sets = ReleaseApiClient.json_data(client.get("/api/rule-sets"))
|
||||
for item in rule_sets:
|
||||
if str(item.get("ruleType") or "") == rule_type:
|
||||
return item
|
||||
raise AssertionError(f"规则集不存在: {rule_type}")
|
||||
|
||||
|
||||
@pytest.mark.release
|
||||
def test_g6_rule_detail_version_management_save_publish_and_rollback(admin_client: ReleaseApiClient) -> None:
|
||||
"""覆盖规则配置详情页的版本管理闭环。
|
||||
|
||||
该用例会创建一个真实历史版本并短暂发布,最后回滚到执行前的 currentVersionId。
|
||||
"""
|
||||
|
||||
target_rule_set = _select_rule_set(admin_client)
|
||||
rule_type = str(target_rule_set["ruleType"])
|
||||
original_rule_set_id = int(target_rule_set["id"])
|
||||
original_current_version_id = int(target_rule_set["currentVersionId"])
|
||||
original_tenant_code = str(target_rule_set.get("effectiveTenantCode") or "")
|
||||
|
||||
versions_before = ReleaseApiClient.json_data(admin_client.get(f"/api/rule-sets/{rule_type}/versions"))
|
||||
assert isinstance(versions_before, list)
|
||||
assert {int(item["ruleSetId"]) for item in versions_before} == {original_rule_set_id}
|
||||
assert original_current_version_id in {int(item["id"]) for item in versions_before}
|
||||
|
||||
content = ReleaseApiClient.json_data(admin_client.get(f"/api/rule-sets/versions/{original_current_version_id}/content"))
|
||||
assert int(content["ruleSetId"]) == original_rule_set_id
|
||||
assert str(content["ruleType"]) == rule_type
|
||||
assert str(content["yamlText"]).strip()
|
||||
|
||||
new_version_no = f"pytest-vm-{int(time.time())}"
|
||||
yaml_text = _set_metadata_version(str(content["yamlText"]), new_version_no)
|
||||
created_version_id: int | None = None
|
||||
|
||||
try:
|
||||
created = ReleaseApiClient.json_data(
|
||||
admin_client.post(
|
||||
f"/api/rule-sets/{rule_type}/versions",
|
||||
json={
|
||||
"yamlText": yaml_text,
|
||||
"changeNote": f"pytest rule detail version management smoke {new_version_no}",
|
||||
},
|
||||
expected_status=200,
|
||||
)
|
||||
)
|
||||
created_version_id = int(created["id"])
|
||||
assert int(created["ruleSetId"]) == original_rule_set_id
|
||||
assert str(created["versionNo"]) == new_version_no
|
||||
assert str(created["status"]) == "draft"
|
||||
|
||||
versions_after_create = ReleaseApiClient.json_data(admin_client.get(f"/api/rule-sets/{rule_type}/versions"))
|
||||
assert created_version_id in {int(item["id"]) for item in versions_after_create}
|
||||
assert {int(item["ruleSetId"]) for item in versions_after_create} == {original_rule_set_id}
|
||||
|
||||
published = ReleaseApiClient.json_data(
|
||||
admin_client.post(
|
||||
f"/api/rule-sets/{rule_type}/publish",
|
||||
json={"versionId": created_version_id},
|
||||
expected_status=200,
|
||||
)
|
||||
)
|
||||
assert int(published["id"]) == created_version_id
|
||||
assert int(published["ruleSetId"]) == original_rule_set_id
|
||||
assert str(published["status"]) == "published"
|
||||
|
||||
current_after_publish = _rule_set_by_type(admin_client, rule_type)
|
||||
assert int(current_after_publish["id"]) == original_rule_set_id
|
||||
assert int(current_after_publish["currentVersionId"]) == created_version_id
|
||||
assert str(current_after_publish.get("effectiveTenantCode") or "") == original_tenant_code
|
||||
|
||||
finally:
|
||||
if created_version_id:
|
||||
admin_client.post(
|
||||
f"/api/rule-sets/{rule_type}/rollback",
|
||||
json={"versionId": original_current_version_id},
|
||||
expected_status=200,
|
||||
)
|
||||
|
||||
restored = _rule_set_by_type(admin_client, rule_type)
|
||||
assert int(restored["id"]) == original_rule_set_id
|
||||
assert int(restored["currentVersionId"]) == original_current_version_id
|
||||
assert str(restored.get("effectiveTenantCode") or "") == original_tenant_code
|
||||
|
||||
|
||||
@pytest.mark.release
|
||||
def test_g6_rule_detail_version_management_rejects_cross_rule_set_publish(admin_client: ReleaseApiClient) -> None:
|
||||
"""发布接口必须拒绝把其他规则集的版本发布到当前规则类型。"""
|
||||
|
||||
rule_sets = ReleaseApiClient.json_data(admin_client.get("/api/rule-sets"))
|
||||
candidates = [item for item in rule_sets if item.get("currentVersionId")]
|
||||
if len(candidates) < 2:
|
||||
pytest.skip("当前环境少于两个带 currentVersionId 的规则集,无法测试跨规则集发布拦截")
|
||||
|
||||
left = candidates[0]
|
||||
right = next((item for item in candidates[1:] if str(item["ruleType"]) != str(left["ruleType"])), None)
|
||||
if right is None:
|
||||
pytest.skip("当前环境没有可用于跨规则类型发布拦截的第二个规则集")
|
||||
|
||||
response = admin_client.post(
|
||||
f"/api/rule-sets/{left['ruleType']}/publish",
|
||||
json={"versionId": int(right["currentVersionId"])},
|
||||
expected_status=403,
|
||||
)
|
||||
assert "当前租户不能发布或回滚其他租户的规则版本" in response.text
|
||||
Reference in New Issue
Block a user