test_repository_db.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. """SQLAlchemy repository tests against an in-memory SQLite database.
  2. Verifies day routing (latest dt + specific dt + missing) and rolling-row column
  3. mapping against a real engine, with no Postgres needed.
  4. """
  5. from __future__ import annotations
  6. import pytest
  7. from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
  8. from app.db.models import (
  9. AdsTrdGroupFunnelDaily,
  10. AdsTrdGroupFunnelRolling,
  11. Base,
  12. )
  13. from app.schemas import DataStatus, Period
  14. from app.services.funnel import run_funnel_query
  15. from app.services.repository import SqlAlchemyFunnelRepository
  16. def _daily_row(dt: str, top: int) -> AdsTrdGroupFunnelDaily:
  17. return AdsTrdGroupFunnelDaily(
  18. dt=dt,
  19. uv_start=top,
  20. uv_show=int(top * 0.8),
  21. uv_detail=int(top * 0.5),
  22. uv_order=int(top * 0.2),
  23. uv_paid=int(top * 0.16),
  24. )
  25. def _rolling_row(dt: str, top7: int, top30: int) -> AdsTrdGroupFunnelRolling:
  26. return AdsTrdGroupFunnelRolling(
  27. dt=dt,
  28. uv_start_7d=top7,
  29. uv_show_7d=int(top7 * 0.8),
  30. uv_detail_7d=int(top7 * 0.5),
  31. uv_order_7d=int(top7 * 0.2),
  32. uv_paid_7d=int(top7 * 0.16),
  33. uv_start_30d=top30,
  34. uv_show_30d=int(top30 * 0.8),
  35. uv_detail_30d=int(top30 * 0.5),
  36. uv_order_30d=int(top30 * 0.2),
  37. uv_paid_30d=int(top30 * 0.16),
  38. )
  39. @pytest.fixture
  40. async def sqlite_sessionmaker():
  41. engine = create_async_engine("sqlite+aiosqlite:///:memory:")
  42. async with engine.begin() as conn:
  43. await conn.run_sync(Base.metadata.create_all)
  44. maker = async_sessionmaker(engine, expire_on_commit=False)
  45. yield maker
  46. await engine.dispose()
  47. async def test_empty_daily_returns_none(sqlite_sessionmaker) -> None:
  48. async with sqlite_sessionmaker() as session:
  49. repo = SqlAlchemyFunnelRepository(session)
  50. assert await repo.fetch_daily(None) is None
  51. assert await repo.fetch_daily("20260623") is None
  52. async def test_empty_rolling_returns_none(sqlite_sessionmaker) -> None:
  53. async with sqlite_sessionmaker() as session:
  54. repo = SqlAlchemyFunnelRepository(session)
  55. assert await repo.fetch_rolling() is None
  56. async def test_daily_latest_dt_is_selected(sqlite_sessionmaker) -> None:
  57. async with sqlite_sessionmaker() as session:
  58. session.add_all(
  59. [
  60. _daily_row("20260621", 100),
  61. _daily_row("20260623", 300),
  62. _daily_row("20260622", 200),
  63. ]
  64. )
  65. await session.commit()
  66. async with sqlite_sessionmaker() as session:
  67. repo = SqlAlchemyFunnelRepository(session)
  68. snapshot = await repo.fetch_daily(None)
  69. assert snapshot is not None
  70. assert snapshot.dt == "20260623"
  71. assert snapshot.values["uv_start"] == 300
  72. async def test_daily_specific_dt(sqlite_sessionmaker) -> None:
  73. async with sqlite_sessionmaker() as session:
  74. session.add_all([_daily_row("20260621", 100), _daily_row("20260623", 300)])
  75. await session.commit()
  76. async with sqlite_sessionmaker() as session:
  77. repo = SqlAlchemyFunnelRepository(session)
  78. snapshot = await repo.fetch_daily("20260621")
  79. assert snapshot is not None
  80. assert snapshot.dt == "20260621"
  81. assert snapshot.values["uv_start"] == 100
  82. async def test_daily_specific_dt_missing(sqlite_sessionmaker) -> None:
  83. async with sqlite_sessionmaker() as session:
  84. session.add(_daily_row("20260623", 300))
  85. await session.commit()
  86. async with sqlite_sessionmaker() as session:
  87. repo = SqlAlchemyFunnelRepository(session)
  88. assert await repo.fetch_daily("20200101") is None
  89. async def test_rolling_row_columns(sqlite_sessionmaker) -> None:
  90. async with sqlite_sessionmaker() as session:
  91. session.add(_rolling_row("20260623", top7=700, top30=3000))
  92. await session.commit()
  93. async with sqlite_sessionmaker() as session:
  94. repo = SqlAlchemyFunnelRepository(session)
  95. resp7 = await run_funnel_query(Period.last_7d, repo)
  96. assert resp7.data_status == DataStatus.ready
  97. assert resp7.snapshot_dt == "20260623"
  98. assert resp7.results[0].uv == 700
  99. resp30 = await run_funnel_query(Period.last_30d, repo)
  100. assert resp30.results[0].uv == 3000