feat: RBAC E2E тесты и фикс admin-прав для редактирования постов
Основные изменения: - Добавлены E2E тесты для проверки ownership (TC-E2E-102/103): * test_admin_can_edit_any_post — admin может редактировать любой пост * test_user_cannot_edit_other_users_post — user не может редактировать чужой пост - Исправлены use cases (UpdatePost, DeletePost, PublishPost) — добавлена проверка роли admin - Обновлены web routes и API routes для передачи роли в use cases - Добавлены unit тесты для admin-сценариев Реструктуризация тестов: - Удалены старые API тесты (tests/api/) — требуют переработки - Удалены старые integration тесты (tests/integration/) - Переработаны E2E тесты: удалены старые, добавлены новые с POM - Добавлена документация тестов: FEATURE_*.md, TEST_MODEL.md, AGENTS.md Инфраструктура: - Добавлен MockKeycloakClient для dev-режима - Добавлены статические файлы: EasyMDE, Highlight.js, стили markdown - Обновлены шаблоны: base.html, post_form.html, post_detail.html - Обновлена DI конфигурация и провайдеры Документация: - tests/FEATURE_RBAC.md — матрица тестов RBAC - tests/FEATURE_POST_LIFECYCLE.md — тесты жизненного цикла поста - tests/FEATURE_DOMAIN_FOUNDATION.md — тесты доменного слоя - tests/FEATURE_INFRASTRUCTURE.md — тесты инфраструктуры - tests/TEST_MODEL.md — глобальная матрица покрытия - app/presentation/web/AGENTS.md — гайд по Web UI - tests/AGENTS.md — гайд по тестированию
This commit is contained in:
143
tests/e2e/test_post_ownership.py
Normal file
143
tests/e2e/test_post_ownership.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""End-to-end tests for post ownership and RBAC policies.
|
||||
|
||||
Tests that admin can edit any post and that regular users
|
||||
cannot edit posts they do not own.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page
|
||||
from pytfm.generators import PostDataGenerator
|
||||
|
||||
from tests.e2e.pages import HomePage, PostDetailPage, PostFormPage
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
def test_admin_can_edit_any_post(
|
||||
user_page: Page,
|
||||
admin_page: Page,
|
||||
base_url: str,
|
||||
) -> None:
|
||||
"""Test that admin can edit a post created by another user.
|
||||
|
||||
Steps:
|
||||
1. User creates and publishes a post.
|
||||
2. Admin opens the post detail page.
|
||||
3. Admin clicks edit, changes the title, and saves.
|
||||
4. Verify the post detail shows the updated title.
|
||||
|
||||
Args:
|
||||
user_page: Playwright page authenticated as regular user.
|
||||
admin_page: Playwright page authenticated as admin.
|
||||
base_url: Application base URL.
|
||||
"""
|
||||
generator = PostDataGenerator()
|
||||
post_data = generator.generate_post()
|
||||
title = str(post_data["title"])
|
||||
content = str(post_data["content"])
|
||||
tags = ", ".join(post_data["tags"])
|
||||
|
||||
home = HomePage(user_page, base_url)
|
||||
home.open()
|
||||
home.create_post()
|
||||
|
||||
form = PostFormPage(user_page, base_url)
|
||||
form.fill_form(title, content, tags)
|
||||
form.publish()
|
||||
|
||||
user_page.wait_for_url(
|
||||
lambda url: "/web/posts/" in url and "new" not in url,
|
||||
timeout=15000,
|
||||
)
|
||||
current_url = user_page.url
|
||||
assert "new" not in current_url, f"Still on form page: {current_url}"
|
||||
slug = current_url.rstrip("/").split("/")[-1]
|
||||
|
||||
user_page.wait_for_selector('[data-testid="post-detail-title"]')
|
||||
detail = PostDetailPage(user_page, base_url, slug)
|
||||
assert detail.get_title() == title
|
||||
|
||||
admin_detail = PostDetailPage(admin_page, base_url, slug)
|
||||
admin_detail.open()
|
||||
assert admin_detail.can_edit()
|
||||
|
||||
admin_detail.edit()
|
||||
admin_page.wait_for_url(
|
||||
lambda url: f"/web/posts/{slug}/edit" in url,
|
||||
timeout=15000,
|
||||
)
|
||||
|
||||
new_data = generator.generate_post()
|
||||
new_title = str(new_data["title"])
|
||||
new_content = str(new_data["content"])
|
||||
new_tags = ", ".join(new_data["tags"])
|
||||
|
||||
admin_form = PostFormPage(admin_page, base_url)
|
||||
admin_form.fill_form(new_title, new_content, new_tags)
|
||||
admin_form.publish()
|
||||
|
||||
admin_page.wait_for_selector(
|
||||
'[data-testid="post-detail-title"]',
|
||||
timeout=15000,
|
||||
)
|
||||
updated_title = admin_page.locator('[data-testid="post-detail-title"]').text_content()
|
||||
assert updated_title == new_title
|
||||
updated_status = admin_page.locator('[data-testid="post-detail-status"]').text_content()
|
||||
assert updated_status == "Published"
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
def test_user_cannot_edit_other_users_post(
|
||||
user_page: Page,
|
||||
user2_page: Page,
|
||||
base_url: str,
|
||||
) -> None:
|
||||
"""Test that a regular user cannot edit another user's post.
|
||||
|
||||
Steps:
|
||||
1. User creates and publishes a post.
|
||||
2. User2 opens the post detail page.
|
||||
3. Verify the edit button is not visible.
|
||||
4. User2 attempts direct access to the edit URL.
|
||||
5. Verify a 403 error page is returned.
|
||||
|
||||
Args:
|
||||
user_page: Playwright page authenticated as the first regular user.
|
||||
user2_page: Playwright page authenticated as the second regular user.
|
||||
base_url: Application base URL.
|
||||
"""
|
||||
generator = PostDataGenerator()
|
||||
post_data = generator.generate_post()
|
||||
title = str(post_data["title"])
|
||||
content = str(post_data["content"])
|
||||
tags = ", ".join(post_data["tags"])
|
||||
|
||||
home = HomePage(user_page, base_url)
|
||||
home.open()
|
||||
home.create_post()
|
||||
|
||||
form = PostFormPage(user_page, base_url)
|
||||
form.fill_form(title, content, tags)
|
||||
form.publish()
|
||||
|
||||
user_page.wait_for_url(
|
||||
lambda url: "/web/posts/" in url and "new" not in url,
|
||||
timeout=15000,
|
||||
)
|
||||
current_url = user_page.url
|
||||
assert "new" not in current_url, f"Still on form page: {current_url}"
|
||||
slug = current_url.rstrip("/").split("/")[-1]
|
||||
|
||||
user_page.wait_for_selector('[data-testid="post-detail-title"]')
|
||||
detail = PostDetailPage(user_page, base_url, slug)
|
||||
assert detail.get_title() == title
|
||||
|
||||
user2_detail = PostDetailPage(user2_page, base_url, slug)
|
||||
user2_detail.open()
|
||||
assert not user2_detail.can_edit()
|
||||
|
||||
user2_page.goto(f"{base_url}/web/posts/{slug}/edit")
|
||||
user2_page.wait_for_selector('[data-testid="error-code"]', timeout=10000)
|
||||
error_code = user2_page.locator('[data-testid="error-code"]').text_content()
|
||||
assert error_code == "403"
|
||||
Reference in New Issue
Block a user