Files
blog.pyaqa.ru/tests/FEATURE_DOMAIN_FOUNDATION.md
Sergey Vanyushkin 46cc06b596 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 — гайд по тестированию
2026-05-07 19:55:15 +03:00

8.6 KiB

Test Model: Domain Foundation

Feature: Core domain building blocks — entities, value objects, and exceptions. These tests validate business rules at the domain layer with no external dependencies.

Unit Test Cases

Entities

TC-UNIT-201: Post Creation

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_creation
  • Expected:
    • id is a valid UUID
    • title.value == "Test Title"
    • content.value matches input
    • slug.value == "test-title" (auto-generated)
    • author_id == "user-123"
    • published is False
    • tags == ["test", "python"]
  • Last Verified: 2026-05-07

TC-UNIT-202: Post Publish

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_publish
  • Expected: published transitions from False to True
  • Last Verified: 2026-05-07

TC-UNIT-203: Post Unpublish

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_unpublish
  • Expected: published transitions from True to False
  • Last Verified: 2026-05-07

TC-UNIT-204: Post Update Title

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_update_title
  • Expected: Title and slug updated, updated_at changed
  • Last Verified: 2026-05-07

TC-UNIT-205: Post Update Content

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_update_content
  • Expected: Content updated, updated_at changed
  • Last Verified: 2026-05-07

TC-UNIT-206: Post Update Tags

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_update_tags
  • Expected: Tags replaced with new list, updated_at changed
  • Last Verified: 2026-05-07

TC-UNIT-207: Post Add Tag

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_add_tag
  • Expected: New tag appended to existing tags
  • Last Verified: 2026-05-07

TC-UNIT-208: Post Remove Tag

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_entities.py::TestPost::test_post_remove_tag
  • Expected: Tag removed from list
  • Last Verified: 2026-05-07

Value Objects

TC-UNIT-301: Title — Valid

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestTitle::test_valid_title
  • Expected: Title("Valid Title").value == "Valid Title"
  • Last Verified: 2026-05-07

TC-UNIT-302: Title — Too Short

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestTitle::test_title_too_short
  • Expected: Raises ValueError with message containing "at least"
  • Last Verified: 2026-05-07

TC-UNIT-303: Title — Too Long

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestTitle::test_title_too_long
  • Expected: Raises ValueError with message containing "at most"
  • Last Verified: 2026-05-07

TC-UNIT-304: Title — Empty / Whitespace

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestTitle::test_title_empty
  • Expected: Raises ValueError with message containing "empty"
  • Last Verified: 2026-05-07

TC-UNIT-305: Title — Non-String

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestTitle::test_title_not_string
  • Expected: Raises ValueError with message containing "string"
  • Last Verified: 2026-05-07

TC-UNIT-306: Content — Valid

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestContent::test_valid_content
  • Expected: Content created successfully
  • Last Verified: 2026-05-07

TC-UNIT-307: Content — Too Short

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestContent::test_content_too_short
  • Expected: Raises ValueError with "at least"
  • Last Verified: 2026-05-07

TC-UNIT-308: Content — Too Long

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestContent::test_content_too_long
  • Expected: Raises ValueError with "at most"
  • Last Verified: 2026-05-07

TC-UNIT-309: Content — Empty / Whitespace

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestContent::test_content_empty
  • Expected: Raises ValueError with "empty"
  • Last Verified: 2026-05-07

TC-UNIT-310: Slug — Valid

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_valid_slug
  • Expected: Slug("valid-slug").value == "valid-slug"
  • Last Verified: 2026-05-07

TC-UNIT-311: Slug — From Title

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_slug_from_title
  • Expected: Slug.from_title("Hello World Post") == "hello-world-post"
  • Last Verified: 2026-05-07

TC-UNIT-312: Slug — From Title with Special Characters

  • Type: Edge
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_slug_from_title_with_special_chars
  • Expected: Special chars stripped, words hyphenated
  • Last Verified: 2026-05-07

TC-UNIT-313: Slug — From Title with Only Special Characters

  • Type: Edge
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_slug_from_title_only_special_chars
  • Expected: Falls back to "post"
  • Last Verified: 2026-05-07

TC-UNIT-314: Slug — Invalid Characters (underscore)

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_slug_invalid_chars
  • Expected: Raises ValueError with "lowercase"
  • Last Verified: 2026-05-07

TC-UNIT-315: Slug — Uppercase Letters

  • Type: Negative
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_slug_uppercase
  • Expected: Raises ValueError with "lowercase"
  • Last Verified: 2026-05-07

TC-UNIT-316: Slug — Equality and Hash

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_value_objects.py::TestSlug::test_slug_equality
  • Expected: Equal slugs have equal values and hashes
  • Last Verified: 2026-05-07

Exceptions

TC-UNIT-401: DomainException — Base

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_exceptions.py::TestDomainExceptions::test_base_exception
  • Expected: Message stored and returned via str()
  • Last Verified: 2026-05-07

TC-UNIT-402: ValidationException

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_exceptions.py::TestDomainExceptions::test_validation_exception
  • Expected: Inherits DomainException, stores message
  • Last Verified: 2026-05-07

TC-UNIT-403: NotFoundException

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_exceptions.py::TestDomainExceptions::test_not_found_exception
  • Expected: Inherits DomainException, stores message
  • Last Verified: 2026-05-07

TC-UNIT-404: AlreadyExistsException

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_exceptions.py::TestDomainExceptions::test_already_exists_exception
  • Expected: Inherits DomainException, stores message
  • Last Verified: 2026-05-07

TC-UNIT-405: UnauthorizedException

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_exceptions.py::TestDomainExceptions::test_unauthorized_exception
  • Expected: Inherits DomainException, stores message
  • Last Verified: 2026-05-07

TC-UNIT-406: ForbiddenException

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_exceptions.py::TestDomainExceptions::test_forbidden_exception
  • Expected: Inherits DomainException, stores message
  • Last Verified: 2026-05-07

Coverage Summary

Component Cases Status
Post Entity 8 All core operations covered
Title VO 5 Validation rules fully covered
Content VO 4 Validation rules fully covered
Slug VO 7 Generation and validation covered
Domain Exceptions 6 All exception types covered

Gaps (Not Yet Covered)

  • TC-UNIT-209: Post Entity — updated_at does not change when update values are identical
  • TC-UNIT-210: Post Entity — attempt to publish already published post (idempotent behavior)
  • TC-UNIT-317: Slug — collision handling (unique constraint) at domain level
  • TC-UNIT-318: Content — exact boundary values (min length - 1, max length + 1)