test_sqlalchemy_repositories.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. from dataclasses import replace
  2. from datetime import UTC, datetime
  3. from fastapi.testclient import TestClient
  4. from sqlalchemy import create_engine, func, select
  5. from sqlalchemy.orm import Session, sessionmaker
  6. from sqlalchemy.pool import StaticPool
  7. from zbt.core.config import Settings
  8. from zbt.core.passwords import PasswordService
  9. from zbt.domains.catalog.models import Plan, Product, ProductVersion
  10. from zbt.domains.catalog.repository import InMemoryCatalogRepository
  11. from zbt.domains.identity.models import AdminUser, H5User
  12. from zbt.infrastructure.mysql.core_models import CoreBase, PlanRecord
  13. from zbt.infrastructure.mysql.repositories import (
  14. SqlAlchemyCatalogRepository,
  15. SqlAlchemyIdentityRepository,
  16. )
  17. from zbt.main import create_app
  18. def test_sqlalchemy_repositories_round_trip_identity_and_catalog() -> None:
  19. engine = create_engine("sqlite+pysqlite:///:memory:")
  20. CoreBase.metadata.create_all(engine)
  21. session_factory = sessionmaker(engine, class_=Session, expire_on_commit=False)
  22. identities = SqlAlchemyIdentityRepository(session_factory)
  23. catalog = SqlAlchemyCatalogRepository(session_factory)
  24. identities.save_h5_user(
  25. H5User(
  26. id="01USER00000000000000000001",
  27. mobile="18800000001",
  28. mobile_masked="188****0001",
  29. display_name="测试用户甲",
  30. status="ACTIVE",
  31. created_at=datetime(2026, 7, 25, tzinfo=UTC),
  32. )
  33. )
  34. catalog.save_product(
  35. Product(
  36. id="01PRODUCT00000000000000001",
  37. product_code="MED-BASIC",
  38. name="蓉惠基础医疗险",
  39. category="MEDICAL",
  40. summary="基础医疗保障",
  41. status="ACTIVE",
  42. )
  43. )
  44. version = ProductVersion(
  45. id="01VERSION00000000000000002",
  46. product_id="01PRODUCT00000000000000001",
  47. version_no="2.0.0",
  48. status="PUBLISHED",
  49. effective_from=datetime(2026, 1, 1, tzinfo=UTC),
  50. effective_to=None,
  51. plans=(Plan(id="01PLAN0000000000000000002", code="BASIC", name="基础计划"),),
  52. )
  53. catalog.save_version(version)
  54. catalog.save_version(version)
  55. assert identities.get_h5_user_by_mobile("18800000001").display_name == "测试用户甲"
  56. assert catalog.list_products()[0].product_code == "MED-BASIC"
  57. loaded_version = catalog.list_versions("01PRODUCT00000000000000001")[0]
  58. assert loaded_version.version_no == "2.0.0"
  59. assert loaded_version.is_available_at(datetime(2026, 7, 25, tzinfo=UTC))
  60. with session_factory() as session:
  61. assert session.scalar(select(func.count()).select_from(PlanRecord)) == 1
  62. def test_admin_session_loaded_from_sql_repository_remains_active() -> None:
  63. engine = create_engine(
  64. "sqlite+pysqlite:///:memory:",
  65. connect_args={"check_same_thread": False},
  66. poolclass=StaticPool,
  67. )
  68. CoreBase.metadata.create_all(engine)
  69. session_factory = sessionmaker(engine, class_=Session, expire_on_commit=False)
  70. identities = SqlAlchemyIdentityRepository(session_factory)
  71. admin_user = AdminUser(
  72. id="01ADMIN0000000000000000001",
  73. username="admin",
  74. password_hash=PasswordService().hash("旧密码仅用于覆盖测试"),
  75. display_name="系统管理员",
  76. status="ACTIVE",
  77. roles=("SUPER_ADMIN",),
  78. permissions=("*",),
  79. data_scope="ALL",
  80. )
  81. identities.save_admin_user(admin_user)
  82. identities.save_admin_user(
  83. replace(
  84. admin_user,
  85. password_hash=PasswordService().hash("zaq1XSW@"),
  86. )
  87. )
  88. settings = Settings(
  89. app_env="test",
  90. jwt_access_secret="a" * 32,
  91. jwt_refresh_secret="b" * 32,
  92. field_encryption_key="c" * 32,
  93. )
  94. application = create_app(
  95. settings=settings,
  96. identity_repository=identities,
  97. catalog_repository=InMemoryCatalogRepository(),
  98. )
  99. with TestClient(application) as client:
  100. login = client.post(
  101. "/api/v1/admin/auth/login",
  102. json={"username": "admin", "password": "zaq1XSW@"},
  103. )
  104. token = login.json()["data"]["tokens"]["access_token"]
  105. profile = client.get(
  106. "/api/v1/admin/me",
  107. headers={"Authorization": f"Bearer {token}"},
  108. )
  109. assert profile.status_code == 200
  110. assert profile.json()["data"]["display_name"] == "系统管理员"