f55d0d6f0e
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 33s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m5s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 59s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m0s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m6s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m58s
CI/CD Pipeline / Unit Tests (push) Failing after 4m39s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m20s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 2m51s
CI/CD Pipeline / Build Staging API Image (push) Successful in 15m48s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m45s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 15s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m56s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m6s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
167 lines
5.1 KiB
Python
Executable File
167 lines
5.1 KiB
Python
Executable File
"""领域层异常类单元测试."""
|
|
|
|
import pytest
|
|
|
|
from packages.domain.exceptions import (
|
|
DomainError,
|
|
NotFoundError,
|
|
QuotaExceededError,
|
|
ValidationError,
|
|
)
|
|
|
|
|
|
class TestDomainError:
|
|
"""领域异常基类测试."""
|
|
|
|
def test_is_exception(self):
|
|
"""DomainError 继承自 Exception."""
|
|
err = DomainError("test")
|
|
assert isinstance(err, Exception)
|
|
|
|
def test_message(self):
|
|
"""可以设置错误消息."""
|
|
err = DomainError("something wrong")
|
|
assert str(err) == "something wrong"
|
|
|
|
def test_empty_message(self):
|
|
"""支持空消息."""
|
|
err = DomainError()
|
|
assert str(err) == ""
|
|
|
|
def test_can_be_raised(self):
|
|
"""可以被 raise 和 catch."""
|
|
with pytest.raises(DomainError) as exc_info:
|
|
raise DomainError("oops")
|
|
assert str(exc_info.value) == "oops"
|
|
|
|
|
|
class TestNotFoundError:
|
|
"""资源不存在异常测试."""
|
|
|
|
def test_inherits_domain_error(self):
|
|
"""NotFoundError 继承自 DomainError."""
|
|
err = NotFoundError("user not found")
|
|
assert isinstance(err, DomainError)
|
|
assert isinstance(err, Exception)
|
|
|
|
def test_message(self):
|
|
"""错误消息正确."""
|
|
err = NotFoundError("project 123 not found")
|
|
assert str(err) == "project 123 not found"
|
|
assert "123" in str(err)
|
|
|
|
def test_can_catch_as_domain_error(self):
|
|
"""可以用 DomainError 捕获."""
|
|
with pytest.raises(DomainError):
|
|
raise NotFoundError("not found")
|
|
|
|
|
|
class TestValidationError:
|
|
"""校验失败异常测试."""
|
|
|
|
def test_inherits_domain_error(self):
|
|
"""ValidationError 继承自 DomainError."""
|
|
err = ValidationError("invalid input")
|
|
assert isinstance(err, DomainError)
|
|
|
|
def test_message(self):
|
|
"""错误消息正确."""
|
|
msg = "name must not be empty"
|
|
err = ValidationError(msg)
|
|
assert str(err) == msg
|
|
|
|
def test_not_not_found(self):
|
|
"""ValidationError 不是 NotFoundError."""
|
|
err = ValidationError("bad")
|
|
assert not isinstance(err, NotFoundError)
|
|
|
|
|
|
class TestQuotaExceededError:
|
|
"""配额超限异常测试."""
|
|
|
|
def test_inherits_domain_error(self):
|
|
"""QuotaExceededError 继承自 DomainError."""
|
|
err = QuotaExceededError("storage", 100.0, 150.0)
|
|
assert isinstance(err, DomainError)
|
|
|
|
def test_dimension_attribute(self):
|
|
"""保存 dimension 属性."""
|
|
err = QuotaExceededError("storage", 100.0, 150.0)
|
|
assert err.dimension == "storage"
|
|
|
|
def test_limit_attribute(self):
|
|
"""保存 limit 属性."""
|
|
err = QuotaExceededError("storage", 100.0, 150.0)
|
|
assert err.limit == 100.0
|
|
|
|
def test_used_attribute(self):
|
|
"""保存 used 属性."""
|
|
err = QuotaExceededError("storage", 100.0, 150.0)
|
|
assert err.used == 150.0
|
|
|
|
def test_message_format(self):
|
|
"""错误消息格式正确."""
|
|
err = QuotaExceededError("credits", 50.0, 75.0)
|
|
msg = str(err)
|
|
assert "credits" in msg
|
|
assert "50" in msg
|
|
assert "75" in msg
|
|
assert "Quota exceeded" in msg
|
|
|
|
def test_zero_limit(self):
|
|
"""limit 为 0 的情况."""
|
|
err = QuotaExceededError("test", 0.0, 1.0)
|
|
assert err.limit == 0.0
|
|
assert err.used == 1.0
|
|
assert "0" in str(err)
|
|
|
|
def test_equal_limit_and_used(self):
|
|
"""used 刚好等于 limit(边界情况)."""
|
|
err = QuotaExceededError("test", 100.0, 100.0)
|
|
assert err.used == 100.0
|
|
assert err.limit == 100.0
|
|
|
|
def test_integer_values(self):
|
|
"""整数值也能正常工作."""
|
|
err = QuotaExceededError("count", 10, 20)
|
|
assert err.dimension == "count"
|
|
assert err.limit == 10
|
|
assert err.used == 20
|
|
|
|
def test_can_catch_as_domain_error(self):
|
|
"""可以用 DomainError 捕获."""
|
|
with pytest.raises(DomainError):
|
|
raise QuotaExceededError("x", 1.0, 2.0)
|
|
|
|
|
|
class TestExceptionHierarchy:
|
|
"""异常继承关系验证."""
|
|
|
|
def test_all_are_domain_errors(self):
|
|
"""所有领域异常都是 DomainError."""
|
|
errors = [
|
|
NotFoundError("test"),
|
|
ValidationError("test"),
|
|
QuotaExceededError("test", 1, 2),
|
|
]
|
|
for err in errors:
|
|
assert isinstance(err, DomainError)
|
|
|
|
def test_all_are_exceptions(self):
|
|
"""所有领域异常都是 Exception."""
|
|
errors = [
|
|
DomainError("test"),
|
|
NotFoundError("test"),
|
|
ValidationError("test"),
|
|
QuotaExceededError("test", 1, 2),
|
|
]
|
|
for err in errors:
|
|
assert isinstance(err, Exception)
|
|
|
|
def test_not_found_is_not_validation(self):
|
|
"""不同异常类型不能互相混淆."""
|
|
assert not isinstance(NotFoundError("x"), ValidationError)
|
|
assert not isinstance(ValidationError("x"), NotFoundError)
|
|
assert not isinstance(QuotaExceededError("x", 1, 2), NotFoundError)
|
|
assert not isinstance(QuotaExceededError("x", 1, 2), ValidationError)
|