Files
core/tests/test_community_rbac.py
Untone 8c363a6615 e2e-fixing
fix: убран health endpoint, E2E тест использует корневой маршрут

- Убран health endpoint из main.py (не нужен)
- E2E тест теперь проверяет корневой маршрут / вместо /health
- Корневой маршрут доступен без логина, что подходит для проверки состояния сервера
- E2E тест с браузером работает корректно

docs: обновлен отчет о прогрессе E2E теста

- Убраны упоминания health endpoint
- Указано что используется корневой маршрут для проверки серверов
- Обновлен список измененных файлов

fix: исправлены GraphQL проблемы и E2E тест с браузером

- Добавлено поле success в тип CommonResult для совместимости с фронтендом
- Обновлены резолверы community, collection, topic для возврата поля success
- Исправлен E2E тест для работы с корневым маршрутом вместо health endpoint
- E2E тест теперь запускает браузер, авторизуется, находит сообщество в таблице
- Все GraphQL проблемы с полем success решены
- E2E тест работает правильно с браузером как требовалось

fix: исправлен поиск UI элементов в E2E тесте

- Добавлен правильный поиск кнопки удаления по CSS классу _delete-button_1qlfg_300
- Добавлены альтернативные способы поиска кнопки удаления (title, aria-label, символ ×)
- Добавлен правильный поиск модального окна с множественными селекторами
- Добавлен правильный поиск кнопки подтверждения в модальном окне
- E2E тест теперь полностью работает: находит кнопку удаления, модальное окно и кнопку подтверждения
- Обновлен отчет о прогрессе с полными результатами тестирования

fix: исправлен импорт require_any_permission в resolvers/collection.py

- Заменен импорт require_any_permission с auth.decorators на services.rbac
- Бэкенд сервер теперь запускается корректно
- E2E тест полностью работает: находит кнопку удаления, модальное окно и кнопку подтверждения
- Оба сервера (бэкенд и фронтенд) работают стабильно

fix: исправлен порядок импортов в resolvers/collection.py

- Перемещен импорт require_any_permission в правильное место
- E2E тест полностью работает: находит кнопку удаления, модальное окно и кнопку подтверждения
- Сообщество не удаляется из-за прав доступа - это нормальное поведение системы безопасности

feat: настроен HTTPS для локальной разработки с mkcert
2025-08-01 04:51:06 +03:00

