项目文件夹

文件
wehub-resource-sync c889a57b6b
Test Suites / Build CI Environment (push) Has been cancelled
Test Suites / Basic Tests (push) Has been cancelled
Test Suites / End-to-End Tests (push) Has been cancelled
Test Suites / CLI Tests (push) Has been cancelled
Test Suites / Slow End-to-End Tests (push) Has been cancelled
Test Suites / Graph Database Tests (push) Has been cancelled
Test Suites / Vector DB Tests (push) Has been cancelled
Test Suites / Temporal Graph Test (push) Has been cancelled
Test Suites / Search Test on Different DBs (push) Has been cancelled
Test Suites / Example Tests (push) Has been cancelled
Test Suites / Notebook Tests (push) Has been cancelled
Test Suites / OS and Python Tests Ubuntu (push) Has been cancelled
Test Suites / OS and Python Tests Extended (push) Has been cancelled
Test Suites / LLM Test Suite (push) Has been cancelled
Test Suites / S3 File Storage Test (push) Has been cancelled
Test Suites / Run Integration Tests (push) Has been cancelled
Test Suites / MCP Tests (push) Has been cancelled
Test Suites / Docker Compose Test (push) Has been cancelled
Test Suites / Docker CI test (push) Has been cancelled
Test Suites / Relational DB Migration Tests (push) Has been cancelled
Test Suites / Distributed Cognee Test (push) Has been cancelled
Test Suites / DB Examples Tests (push) Has been cancelled
Test Suites / Test Completion Status (push) Has been cancelled
Test Suites / Claude Code Review (push) Has been cancelled
Test Suites / basic checks (push) Has been cancelled
build | Build and Push Cognee MCP Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
build | Build and Push Docker Image to dockerhub / docker-build-and-push (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.11) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Core Functionality (3.12) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (kuzu, kuzu) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges with Different Graph Databases (neo4j, neo4j) (push) Has been cancelled
Weighted Edges Tests / Test Weighted Edges Examples (push) Has been cancelled
Weighted Edges Tests / Code Quality for Weighted Edges (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:02:24 +08:00

283 行
11 KiB
Python

import asyncio
import os
import pathlib
from unittest.mock import AsyncMock, patch
from uuid import UUID
import pytest
import pytest_asyncio
import cognee
from cognee.context_global_variables import backend_access_control_enabled
from cognee.exceptions import CogneeValidationError
from cognee.infrastructure.databases.exceptions import EntityNotFoundError
from cognee.modules.engine.operations.setup import setup as engine_setup
from cognee.modules.search.types import SearchType
from cognee.modules.users.exceptions import PermissionDeniedError
from cognee.modules.users.methods import create_user, get_user
from cognee.modules.users.permissions.methods import authorized_give_permission_on_datasets
from cognee.modules.users.roles.methods import add_user_to_role, create_role
from cognee.modules.users.tenants.methods import (
add_user_to_tenant,
create_tenant,
select_tenant,
remove_user_from_tenant,
)
pytestmark = pytest.mark.asyncio
def _extract_dataset_id_from_remember(remember_result):
"""Extract dataset_id from remember output."""
return UUID(remember_result.dataset_id)
async def _reset_engines_and_prune() -> None:
"""Reset db engine caches and prune data/system."""
try:
from cognee.infrastructure.databases.vector import get_vector_engine_async
vector_engine = await get_vector_engine_async()
if hasattr(vector_engine, "engine") and hasattr(vector_engine.engine, "dispose"):
await vector_engine.engine.dispose(close=True)
except Exception:
pass
from cognee.infrastructure.databases.relational.create_relational_engine import (
create_relational_engine,
)
from cognee.infrastructure.databases.vector.create_vector_engine import _create_vector_engine
from cognee.infrastructure.databases.graph.get_graph_engine import _create_graph_engine
_create_graph_engine.cache_clear()
_create_vector_engine.cache_clear()
create_relational_engine.cache_clear()
await cognee.prune.prune_data()
await cognee.prune.prune_system(metadata=True)
@pytest.fixture(scope="module")
def event_loop():
"""Single event loop for this module (avoids cross-loop futures)."""
loop = asyncio.new_event_loop()
try:
yield loop
finally:
loop.close()
@pytest_asyncio.fixture(scope="module")
async def permissions_example_env(tmp_path_factory):
"""One-time environment setup for the permissions example test."""
# Ensure permissions feature is enabled (example requires it), but don't override if caller set it already.
os.environ.setdefault("ENABLE_BACKEND_ACCESS_CONTROL", "True")
root = tmp_path_factory.mktemp("permissions_example")
cognee.config.data_root_directory(str(root / "data"))
cognee.config.system_root_directory(str(root / "system"))
await _reset_engines_and_prune()
await engine_setup()
assert backend_access_control_enabled(), (
"Expected permissions to be enabled via ENABLE_BACKEND_ACCESS_CONTROL=True"
)
yield
await _reset_engines_and_prune()
async def test_permissions_example_flow(permissions_example_env):
"""Pytest version of `examples/python/permissions_example.py` (same scenarios, asserts instead of prints)."""
# Patch LLM calls so GRAPH_COMPLETION can run without external API keys.
llm_patch = patch(
"cognee.infrastructure.llm.LLMGateway.LLMGateway.acreate_structured_output",
new_callable=AsyncMock,
return_value="MOCK_ANSWER",
)
# Resolve example data file path (repo-shipped PDF).
repo_root = pathlib.Path(__file__).resolve().parent
explanation_file_path = str(repo_root / "test_data" / "artificial-intelligence.pdf")
assert pathlib.Path(explanation_file_path).exists(), (
f"Expected example PDF to exist at {explanation_file_path}"
)
# Same QUANTUM text as in the example.
text = """A quantum computer is a computer that takes advantage of quantum mechanical phenomena.
At small scales, physical matter exhibits properties of both particles and waves, and quantum computing leverages
this behavior, specifically quantum superposition and entanglement, using specialized hardware that supports the
preparation and manipulation of quantum states.
"""
# Create user_1, remember AI dataset.
user_1 = await create_user("user_1@example.com", "example")
ai_remember_result = await cognee.remember(
[explanation_file_path],
dataset_name="AI",
user=user_1,
self_improvement=False,
)
# Create user_2, remember QUANTUM dataset.
user_2 = await create_user("user_2@example.com", "example")
quantum_remember_result = await cognee.remember(
[text],
dataset_name="QUANTUM",
user=user_2,
self_improvement=False,
)
ai_dataset_id = _extract_dataset_id_from_remember(ai_remember_result)
quantum_dataset_id = _extract_dataset_id_from_remember(quantum_remember_result)
assert ai_dataset_id is not None
assert quantum_dataset_id is not None
with llm_patch:
# user_1 can read own dataset.
recall_results = await cognee.recall(
query_type=SearchType.GRAPH_COMPLETION,
query_text="What is in the document?",
user=user_1,
dataset_ids=[ai_dataset_id],
)
assert isinstance(recall_results, list) and len(recall_results) == 1
assert recall_results[0].dataset_name == "AI"
# GRAPH_COMPLETION appends an "Evidence:" section when include_references is
# enabled (off by default), so match the mocked completion as a prefix.
assert recall_results[0].text.startswith("MOCK_ANSWER")
# user_1 can't read dataset owned by user_2.
with pytest.raises(PermissionDeniedError):
await cognee.recall(
query_type=SearchType.GRAPH_COMPLETION,
query_text="What is in the document?",
user=user_1,
dataset_ids=[quantum_dataset_id],
)
# user_1 can't remember to user_2's dataset.
with pytest.raises(PermissionDeniedError):
await cognee.remember(
[explanation_file_path],
dataset_id=quantum_dataset_id,
user=user_1,
self_improvement=False,
)
# user_2 grants read permission to user_1 for QUANTUM dataset.
await authorized_give_permission_on_datasets(user_1.id, [quantum_dataset_id], "read", user_2.id)
with llm_patch:
# Now user_1 can read QUANTUM dataset via dataset_id.
recall_results = await cognee.recall(
query_type=SearchType.GRAPH_COMPLETION,
query_text="What is in the document?",
user=user_1,
dataset_ids=[quantum_dataset_id],
)
assert isinstance(recall_results, list) and len(recall_results) == 1
assert recall_results[0].dataset_name == "QUANTUM"
# GRAPH_COMPLETION appends an "Evidence:" section when include_references is
# enabled (off by default), so match the mocked completion as a prefix.
assert recall_results[0].text.startswith("MOCK_ANSWER")
# Tenant + role scenario.
tenant_id = await create_tenant("CogneeLab", user_2.id)
await select_tenant(user_id=user_2.id, tenant_id=tenant_id)
role_id = await create_role(role_name="Researcher", owner_id=user_2.id)
user_3 = await create_user("user_3@example.com", "example")
await add_user_to_tenant(user_id=user_3.id, tenant_id=tenant_id, owner_id=user_2.id)
await add_user_to_role(user_id=user_3.id, role_id=role_id, owner_id=user_2.id)
await select_tenant(user_id=user_3.id, tenant_id=tenant_id)
# Can't grant role permission on a dataset that isn't part of the active tenant.
with pytest.raises(PermissionDeniedError):
await authorized_give_permission_on_datasets(
role_id, [quantum_dataset_id], "read", user_2.id
)
# Re-create QUANTUM dataset in CogneeLab tenant so role permissions can be assigned.
user_2 = await get_user(user_2.id) # refresh tenant context
quantum_cognee_lab_remember_result = await cognee.remember(
[text],
dataset_name="QUANTUM_COGNEE_LAB",
user=user_2,
self_improvement=False,
)
quantum_cognee_lab_dataset_id = _extract_dataset_id_from_remember(
quantum_cognee_lab_remember_result
)
assert quantum_cognee_lab_dataset_id is not None
await authorized_give_permission_on_datasets(
role_id, [quantum_cognee_lab_dataset_id], "read", user_2.id
)
with llm_patch:
# user_3 can read via role permission.
recall_results = await cognee.recall(
query_type=SearchType.GRAPH_COMPLETION,
query_text="What is in the document?",
user=user_3,
dataset_ids=[quantum_cognee_lab_dataset_id],
)
assert isinstance(recall_results, list) and len(recall_results) == 1
assert recall_results[0].dataset_name == "QUANTUM_COGNEE_LAB"
# GRAPH_COMPLETION appends an "Evidence:" section when include_references is
# enabled (off by default), so match the mocked completion as a prefix.
assert recall_results[0].text.startswith("MOCK_ANSWER")
# Remove user_3 from tenant (tenant owner user_2 removes user_3).
await remove_user_from_tenant(user_id=user_3.id, tenant_id=tenant_id, owner_id=user_2.id)
# user_3 can no longer read the tenant dataset after being removed.
with pytest.raises(PermissionDeniedError):
with llm_patch:
await cognee.recall(
query_type=SearchType.GRAPH_COMPLETION,
query_text="What is in the document?",
user=user_3,
dataset_ids=[quantum_cognee_lab_dataset_id],
)
async def test_remove_user_from_tenant_non_owner_gets_403(permissions_example_env):
"""Only tenant owner can remove users; non-owner gets 403."""
owner = await create_user("owner_rm@example.com", "example")
member = await create_user("member_rm@example.com", "example")
tenant_id = await create_tenant("TenantRm", owner.id)
await add_user_to_tenant(user_id=member.id, tenant_id=tenant_id, owner_id=owner.id)
# Member cannot remove another user (or themselves) from the tenant.
with pytest.raises(PermissionDeniedError):
await remove_user_from_tenant(user_id=member.id, tenant_id=tenant_id, owner_id=member.id)
async def test_remove_user_from_tenant_cannot_remove_owner(permissions_example_env):
"""Removing the tenant owner from their own tenant returns 400."""
owner = await create_user("owner_only@example.com", "example")
tenant_id = await create_tenant("TenantOwner", owner.id)
with pytest.raises(CogneeValidationError) as exc_info:
await remove_user_from_tenant(user_id=owner.id, tenant_id=tenant_id, owner_id=owner.id)
assert exc_info.value.status_code == 400
assert "Cannot remove the tenant owner" in exc_info.value.message
async def test_remove_user_from_tenant_user_not_in_tenant_404(permissions_example_env):
"""Removing a user who is not in the tenant returns 404."""
owner = await create_user("owner_404@example.com", "example")
other_user = await create_user("other_404@example.com", "example")
tenant_id = await create_tenant("Tenant404", owner.id)
# other_user is not added to the tenant.
with pytest.raises(EntityNotFoundError) as exc_info:
await remove_user_from_tenant(user_id=other_user.id, tenant_id=tenant_id, owner_id=owner.id)
assert "User not found in this tenant" in exc_info.value.message