Основные изменения: - Добавлены 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 — гайд по тестированию
8.6 KiB
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:
idis a valid UUIDtitle.value == "Test Title"content.valuematches inputslug.value == "test-title"(auto-generated)author_id == "user-123"published is Falsetags == ["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:
publishedtransitions fromFalsetoTrue - 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:
publishedtransitions fromTruetoFalse - 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_atchanged - 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_atchanged - 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_atchanged - 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
ValueErrorwith 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
ValueErrorwith 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
ValueErrorwith 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
ValueErrorwith 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
ValueErrorwith "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
ValueErrorwith "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
ValueErrorwith "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
ValueErrorwith "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
ValueErrorwith "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_atdoes 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)