@@ -8,151 +8,298 @@ from services.dataset_service import DatasetService
|
|||||||
from services.errors.account import NoPermissionError
|
from services.errors.account import NoPermissionError
|
||||||
|
|
||||||
|
|
||||||
|
class DatasetPermissionTestDataFactory:
|
||||||
|
"""Factory class for creating test data and mock objects for dataset permission tests."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_dataset_mock(
|
||||||
|
dataset_id: str = "dataset-123",
|
||||||
|
tenant_id: str = "test-tenant-123",
|
||||||
|
created_by: str = "creator-456",
|
||||||
|
permission: DatasetPermissionEnum = DatasetPermissionEnum.ONLY_ME,
|
||||||
|
**kwargs,
|
||||||
|
) -> Mock:
|
||||||
|
"""Create a mock dataset with specified attributes."""
|
||||||
|
dataset = Mock(spec=Dataset)
|
||||||
|
dataset.id = dataset_id
|
||||||
|
dataset.tenant_id = tenant_id
|
||||||
|
dataset.created_by = created_by
|
||||||
|
dataset.permission = permission
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(dataset, key, value)
|
||||||
|
return dataset
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_user_mock(
|
||||||
|
user_id: str = "user-789",
|
||||||
|
tenant_id: str = "test-tenant-123",
|
||||||
|
role: TenantAccountRole = TenantAccountRole.NORMAL,
|
||||||
|
**kwargs,
|
||||||
|
) -> Mock:
|
||||||
|
"""Create a mock user with specified attributes."""
|
||||||
|
user = Mock(spec=Account)
|
||||||
|
user.id = user_id
|
||||||
|
user.current_tenant_id = tenant_id
|
||||||
|
user.current_role = role
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(user, key, value)
|
||||||
|
return user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_dataset_permission_mock(
|
||||||
|
dataset_id: str = "dataset-123",
|
||||||
|
account_id: str = "user-789",
|
||||||
|
**kwargs,
|
||||||
|
) -> Mock:
|
||||||
|
"""Create a mock dataset permission record."""
|
||||||
|
permission = Mock(spec=DatasetPermission)
|
||||||
|
permission.dataset_id = dataset_id
|
||||||
|
permission.account_id = account_id
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(permission, key, value)
|
||||||
|
return permission
|
||||||
|
|
||||||
|
|
||||||
class TestDatasetPermissionService:
|
class TestDatasetPermissionService:
|
||||||
"""Test cases for dataset permission checking functionality"""
|
"""
|
||||||
|
Comprehensive unit tests for DatasetService.check_dataset_permission method.
|
||||||
|
|
||||||
def setup_method(self):
|
This test suite covers all permission scenarios including:
|
||||||
"""Set up test fixtures"""
|
- Cross-tenant access restrictions
|
||||||
# Mock tenant and user
|
- Owner privilege checks
|
||||||
self.tenant_id = "test-tenant-123"
|
- Different permission levels (ONLY_ME, ALL_TEAM, PARTIAL_TEAM)
|
||||||
self.creator_id = "creator-456"
|
- Explicit permission checks for PARTIAL_TEAM
|
||||||
self.normal_user_id = "normal-789"
|
- Error conditions and logging
|
||||||
self.owner_user_id = "owner-999"
|
"""
|
||||||
|
|
||||||
# Mock dataset
|
@pytest.fixture
|
||||||
self.dataset = Mock(spec=Dataset)
|
def mock_dataset_service_dependencies(self):
|
||||||
self.dataset.id = "dataset-123"
|
"""Common mock setup for dataset service dependencies."""
|
||||||
self.dataset.tenant_id = self.tenant_id
|
with patch("services.dataset_service.db.session") as mock_session:
|
||||||
self.dataset.created_by = self.creator_id
|
yield {
|
||||||
|
"db_session": mock_session,
|
||||||
|
}
|
||||||
|
|
||||||
# Mock users
|
@pytest.fixture
|
||||||
self.creator_user = Mock(spec=Account)
|
def mock_logging_dependencies(self):
|
||||||
self.creator_user.id = self.creator_id
|
"""Mock setup for logging tests."""
|
||||||
self.creator_user.current_tenant_id = self.tenant_id
|
with patch("services.dataset_service.logging") as mock_logging:
|
||||||
self.creator_user.current_role = TenantAccountRole.EDITOR
|
yield {
|
||||||
|
"logging": mock_logging,
|
||||||
self.normal_user = Mock(spec=Account)
|
}
|
||||||
self.normal_user.id = self.normal_user_id
|
|
||||||
self.normal_user.current_tenant_id = self.tenant_id
|
|
||||||
self.normal_user.current_role = TenantAccountRole.NORMAL
|
|
||||||
|
|
||||||
self.owner_user = Mock(spec=Account)
|
|
||||||
self.owner_user.id = self.owner_user_id
|
|
||||||
self.owner_user.current_tenant_id = self.tenant_id
|
|
||||||
self.owner_user.current_role = TenantAccountRole.OWNER
|
|
||||||
|
|
||||||
def test_permission_check_different_tenant_should_fail(self):
|
|
||||||
"""Test that users from different tenants cannot access dataset"""
|
|
||||||
self.normal_user.current_tenant_id = "different-tenant"
|
|
||||||
|
|
||||||
with pytest.raises(NoPermissionError, match="You do not have permission to access this dataset."):
|
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.normal_user)
|
|
||||||
|
|
||||||
def test_owner_can_access_any_dataset(self):
|
|
||||||
"""Test that tenant owners can access any dataset regardless of permission"""
|
|
||||||
self.dataset.permission = DatasetPermissionEnum.ONLY_ME
|
|
||||||
|
|
||||||
|
def _assert_permission_check_passes(self, dataset: Mock, user: Mock):
|
||||||
|
"""Helper method to verify that permission check passes without raising exceptions."""
|
||||||
# Should not raise any exception
|
# Should not raise any exception
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.owner_user)
|
DatasetService.check_dataset_permission(dataset, user)
|
||||||
|
|
||||||
def test_only_me_permission_creator_can_access(self):
|
def _assert_permission_check_fails(
|
||||||
"""Test ONLY_ME permission allows only creator to access"""
|
self, dataset: Mock, user: Mock, expected_message: str = "You do not have permission to access this dataset."
|
||||||
self.dataset.permission = DatasetPermissionEnum.ONLY_ME
|
):
|
||||||
|
"""Helper method to verify that permission check fails with expected error."""
|
||||||
|
with pytest.raises(NoPermissionError, match=expected_message):
|
||||||
|
DatasetService.check_dataset_permission(dataset, user)
|
||||||
|
|
||||||
# Creator should be able to access
|
def _assert_database_query_called(self, mock_session: Mock, dataset_id: str, account_id: str):
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.creator_user)
|
"""Helper method to verify database query calls for permission checks."""
|
||||||
|
mock_session.query().filter_by.assert_called_with(dataset_id=dataset_id, account_id=account_id)
|
||||||
|
|
||||||
def test_only_me_permission_others_cannot_access(self):
|
def _assert_database_query_not_called(self, mock_session: Mock):
|
||||||
"""Test ONLY_ME permission denies access to non-creators"""
|
"""Helper method to verify that database query was not called."""
|
||||||
self.dataset.permission = DatasetPermissionEnum.ONLY_ME
|
|
||||||
|
|
||||||
with pytest.raises(NoPermissionError, match="You do not have permission to access this dataset."):
|
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.normal_user)
|
|
||||||
|
|
||||||
def test_all_team_permission_allows_access(self):
|
|
||||||
"""Test ALL_TEAM permission allows any team member to access"""
|
|
||||||
self.dataset.permission = DatasetPermissionEnum.ALL_TEAM
|
|
||||||
|
|
||||||
# Should not raise any exception for team members
|
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.normal_user)
|
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.creator_user)
|
|
||||||
|
|
||||||
@patch("services.dataset_service.db.session")
|
|
||||||
def test_partial_team_permission_creator_can_access(self, mock_session):
|
|
||||||
"""Test PARTIAL_TEAM permission allows creator to access"""
|
|
||||||
self.dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM
|
|
||||||
|
|
||||||
# Should not raise any exception for creator
|
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.creator_user)
|
|
||||||
|
|
||||||
# Should not query database for creator
|
|
||||||
mock_session.query.assert_not_called()
|
mock_session.query.assert_not_called()
|
||||||
|
|
||||||
@patch("services.dataset_service.db.session")
|
# ==================== Cross-Tenant Access Tests ====================
|
||||||
def test_partial_team_permission_with_explicit_permission(self, mock_session):
|
|
||||||
"""Test PARTIAL_TEAM permission allows users with explicit permission"""
|
def test_permission_check_different_tenant_should_fail(self):
|
||||||
self.dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM
|
"""Test that users from different tenants cannot access dataset regardless of other permissions."""
|
||||||
|
# Create dataset and user from different tenants
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(
|
||||||
|
tenant_id="tenant-123", permission=DatasetPermissionEnum.ALL_TEAM
|
||||||
|
)
|
||||||
|
user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="user-789", tenant_id="different-tenant-456", role=TenantAccountRole.EDITOR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should fail due to different tenant
|
||||||
|
self._assert_permission_check_fails(dataset, user)
|
||||||
|
|
||||||
|
# ==================== Owner Privilege Tests ====================
|
||||||
|
|
||||||
|
def test_owner_can_access_any_dataset(self):
|
||||||
|
"""Test that tenant owners can access any dataset regardless of permission level."""
|
||||||
|
# Create dataset with restrictive permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(permission=DatasetPermissionEnum.ONLY_ME)
|
||||||
|
|
||||||
|
# Create owner user
|
||||||
|
owner_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="owner-999", role=TenantAccountRole.OWNER
|
||||||
|
)
|
||||||
|
|
||||||
|
# Owner should have access regardless of dataset permission
|
||||||
|
self._assert_permission_check_passes(dataset, owner_user)
|
||||||
|
|
||||||
|
# ==================== ONLY_ME Permission Tests ====================
|
||||||
|
|
||||||
|
def test_only_me_permission_creator_can_access(self):
|
||||||
|
"""Test ONLY_ME permission allows only the dataset creator to access."""
|
||||||
|
# Create dataset with ONLY_ME permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(
|
||||||
|
created_by="creator-456", permission=DatasetPermissionEnum.ONLY_ME
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create creator user
|
||||||
|
creator_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="creator-456", role=TenantAccountRole.EDITOR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Creator should be able to access
|
||||||
|
self._assert_permission_check_passes(dataset, creator_user)
|
||||||
|
|
||||||
|
def test_only_me_permission_others_cannot_access(self):
|
||||||
|
"""Test ONLY_ME permission denies access to non-creators."""
|
||||||
|
# Create dataset with ONLY_ME permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(
|
||||||
|
created_by="creator-456", permission=DatasetPermissionEnum.ONLY_ME
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create normal user (not the creator)
|
||||||
|
normal_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="normal-789", role=TenantAccountRole.NORMAL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Non-creator should be denied access
|
||||||
|
self._assert_permission_check_fails(dataset, normal_user)
|
||||||
|
|
||||||
|
# ==================== ALL_TEAM Permission Tests ====================
|
||||||
|
|
||||||
|
def test_all_team_permission_allows_access(self):
|
||||||
|
"""Test ALL_TEAM permission allows any team member to access the dataset."""
|
||||||
|
# Create dataset with ALL_TEAM permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(permission=DatasetPermissionEnum.ALL_TEAM)
|
||||||
|
|
||||||
|
# Create different types of team members
|
||||||
|
normal_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="normal-789", role=TenantAccountRole.NORMAL
|
||||||
|
)
|
||||||
|
editor_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="editor-456", role=TenantAccountRole.EDITOR
|
||||||
|
)
|
||||||
|
|
||||||
|
# All team members should have access
|
||||||
|
self._assert_permission_check_passes(dataset, normal_user)
|
||||||
|
self._assert_permission_check_passes(dataset, editor_user)
|
||||||
|
|
||||||
|
# ==================== PARTIAL_TEAM Permission Tests ====================
|
||||||
|
|
||||||
|
def test_partial_team_permission_creator_can_access(self, mock_dataset_service_dependencies):
|
||||||
|
"""Test PARTIAL_TEAM permission allows creator to access without database query."""
|
||||||
|
# Create dataset with PARTIAL_TEAM permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(
|
||||||
|
created_by="creator-456", permission=DatasetPermissionEnum.PARTIAL_TEAM
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create creator user
|
||||||
|
creator_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="creator-456", role=TenantAccountRole.EDITOR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Creator should have access without database query
|
||||||
|
self._assert_permission_check_passes(dataset, creator_user)
|
||||||
|
self._assert_database_query_not_called(mock_dataset_service_dependencies["db_session"])
|
||||||
|
|
||||||
|
def test_partial_team_permission_with_explicit_permission(self, mock_dataset_service_dependencies):
|
||||||
|
"""Test PARTIAL_TEAM permission allows users with explicit permission records."""
|
||||||
|
# Create dataset with PARTIAL_TEAM permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(permission=DatasetPermissionEnum.PARTIAL_TEAM)
|
||||||
|
|
||||||
|
# Create normal user (not the creator)
|
||||||
|
normal_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="normal-789", role=TenantAccountRole.NORMAL
|
||||||
|
)
|
||||||
|
|
||||||
# Mock database query to return a permission record
|
# Mock database query to return a permission record
|
||||||
mock_permission = Mock(spec=DatasetPermission)
|
mock_permission = DatasetPermissionTestDataFactory.create_dataset_permission_mock(
|
||||||
mock_session.query().filter_by().first.return_value = mock_permission
|
dataset_id=dataset.id, account_id=normal_user.id
|
||||||
|
)
|
||||||
|
mock_dataset_service_dependencies["db_session"].query().filter_by().first.return_value = mock_permission
|
||||||
|
|
||||||
# Should not raise any exception
|
# User with explicit permission should have access
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.normal_user)
|
self._assert_permission_check_passes(dataset, normal_user)
|
||||||
|
self._assert_database_query_called(mock_dataset_service_dependencies["db_session"], dataset.id, normal_user.id)
|
||||||
|
|
||||||
# Verify database was queried correctly
|
def test_partial_team_permission_without_explicit_permission(self, mock_dataset_service_dependencies):
|
||||||
mock_session.query().filter_by.assert_called_with(dataset_id=self.dataset.id, account_id=self.normal_user.id)
|
"""Test PARTIAL_TEAM permission denies users without explicit permission records."""
|
||||||
|
# Create dataset with PARTIAL_TEAM permission
|
||||||
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(permission=DatasetPermissionEnum.PARTIAL_TEAM)
|
||||||
|
|
||||||
@patch("services.dataset_service.db.session")
|
# Create normal user (not the creator)
|
||||||
def test_partial_team_permission_without_explicit_permission(self, mock_session):
|
normal_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
"""Test PARTIAL_TEAM permission denies users without explicit permission"""
|
user_id="normal-789", role=TenantAccountRole.NORMAL
|
||||||
self.dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM
|
)
|
||||||
|
|
||||||
# Mock database query to return None (no permission record)
|
# Mock database query to return None (no permission record)
|
||||||
mock_session.query().filter_by().first.return_value = None
|
mock_dataset_service_dependencies["db_session"].query().filter_by().first.return_value = None
|
||||||
|
|
||||||
with pytest.raises(NoPermissionError, match="You do not have permission to access this dataset."):
|
# User without explicit permission should be denied access
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.normal_user)
|
self._assert_permission_check_fails(dataset, normal_user)
|
||||||
|
self._assert_database_query_called(mock_dataset_service_dependencies["db_session"], dataset.id, normal_user.id)
|
||||||
|
|
||||||
# Verify database was queried correctly
|
def test_partial_team_permission_non_creator_without_permission_fails(self, mock_dataset_service_dependencies):
|
||||||
mock_session.query().filter_by.assert_called_with(dataset_id=self.dataset.id, account_id=self.normal_user.id)
|
"""Test that non-creators without explicit permission are denied access to PARTIAL_TEAM datasets."""
|
||||||
|
# Create dataset with PARTIAL_TEAM permission
|
||||||
@patch("services.dataset_service.db.session")
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(
|
||||||
def test_partial_team_permission_non_creator_without_permission_fails(self, mock_session):
|
created_by="creator-456", permission=DatasetPermissionEnum.PARTIAL_TEAM
|
||||||
"""Test that non-creators without explicit permission are denied access"""
|
)
|
||||||
self.dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM
|
|
||||||
|
|
||||||
# Create a different user (not the creator)
|
# Create a different user (not the creator)
|
||||||
other_user = Mock(spec=Account)
|
other_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
other_user.id = "other-user-123"
|
user_id="other-user-123", role=TenantAccountRole.NORMAL
|
||||||
other_user.current_tenant_id = self.tenant_id
|
)
|
||||||
other_user.current_role = TenantAccountRole.NORMAL
|
|
||||||
|
|
||||||
# Mock database query to return None (no permission record)
|
# Mock database query to return None (no permission record)
|
||||||
mock_session.query().filter_by().first.return_value = None
|
mock_dataset_service_dependencies["db_session"].query().filter_by().first.return_value = None
|
||||||
|
|
||||||
with pytest.raises(NoPermissionError, match="You do not have permission to access this dataset."):
|
# Non-creator without explicit permission should be denied access
|
||||||
DatasetService.check_dataset_permission(self.dataset, other_user)
|
self._assert_permission_check_fails(dataset, other_user)
|
||||||
|
self._assert_database_query_called(mock_dataset_service_dependencies["db_session"], dataset.id, other_user.id)
|
||||||
|
|
||||||
|
# ==================== Enum Usage Tests ====================
|
||||||
|
|
||||||
def test_partial_team_permission_uses_correct_enum(self):
|
def test_partial_team_permission_uses_correct_enum(self):
|
||||||
"""Test that the method correctly uses DatasetPermissionEnum.PARTIAL_TEAM"""
|
"""Test that the method correctly uses DatasetPermissionEnum.PARTIAL_TEAM instead of string literals."""
|
||||||
# This test ensures we're using the enum instead of string literals
|
# Create dataset with PARTIAL_TEAM permission using enum
|
||||||
self.dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(
|
||||||
|
created_by="creator-456", permission=DatasetPermissionEnum.PARTIAL_TEAM
|
||||||
# Creator should always have access
|
)
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.creator_user)
|
|
||||||
|
# Create creator user
|
||||||
@patch("services.dataset_service.logging")
|
creator_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
@patch("services.dataset_service.db.session")
|
user_id="creator-456", role=TenantAccountRole.EDITOR
|
||||||
def test_permission_denied_logs_debug_message(self, mock_session, mock_logging):
|
)
|
||||||
"""Test that permission denied events are logged"""
|
|
||||||
self.dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM
|
# Creator should always have access regardless of permission level
|
||||||
mock_session.query().filter_by().first.return_value = None
|
self._assert_permission_check_passes(dataset, creator_user)
|
||||||
|
|
||||||
with pytest.raises(NoPermissionError):
|
# ==================== Logging Tests ====================
|
||||||
DatasetService.check_dataset_permission(self.dataset, self.normal_user)
|
|
||||||
|
def test_permission_denied_logs_debug_message(self, mock_dataset_service_dependencies, mock_logging_dependencies):
|
||||||
# Verify debug message was logged
|
"""Test that permission denied events are properly logged for debugging purposes."""
|
||||||
mock_logging.debug.assert_called_with(
|
# Create dataset with PARTIAL_TEAM permission
|
||||||
f"User {self.normal_user.id} does not have permission to access dataset {self.dataset.id}"
|
dataset = DatasetPermissionTestDataFactory.create_dataset_mock(permission=DatasetPermissionEnum.PARTIAL_TEAM)
|
||||||
|
|
||||||
|
# Create normal user (not the creator)
|
||||||
|
normal_user = DatasetPermissionTestDataFactory.create_user_mock(
|
||||||
|
user_id="normal-789", role=TenantAccountRole.NORMAL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mock database query to return None (no permission record)
|
||||||
|
mock_dataset_service_dependencies["db_session"].query().filter_by().first.return_value = None
|
||||||
|
|
||||||
|
# Attempt permission check (should fail)
|
||||||
|
with pytest.raises(NoPermissionError):
|
||||||
|
DatasetService.check_dataset_permission(dataset, normal_user)
|
||||||
|
|
||||||
|
# Verify debug message was logged with correct user and dataset information
|
||||||
|
mock_logging_dependencies["logging"].debug.assert_called_with(
|
||||||
|
f"User {normal_user.id} does not have permission to access dataset {dataset.id}"
|
||||||
)
|
)
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user