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 app.core.config import Settings
  8. from app.core.passwords import PasswordService
  9. from app.domains.catalog.models import Plan, Product, ProductVersion
  10. from app.domains.catalog.repository import InMemoryCatalogRepository
  11. from app.domains.identity.models import AdminUser, H5User
  12. from app.infrastructure.mysql.core_models import CoreBase, PlanRecord
  13. from app.infrastructure.mysql.repositories import (
  14. SqlAlchemyCatalogRepository,
  15. SqlAlchemyIdentityRepository,
  16. )
  17. from app.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"] == "系统管理员"