diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..c63c729
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+Пакет тестов для проекта Окнардия.
+
+Содержит все юнит-тесты и функциональные тесты для валидации:
+- Функций обработки текста (sanitize_slug, safe_html)
+- Функций SEO генерации
+- JSON парсинга и валидации
+"""
+
diff --git a/tests/test_safe_html.py b/tests/test_safe_html.py
new file mode 100644
index 0000000..7186146
--- /dev/null
+++ b/tests/test_safe_html.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Тест для функции safe_html_spec_symbols
+Демонстрирует улучшенную очистку HTML-разметки
+"""
+
+import os
+import sys
+import django
+
+# Добавляем путь к проекту (подъём на одну папку выше, т.к. тесты в папке tests/)
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oknardia.settings')
+django.setup()
+
+from oknardia.web.add_func import safe_html_spec_symbols
+
+
+def test_safe_html_spec_symbols():
+ """Набор тестов для функции safe_html_spec_symbols"""
+
+ test_cases = [
+ # (входная строка, ожидаемый результат, описание)
+ (
+ 'Текст с неразрывным пробелом',
+ 'Текст с неразрывным пробелом',
+ 'Замена на обычный пробел'
+ ),
+ (
+ 'Текст с « кавычками »',
+ 'Текст с « кавычками »',
+ 'Удаление span-тегов и замена кавычек'
+ ),
+ (
+ 'Текст после скрипта',
+ 'Текст после скрипта',
+ 'Удаление содержимого script-тега'
+ ),
+ (
+ 'Текст со стилем',
+ 'Текст со стилем',
+ 'Удаление содержимого style-тега'
+ ),
+ (
+ 'Цена: 100 № (№ = №)',
+ 'Цена: 100 № ( № = №)',
+ 'Замена числовых мнемоник (№) на Unicode'
+ ),
+ (
+ 'Символы: — … © ®',
+ 'Символы: — … © ®',
+ 'Замена именованных мнемоник'
+ ),
+ (
+ '
Новая
строка',
+ 'Новая строка',
+ 'Удаление br-тегов'
+ ),
+ (
+ '
Текст
без разрывов',
+ 'Текст без разрывов',
+ 'Удаление nobr-тегов'
+ ),
+ (
+ 'Множество пробелов\n\n\tи табуляций',
+ 'Множество пробелов и табуляций',
+ 'Очистка множественных пробелов и переносов'
+ ),
+ (
+ 'function foo() { return 42; } остаток',
+ 'остаток',
+ 'Удаление содержимого code-тега'
+ ),
+ (
+ 'preformatted\n text
после',
+ 'после',
+ 'Удаление содержимого pre-тега'
+ ),
+ (
+ ' Текст с пробелами в начале и конце ',
+ 'Текст с пробелами в начале и конце',
+ 'Trim пробелов в начале/конце'
+ ),
+ (
+ 'Число A (A) и B (B)',
+ 'Число A (A) и B (B)',
+ 'Замена десятичных и шестнадцатеричных мнемоник'
+ ),
+ ]
+
+ print("=" * 80)
+ print("ТЕСТЫ ДЛЯ safe_html_spec_symbols")
+ print("=" * 80)
+
+ passed = 0
+ failed = 0
+
+ for idx, (input_str, expected, description) in enumerate(test_cases, 1):
+ result = safe_html_spec_symbols(input_str)
+ is_passed = result == expected
+
+ status = "✓ PASS" if is_passed else "✗ FAIL"
+ print(f"\n{idx}. {status}: {description}")
+ print(f" Вход: {repr(input_str[:60])}")
+ print(f" Ожидаемо: {repr(expected)}")
+ print(f" Получено: {repr(result)}")
+
+ if is_passed:
+ passed += 1
+ else:
+ failed += 1
+
+ print("\n" + "=" * 80)
+ print(f"Результаты: {passed} пройдено, {failed} не пройдено из {len(test_cases)}")
+ print("=" * 80)
+
+ return failed == 0
+
+
+if __name__ == '__main__':
+ success = test_safe_html_spec_symbols()
+ sys.exit(0 if success else 1)
+
diff --git a/tests/test_safe_html_standalone.py b/tests/test_safe_html_standalone.py
new file mode 100644
index 0000000..000b61c
--- /dev/null
+++ b/tests/test_safe_html_standalone.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Тесты для функции safe_html_spec_symbols() из oknardia/web/add_func.py
+
+Проверяет:
+1. Удаление содержимого исключённых тегов (script, style, code, kbd, pre, var, samp)
+2. Удаление обычных HTML-тегов
+3. Замену HTML-мнемоник на Unicode (именованные, десятичные, шестнадцатеричные)
+4. Очистку лишних пробелов
+"""
+
+import sys
+import os
+
+# Добавим путь к проекту для импорта (подъём на одну папку выше, т.к. тесты в папке tests/)
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'oknardia'))
+
+from web.add_func import safe_html_spec_symbols
+
+
+def test_remove_script_tags():
+ """Тест 1: Удаление содержимого тегов после'
+ result = safe_html_spec_symbols(html)
+ assert 'alert' not in result, f"Script-содержимое не удалено: {result}"
+ assert 'Текст' in result and 'после' in result, f"Обычный текст потеряется: {result}"
+ print("✓ Тест 1 (удаление и после'
+ result = safe_html_spec_symbols(html)
+ assert 'xss' not in result and 'script' not in result, f"SVG содержимое не удалено: {result}"
+ assert 'Текст' in result and 'после' in result, f"Обычный текст потеряется: {result}"
+ print("✓ Тест 3c (удаление