feat(lotto-lab): purchase_manager — 구매 결과 자동 체크 + 전략 성과 집계

- backend/app/purchase_manager.py 신규 생성
  - check_purchases_for_draw(): 회차별 미채점 구매 건 자동 채점
  - checker._calc_rank 재사용, RANK_PRIZE 상수 정의
  - 채점 후 strategy_performance 자동 upsert (전략별 집계)
- backend/tests/test_purchase_manager.py에 통합 테스트 2건 추가
  - test_check_purchases_for_draw: 1등/낙첨 결과 검증
  - test_check_purchases_updates_strategy_performance: 성과 테이블 갱신 검증

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 21:12:43 +09:00
parent 4c6e96d59c
commit 706ca410ca
2 changed files with 201 additions and 0 deletions

View File

@@ -1,6 +1,8 @@
# backend/tests/test_purchase_manager.py
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "app"))
# Also insert the backend root so that "backend.app" package is importable
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
import sqlite3
import pytest
@@ -196,3 +198,112 @@ def test_update_strategy_weight():
assert combined_after["total_sets"] == 100
assert combined_after["total_hits_3plus"] == 20
mem.close()
# ── purchase_manager 테스트 ───────────────────────────────────────────────────
def _import_purchase_manager_with_mem(mem_conn):
"""purchase_manager를 메모리 DB에 연결된 상태로 임포트."""
import db
import importlib
# backend.app 패키지로 로드해 상대 임포트가 동작하게 함
import backend.app.purchase_manager as pm
return pm
def test_check_purchases_for_draw():
"""특정 회차 구매 건들의 결과 체크"""
import db
import backend.app.purchase_manager as pm
mem = _make_mem_conn()
with patch("db._conn", return_value=mem):
db.init_db()
# 당첨번호 삽입: 1125회 [3,12,23,34,38,45] bonus=7
mem.execute(
"""INSERT INTO draws (drw_no, drw_date, n1, n2, n3, n4, n5, n6, bonus)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(1125, "2024-12-01", 3, 12, 23, 34, 38, 45, 7),
)
mem.commit()
# 구매 등록: 1등 번호 세트 + 낙첨 세트
purchase = db.add_purchase(
draw_no=1125,
amount=2000,
sets=2,
numbers=[[3, 12, 23, 34, 38, 45], [1, 2, 3, 4, 5, 6]],
is_real=False,
source_strategy="simulation",
)
with patch("db._conn", return_value=mem), \
patch("backend.app.purchase_manager.get_draw", side_effect=lambda drw: db.get_draw(drw)), \
patch("backend.app.purchase_manager.get_purchases", side_effect=lambda **kw: db.get_purchases(**kw)), \
patch("backend.app.purchase_manager.update_purchase_results", side_effect=lambda *a, **kw: db.update_purchase_results(*a, **kw)), \
patch("backend.app.purchase_manager.upsert_strategy_performance", side_effect=lambda **kw: db.upsert_strategy_performance(**kw)):
count = pm.check_purchases_for_draw(1125)
assert count == 1
# 결과 확인
with patch("db._conn", return_value=mem):
checked = db.get_purchases(draw_no=1125, checked=True)
assert len(checked) == 1
results = checked[0]["results"]
assert results is not None
assert len(results) == 2
# 첫 번째 세트: 6개 일치 → 1등
assert results[0]["rank"] == 1
assert results[0]["correct"] == 6
# 두 번째 세트: 3 하나만 일치 → 낙첨(correct=1)
assert results[1]["rank"] == 0
assert results[1]["correct"] == 1
mem.close()
def test_check_purchases_updates_strategy_performance():
"""결과 체크 후 strategy_performance가 갱신되는지 검증"""
import db
import backend.app.purchase_manager as pm
mem = _make_mem_conn()
with patch("db._conn", return_value=mem):
db.init_db()
# 당첨번호 삽입: 1126회
mem.execute(
"""INSERT INTO draws (drw_no, drw_date, n1, n2, n3, n4, n5, n6, bonus)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(1126, "2024-12-08", 1, 2, 3, 4, 5, 6, 7),
)
mem.commit()
db.add_purchase(
draw_no=1126,
amount=5000,
sets=5,
numbers=[[1, 2, 3, 4, 5, 6], [10, 20, 30, 40, 41, 42]],
is_real=False,
source_strategy="simulation",
)
with patch("db._conn", return_value=mem), \
patch("backend.app.purchase_manager.get_draw", side_effect=lambda drw: db.get_draw(drw)), \
patch("backend.app.purchase_manager.get_purchases", side_effect=lambda **kw: db.get_purchases(**kw)), \
patch("backend.app.purchase_manager.update_purchase_results", side_effect=lambda *a, **kw: db.update_purchase_results(*a, **kw)), \
patch("backend.app.purchase_manager.upsert_strategy_performance", side_effect=lambda **kw: db.upsert_strategy_performance(**kw)):
count = pm.check_purchases_for_draw(1126)
assert count == 1
with patch("db._conn", return_value=mem):
perf = db.get_strategy_performance(strategy="simulation")
assert len(perf) >= 1
entry = next((p for p in perf if p["draw_no"] == 1126), None)
assert entry is not None, "draw_no=1126 에 대한 strategy_performance 없음"
assert entry["strategy"] == "simulation"
assert entry["sets_count"] == 2 # 2개 세트
mem.close()