142 lines
4.0 KiB
Python
142 lines
4.0 KiB
Python
"""
|
|
악성·반복 민원 제한 서비스 테스트.
|
|
"""
|
|
import pytest
|
|
from datetime import datetime, timedelta, timezone
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
from app.services.moderation import ModerationService, ModerationResult
|
|
from app.models.moderation import UserRestriction, RestrictionLevel
|
|
|
|
|
|
def make_db(restriction=None):
|
|
db = AsyncMock()
|
|
db.add = MagicMock()
|
|
db.commit = AsyncMock()
|
|
db.execute = AsyncMock()
|
|
scalar = MagicMock()
|
|
scalar.scalar_one_or_none = MagicMock(return_value=restriction)
|
|
db.execute.return_value = scalar
|
|
return db
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_normal_user_is_allowed():
|
|
"""제한 없는 사용자 → 허용."""
|
|
db = make_db(restriction=None)
|
|
service = ModerationService(db)
|
|
result = await service.check("t1", "user-1")
|
|
assert result.allowed is True
|
|
assert result.level == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_blocked_user_is_denied():
|
|
"""Level 5 (BLOCKED) → 차단."""
|
|
r = MagicMock()
|
|
r.level = RestrictionLevel.BLOCKED
|
|
r.expires_at = None
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
result = await service.check("t1", "user-block")
|
|
assert result.allowed is False
|
|
assert result.level == RestrictionLevel.BLOCKED
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_suspended_user_is_denied():
|
|
"""Level 4 (SUSPENDED) → 편집장 확인 전 차단."""
|
|
r = MagicMock()
|
|
r.level = RestrictionLevel.SUSPENDED
|
|
r.expires_at = datetime.now(timezone.utc) + timedelta(hours=1)
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
result = await service.check("t1", "user-sus")
|
|
assert result.allowed is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_warning_user_has_delay():
|
|
"""Level 2 (WARNING) → 허용 + 30초 지연."""
|
|
r = MagicMock()
|
|
r.level = RestrictionLevel.WARNING
|
|
r.expires_at = datetime.now(timezone.utc) + timedelta(hours=1)
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
result = await service.check("t1", "user-warn")
|
|
assert result.allowed is True
|
|
assert result.delay_seconds == 30
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_escalate_increases_level():
|
|
"""레벨 상승: NORMAL → 다음 레벨."""
|
|
r = MagicMock(spec=UserRestriction)
|
|
r.level = RestrictionLevel.NORMAL
|
|
r.expires_at = None
|
|
r.auto_applied = True
|
|
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
new_level = await service.escalate("t1", "user-1", "반복 민원")
|
|
|
|
assert new_level == RestrictionLevel.NORMAL + 1
|
|
db.commit.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_escalate_stops_at_suspended():
|
|
"""Level 4 이상 자동 상승 금지."""
|
|
r = MagicMock(spec=UserRestriction)
|
|
r.level = RestrictionLevel.SUSPENDED
|
|
r.expires_at = None
|
|
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
new_level = await service.escalate("t1", "user-1")
|
|
|
|
# Level 4에서 멈춤
|
|
assert new_level == RestrictionLevel.SUSPENDED
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_release_resets_level():
|
|
"""수동 해제 → level 0."""
|
|
r = MagicMock(spec=UserRestriction)
|
|
r.level = RestrictionLevel.SUSPENDED
|
|
r.expires_at = None
|
|
r.auto_applied = True
|
|
r.applied_by = None
|
|
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
await service.release("t1", "user-1", applied_by="editor-1")
|
|
|
|
assert r.level == RestrictionLevel.NORMAL
|
|
assert r.applied_by == "editor-1"
|
|
db.commit.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_check_expired_restriction_resets():
|
|
"""만료된 제한 → 자동 해제."""
|
|
r = MagicMock(spec=UserRestriction)
|
|
r.level = RestrictionLevel.WARNING # Level 2
|
|
r.expires_at = datetime.now(timezone.utc) - timedelta(hours=1) # 만료
|
|
r.auto_applied = True
|
|
r.applied_by = None
|
|
|
|
db = make_db(restriction=r)
|
|
|
|
service = ModerationService(db)
|
|
result = await service.check("t1", "user-expired")
|
|
|
|
assert result.allowed is True
|
|
assert result.level == 0
|