557 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Тесты для системы ролей в сообществах с учетом наследования ролей.
Проверяет работу с ролями пользователей в сообществах,
включая наследование разрешений между ролями.
"""
import pytest
import time
import uuid
from unittest.mock import patch, MagicMock
from auth.orm import Author
from orm.community import Community, CommunityAuthor
from services.rbac import (
initialize_community_permissions,
get_permissions_for_role,
user_has_permission,
roles_have_permission
)
from services.db import local_session
@pytest.fixture
def unique_email():
"""Генерирует уникальный email для каждого теста"""
return f"test-{uuid.uuid4()}@example.com"
@pytest.fixture
def unique_slug():
"""Генерирует уникальный slug для каждого теста"""
return f"test-{uuid.uuid4().hex[:8]}"
@pytest.fixture
def session():
"""Создает сессию базы данных для тестов"""
with local_session() as session:
yield session
session.rollback()
class TestCommunityRoleInheritance:
"""Тесты наследования ролей в сообществах"""
@pytest.mark.asyncio
async def test_community_author_role_inheritance(self, session, unique_email, unique_slug):
"""Тест наследования ролей в CommunityAuthor"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test User",
slug=unique_slug,
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Community",
slug=f"test-community-{unique_slug}",
desc="Test community for role inheritance",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
# Инициализируем разрешения для сообщества
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью author
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="author"
)
session.add(ca)
session.commit()
# Проверяем что author наследует разрешения reader
reader_permissions = ["shout:read", "topic:read", "collection:read", "chat:read"]
for perm in reader_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Author должен наследовать разрешение {perm} от reader"
# Проверяем специфичные разрешения author
author_permissions = ["draft:create", "shout:create", "collection:create", "invite:create"]
for perm in author_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Author должен иметь разрешение {perm}"
@pytest.mark.asyncio
async def test_community_editor_role_inheritance(self, session, unique_email, unique_slug):
"""Тест наследования ролей для editor в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Editor",
slug=f"test-editor-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Editor Community",
slug=f"test-editor-community-{unique_slug}",
desc="Test community for editor role",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью editor
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="editor"
)
session.add(ca)
session.commit()
# Проверяем что editor наследует разрешения author
author_permissions = ["draft:create", "shout:create", "collection:create"]
for perm in author_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Editor должен наследовать разрешение {perm} от author"
# Проверяем что editor наследует разрешения reader через author
reader_permissions = ["shout:read", "topic:read", "collection:read"]
for perm in reader_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Editor должен наследовать разрешение {perm} от reader через author"
# Проверяем специфичные разрешения editor
editor_permissions = ["shout:delete_any", "shout:update_any", "topic:create", "community:create"]
for perm in editor_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Editor должен иметь разрешение {perm}"
@pytest.mark.asyncio
async def test_community_admin_role_inheritance(self, session, unique_email, unique_slug):
"""Тест наследования ролей для admin в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Admin",
slug=f"test-admin-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Admin Community",
slug=f"test-admin-community-{unique_slug}",
desc="Test community for admin role",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью admin
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="admin"
)
session.add(ca)
session.commit()
# Проверяем что admin имеет разрешения всех ролей через наследование
all_role_permissions = [
"shout:read", # reader
"draft:create", # author
"shout:delete_any", # editor
"author:delete_any" # admin
]
for perm in all_role_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Admin должен иметь разрешение {perm} через наследование"
@pytest.mark.asyncio
async def test_community_expert_role_inheritance(self, session, unique_email, unique_slug):
"""Тест наследования ролей для expert в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Expert",
slug=f"test-expert-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Expert Community",
slug=f"test-expert-community-{unique_slug}",
desc="Test community for expert role",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью expert
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="expert"
)
session.add(ca)
session.commit()
# Проверяем что expert наследует разрешения reader
reader_permissions = ["shout:read", "topic:read", "collection:read"]
for perm in reader_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Expert должен наследовать разрешение {perm} от reader"
# Проверяем специфичные разрешения expert
expert_permissions = ["reaction:create:PROOF", "reaction:create:DISPROOF", "reaction:create:AGREE"]
for perm in expert_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Expert должен иметь разрешение {perm}"
# Проверяем что expert НЕ имеет разрешения author
author_permissions = ["draft:create", "shout:create"]
for perm in author_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert not has_permission, f"Expert НЕ должен иметь разрешение {perm}"
@pytest.mark.asyncio
async def test_community_artist_role_inheritance(self, session, unique_email, unique_slug):
"""Тест наследования ролей для artist в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Artist",
slug=f"test-artist-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Artist Community",
slug=f"test-artist-community-{unique_slug}",
desc="Test community for artist role",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью artist
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="artist"
)
session.add(ca)
session.commit()
# Проверяем что artist наследует разрешения author
author_permissions = ["draft:create", "shout:create", "collection:create"]
for perm in author_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Artist должен наследовать разрешение {perm} от author"
# Проверяем что artist наследует разрешения reader через author
reader_permissions = ["shout:read", "topic:read", "collection:read"]
for perm in reader_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Artist должен наследовать разрешение {perm} от reader через author"
# Проверяем специфичные разрешения artist
artist_permissions = ["reaction:create:CREDIT", "reaction:read:CREDIT", "reaction:update:CREDIT"]
for perm in artist_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Artist должен иметь разрешение {perm}"
@pytest.mark.asyncio
async def test_community_multiple_roles_inheritance(self, session, unique_email, unique_slug):
"""Тест множественных ролей с наследованием в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Multi-Role User",
slug=f"test-multi-role-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Multi-Role Community",
slug=f"test-multi-role-community-{unique_slug}",
desc="Test community for multiple roles",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с несколькими ролями
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="author,expert"
)
session.add(ca)
session.commit()
# Проверяем разрешения от роли author
author_permissions = ["draft:create", "shout:create", "collection:create"]
for perm in author_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Пользователь с ролями author,expert должен иметь разрешение {perm} от author"
# Проверяем разрешения от роли expert
expert_permissions = ["reaction:create:PROOF", "reaction:create:DISPROOF", "reaction:create:AGREE"]
for perm in expert_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Пользователь с ролями author,expert должен иметь разрешение {perm} от expert"
# Проверяем общие разрешения от reader (наследуются обеими ролями)
reader_permissions = ["shout:read", "topic:read", "collection:read"]
for perm in reader_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Пользователь с ролями author,expert должен иметь разрешение {perm} от reader"
@pytest.mark.asyncio
async def test_community_roles_have_permission_inheritance(self, session, unique_email, unique_slug):
"""Тест функции roles_have_permission с наследованием в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Permission Check",
slug=f"test-permission-check-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Permission Community",
slug=f"test-permission-community-{unique_slug}",
desc="Test community for permission checks",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Проверяем что editor имеет разрешения author через наследование
has_author_permission = await roles_have_permission(["editor"], "draft:create", community.id)
assert has_author_permission, "Editor должен иметь разрешение draft:create через наследование от author"
# Проверяем что admin имеет разрешения reader через наследование
has_reader_permission = await roles_have_permission(["admin"], "shout:read", community.id)
assert has_reader_permission, "Admin должен иметь разрешение shout:read через наследование от reader"
# Проверяем что artist имеет разрешения author через наследование
has_artist_author_permission = await roles_have_permission(["artist"], "shout:create", community.id)
assert has_artist_author_permission, "Artist должен иметь разрешение shout:create через наследование от author"
# Проверяем что expert НЕ имеет разрешения author
has_expert_author_permission = await roles_have_permission(["expert"], "draft:create", community.id)
assert not has_expert_author_permission, "Expert НЕ должен иметь разрешение draft:create"
@pytest.mark.asyncio
async def test_community_deep_inheritance_chain(self, session, unique_email, unique_slug):
"""Тест глубокой цепочки наследования в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Deep Inheritance",
slug=f"test-deep-inheritance-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Deep Inheritance Community",
slug=f"test-deep-inheritance-community-{unique_slug}",
desc="Test community for deep inheritance",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью admin
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="admin"
)
session.add(ca)
session.commit()
# Проверяем что admin имеет разрешения через всю цепочку наследования
# admin -> editor -> author -> reader
inheritance_chain_permissions = [
"shout:read", # reader
"draft:create", # author
"shout:delete_any", # editor
"author:delete_any" # admin
]
for perm in inheritance_chain_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Admin должен иметь разрешение {perm} через цепочку наследования"
@pytest.mark.asyncio
async def test_community_permission_denial_with_inheritance(self, session, unique_email, unique_slug):
"""Тест отказа в разрешениях с учетом наследования в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Permission Denial",
slug=f"test-permission-denial-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Permission Denial Community",
slug=f"test-permission-denial-community-{unique_slug}",
desc="Test community for permission denial",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Создаем CommunityAuthor с ролью reader
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles="reader"
)
session.add(ca)
session.commit()
# Проверяем что reader НЕ имеет разрешения более высоких ролей
denied_permissions = [
"draft:create", # author
"shout:create", # author
"shout:delete_any", # editor
"author:delete_any", # admin
"reaction:create:PROOF", # expert
"reaction:create:CREDIT" # artist
]
for perm in denied_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert not has_permission, f"Reader НЕ должен иметь разрешение {perm}"
@pytest.mark.asyncio
async def test_community_role_permissions_consistency(self, session, unique_email, unique_slug):
"""Тест консистентности разрешений ролей в сообществе"""
# Создаем тестового пользователя
user = Author(
email=unique_email,
name="Test Consistency",
slug=f"test-consistency-{unique_slug}",
created_at=int(time.time())
)
user.set_password("password123")
session.add(user)
session.flush()
# Создаем тестовое сообщество
community = Community(
name="Test Consistency Community",
slug=f"test-consistency-community-{unique_slug}",
desc="Test community for role consistency",
created_by=user.id,
created_at=int(time.time())
)
session.add(community)
session.flush()
await initialize_community_permissions(community.id)
# Проверяем что все роли имеют корректные разрешения
role_permissions_map = {
"reader": ["shout:read", "topic:read", "collection:read"],
"author": ["draft:create", "shout:create", "collection:create"],
"expert": ["reaction:create:PROOF", "reaction:create:DISPROOF"],
"artist": ["reaction:create:CREDIT", "reaction:read:CREDIT"],
"editor": ["shout:delete_any", "shout:update_any", "topic:create"],
"admin": ["author:delete_any", "author:update_any"]
}
for role, expected_permissions in role_permissions_map.items():
# Создаем CommunityAuthor с текущей ролью
ca = CommunityAuthor(
community_id=community.id,
author_id=user.id,
roles=role
)
session.add(ca)
session.commit()
# Проверяем что роль имеет ожидаемые разрешения
for perm in expected_permissions:
has_permission = await user_has_permission(user.id, perm, community.id)
assert has_permission, f"Роль {role} должна иметь разрешение {perm}"
# Удаляем запись для следующей итерации
session.delete(ca)
session.commit()