Files
OpenViking/tests/cli/test_cli_content.py
Qin Haojie e357af6ac7 feat(acl): 增加资源 ACL、用户组授权与向量权限过滤 (#3213)
* feat(acl): add resource access control

Persist direct and inherited ACL fields in context records so filesystem operations and vector retrieval enforce the same permissions.

* docs(acl): align behavior documentation

* feat(acl): 支持用户组授权

* refactor(acl): 收敛权限更新并补齐异步身份

确保 ACL 更新结果与锁内状态一致,移动失败可恢复向量 URI,并让后台解析沿用发起请求的用户组身份。精简重复实现和低价值测试。

* refactor(acl): 仅在共享资源区启用授权

个人资源保持 owner 私有,分享通过移动到共享区完成;跨区移动按目标 scope 继承或清空 ACL,并将向量权限过滤限制在共享区。

* chore(acl): 移除无关解析器清理

恢复 telemetry 空上下文和 import 的基线写法,仅保留 ACL 用户组身份传递。

* fix(acl): 收紧权限读取与过滤分页

ACL 元数据读取失败时终止权限判断,并确保 grep 的 node_limit 作用于权限过滤后的可见结果。

* refactor(acl): 收敛测试与检索取数逻辑

删除重复、时序绑定和实现细节测试,并撤销非必要的 grep 循环扩容。保留用例只覆盖独立的安全、一致性和兼容性故障。

* refactor(acl): 统一资源操作鉴权

将 write、delete 和 reindex 收敛到 read/write/manage 能力检查,并修复 add target 权限校验未等待的问题。

* refactor(acl): 枚举化权限能力

用 AclAction 和 AclLevel 约束内部权限分支,拒绝裸字符串 action,并保持 API 与存储字符串格式兼容。

* fix(acl): route reindex through unified authorization

* refactor(uri): remove redundant user content root helper

* fix(acl): align resource browsing and ingestion permissions

* test(acl): consolidate ACL contract coverage

* feat(acl): grant creators manager access

* fix(acl): scope creator grants to controlled trees

* fix(acl): isolate add-resource parent refresh authorization

* feat(acl): enforce snapshot permissions

* fix(acl): preserve content write permission errors

* feat(acl): add opt-in protection for new shared content

* feat(cli): configure automatic ACL protection

* refactor(acl): simplify authorization flow

* fix(acl): allow write permission for file move and delete

* refactor(acl): simplify group and permission semantics

* fix(acl): allow write access for snapshot file deletion

* fix(acl): preserve user-scoped reindex access

* refactor(acl): compact indexed principal grants

Store one highest-permission token per principal so in-memory authorization and vector filtering share the same ACL representation.

* fix(acl): preserve background task identity

* fix(acl): bypass acl for watch refresh
2026-08-28 17:22:55 +08:00

95 lines
3.6 KiB
Python

# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
# SPDX-License-Identifier: AGPL-3.0
"""CLI content operation tests (read, abstract, overview, download, write, reindex)."""
import os
import tempfile
import uuid
import pytest
from conftest import ov, ov_add_resource, ov_reindex, ov_rm, ov_write
pytestmark = pytest.mark.cli_remote
class TestContentRead:
def test_read(self, test_file_uri):
r = ov(["read", test_file_uri, "-o", "json"])
assert r["exit_code"] == 0, (
f"ov read should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
assert len(r["stdout"]) > 0, "read output should not be empty"
assert "CLI Test" in r["stdout"] or "test" in r["stdout"].lower(), (
f"read output should contain test content, got: {r['stdout'][:200]}"
)
class TestContentAbstract:
def test_abstract(self, test_pack_uri):
r = ov(["abstract", test_pack_uri, "-o", "json"])
assert r["exit_code"] == 0, (
f"ov abstract should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
assert len(r["stdout"]) > 0, "abstract output should not be empty"
class TestContentOverview:
def test_overview(self, test_pack_uri):
r = ov(["overview", test_pack_uri, "-o", "json"])
assert r["exit_code"] == 0, (
f"ov overview should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
assert len(r["stdout"]) > 0, "overview output should not be empty"
class TestContentDownload:
def test_get_download(self, test_file_uri, tmp_path):
local_path = str(tmp_path / "downloaded.txt")
r = ov(["get", test_file_uri, local_path, "-o", "json"])
assert r["exit_code"] == 0, (
f"ov get should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
assert os.path.exists(local_path), "downloaded file should exist"
assert os.path.getsize(local_path) > 0, "downloaded file should not be empty"
class TestContentWrite:
def test_write_replace(self, test_file_uri):
r = ov_write(test_file_uri, "Updated via CLI write.", "--mode", "replace")
assert r["exit_code"] == 0, (
f"ov write replace should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
data = r["json"]
assert data is not None and data.get("ok") is True, "Expected ok=true"
def test_write_append(self, test_file_uri):
r = ov_write(test_file_uri, "\nAppended via CLI.", "--append")
assert r["exit_code"] == 0, (
f"ov write append should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
data = r["json"]
assert data is not None and data.get("ok") is True, "Expected ok=true"
class TestContentReindex:
def test_reindex(self):
reindex_pack = f"viking://~/resources/reindex_{uuid.uuid4().hex[:6]}"
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False, mode="w") as f:
f.write("# Reindex Test\n\nThis is an independent resource for reindex testing.")
temp_path = f.name
try:
try:
r = ov_add_resource(temp_path, reindex_pack)
assert r["exit_code"] == 0, f"add-resource for reindex failed: {r['stderr'][:300]}"
finally:
os.unlink(temp_path)
r = ov_reindex(reindex_pack)
assert r["exit_code"] == 0, (
f"ov reindex should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
)
data = r["json"]
assert data is not None and data.get("ok") is True, "Expected ok=true"
finally:
ov_rm(reindex_pack)