test: migrate backend suites to pytest with module coverage gate

Split backend tests into unit and integration suites, add isolated fixtures and coverage enforcement, and update CI and test documentation. Cover current authentication, content, notification, payment, media, and admin flows; fix behaviors revealed by the new tests. Validate both suites and the per-module gate against local PostgreSQL.
This commit is contained in:
2026-09-26 16:50:29 +03:30
parent 3ec28638d1
commit ff99130938
76 changed files with 3271 additions and 1357 deletions

View File

@@ -184,7 +184,7 @@ class Command(BaseCommand):
def _seed_writers(self, editor_group: Group, password: str):
writers = []
for index, spec in enumerate(WRITERS, start=1):
user, created = User.objects.get_or_create(
user, created = User.all_objects.get_or_create(
username=spec["username"],
defaults={
"first_name": spec["first_name"],
@@ -201,6 +201,8 @@ class Command(BaseCommand):
user.bio = spec["bio"]
user.is_active = True
user.is_mobile_verified = True
user.is_deleted = False
user.deleted_at = None
if created:
user.set_password(password)
set_image_field(
@@ -218,19 +220,24 @@ class Command(BaseCommand):
return writers
def _seed_categories(self):
root, _ = Category.objects.get_or_create(
root, _ = Category.all_objects.get_or_create(
slug="mock-بلاگ-انجمن",
defaults={"name": "بلاگ انجمن", "description": "دسته اصلی محتوای تستی بلاگ"},
)
root.is_deleted = False
root.deleted_at = None
root.save(update_fields=["is_deleted", "deleted_at"])
names = ["برنامه‌نویسی", "علوم کامپیوتر", "توسعه نرم‌افزار", "ابزارها", "هوش مصنوعی"]
categories = {"بلاگ انجمن": root}
for name in names:
category, _ = Category.objects.get_or_create(
category, _ = Category.all_objects.get_or_create(
slug=f"mock-{name}",
defaults={"name": name, "parent": root, "description": f"مطالب تستی درباره {name}"},
)
category.name = name
category.parent = root
category.is_deleted = False
category.deleted_at = None
category.save()
categories[name] = category
return categories
@@ -238,8 +245,10 @@ class Command(BaseCommand):
def _seed_tags(self):
tags = {}
for name in TAG_NAMES:
tag, _ = Tag.objects.get_or_create(slug=f"mock-{name}", defaults={"name": name})
tag, _ = Tag.all_objects.get_or_create(slug=f"mock-{name}", defaults={"name": name})
tag.name = name
tag.is_deleted = False
tag.deleted_at = None
tag.save()
tags[name] = tag
return tags
@@ -247,7 +256,7 @@ class Command(BaseCommand):
def _seed_banners(self):
colors = [(9, 80, 90), (120, 64, 24), (38, 70, 83)]
for index in range(1, 4):
banner, _ = BlogBanner.objects.get_or_create(
banner, _ = BlogBanner.all_objects.get_or_create(
title=f"Mock Blog Banner {index}",
defaults={
"url": f"https://east-guilan-ce.ir/blog?mock-banner={index}",
@@ -260,6 +269,8 @@ class Command(BaseCommand):
banner.alt_text = f"بنر تستی بلاگ {index}"
banner.sort_order = index
banner.is_active = True
banner.is_deleted = False
banner.deleted_at = None
set_image_field(
banner,
"image",
@@ -298,6 +309,8 @@ class Command(BaseCommand):
post.content = make_markdown(spec["title"])
post.excerpt = f"خلاصه تستی برای نوشته «{spec['title']}» که برای بررسی کارت‌ها و سئوی بلاگ استفاده می‌شود."
post.status = Post.StatusChoices.PUBLISHED
post.is_deleted = False
post.deleted_at = None
post.category = categories[spec["category"]]
post.is_featured = index <= 2
post.published_at = post.published_at or timezone.now() - timezone.timedelta(days=index * 3)