수정본
This commit is contained in:
57
tests/insert_test_data.py
Normal file
57
tests/insert_test_data.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
테스트용 샘플 분석 데이터 생성 스크립트
|
||||
|
||||
실제 배치 대신 테스트용 데이터를 DB에 넣습니다.
|
||||
"""
|
||||
|
||||
import psycopg2
|
||||
from datetime import date
|
||||
|
||||
# DB 연결
|
||||
conn = psycopg2.connect(
|
||||
host="shium.net",
|
||||
port=55432,
|
||||
database="stock",
|
||||
user="stock_admin",
|
||||
password="stock_pass"
|
||||
)
|
||||
|
||||
cur = conn.cursor()
|
||||
|
||||
# 테스트 데이터
|
||||
test_data = [
|
||||
('005930', '삼성전자', 58100, -1.11, 20000000, 22.5, 61200, 65400, 68200, 14.5, 18.2, 72000, 58000, 64500, ['RSI 과매도', '스토캐스틱 과매도'], 3),
|
||||
('122630', 'KODEX 레버리지', 41140, -3.52, 40000000, 25.9, 43200, 45100, 47800, 21.9, 24.1, 49000, 39000, 44000, ['RSI 과매도', '스토캐스틱 과매도'], 3),
|
||||
('462330', 'KODEX 2차전지산업레버리지', 1690, -6.63, 50000000, 20.5, 1850, 1920, 2100, 29.4, 32.1, 2200, 1600, 1900, ['RSI 과매도', '볼린저밴드 하단'], 3),
|
||||
('000660', 'SK하이닉스', 179000, -2.15, 15000000, 28.3, 185000, 192000, 198000, 18.5, 22.3, 205000, 172000, 188500, ['RSI 과매도'], 2),
|
||||
('035720', '카카오', 42500, 1.43, 8000000, 52.1, 41800, 40200, 38500, 65.2, 68.1, 46000, 38000, 42000, ['RSI 중립-상승', '골든크로스'], 2),
|
||||
]
|
||||
|
||||
today = date.today()
|
||||
|
||||
print(f"🚀 Inserting test data for {today}...")
|
||||
|
||||
for stock in test_data:
|
||||
cur.execute("""
|
||||
INSERT INTO stock_analysis (
|
||||
stock_code, stock_name, analysis_date, current_price, change_rate, volume,
|
||||
rsi, ma5, ma20, ma60, stochastic_k, stochastic_d,
|
||||
bollinger_upper, bollinger_lower, bollinger_middle,
|
||||
signals, score
|
||||
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
ON CONFLICT DO NOTHING
|
||||
""", (
|
||||
stock[0], stock[1], today, stock[2], stock[3], stock[4],
|
||||
stock[5], stock[6], stock[7], stock[8], stock[9], stock[10],
|
||||
stock[11], stock[12], stock[13], stock[14], stock[15]
|
||||
))
|
||||
print(f" ✅ {stock[0]} ({stock[1]})")
|
||||
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
print(f"\n✅ Test data inserted successfully!")
|
||||
print(f"📊 Total: {len(test_data)} stocks")
|
||||
print(f"\nNow test the API:")
|
||||
print(f" GET http://localhost:8000/api/v1/recommendations/rule-based")
|
||||
160
tests/test_api_safety.py
Executable file
160
tests/test_api_safety.py
Executable file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
API 모니터링 테스트 스크립트
|
||||
|
||||
KIS API의 재시도 및 쓰로틀링 기능을 테스트합니다.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 프로젝트 루트 추가
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from app.services.korea_investment_api import KoreaInvestmentAPI
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def test_throttling():
|
||||
"""쓰로틀링 테스트: 빠른 연속 호출"""
|
||||
logger.info("=" * 60)
|
||||
logger.info("쓰로틀링 테스트 시작: 5개 종목 연속 조회")
|
||||
logger.info("=" * 60)
|
||||
|
||||
kis_api = KoreaInvestmentAPI()
|
||||
|
||||
test_stocks = [
|
||||
"005930", # 삼성전자
|
||||
"000660", # SK하이닉스
|
||||
"035420", # 네이버
|
||||
"051910", # LG화학
|
||||
"005380", # 현대차
|
||||
]
|
||||
|
||||
import time
|
||||
start = time.time()
|
||||
|
||||
for stock_code in test_stocks:
|
||||
logger.info(f"📊 Fetching {stock_code}...")
|
||||
try:
|
||||
result = await kis_api.get_current_price(stock_code)
|
||||
if result and result.get("output"):
|
||||
price = result["output"].get("stck_prpr", "N/A")
|
||||
logger.info(f"✅ {stock_code}: {price}원")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ {stock_code}: {e}")
|
||||
|
||||
elapsed = time.time() - start
|
||||
logger.info(f"⏱️ Total time: {elapsed:.2f}초")
|
||||
logger.info(f"📈 Expected: ~{len(test_stocks) * kis_api.min_request_interval:.1f}초 (쓰로틀링 적용)")
|
||||
|
||||
# 통계 출력
|
||||
stats = kis_api.get_monitor_stats()
|
||||
logger.info("=" * 60)
|
||||
logger.info("📡 API 통계:")
|
||||
logger.info(f" - 총 호출: {stats['total_calls']}건")
|
||||
logger.info(f" - 오류: {stats['total_errors']}건")
|
||||
logger.info(f" - 재시도: {stats['total_retries']}건")
|
||||
logger.info(f" - 오류율: {stats['error_rate']:.1%}")
|
||||
logger.info(f" - 평균 응답: {stats['avg_duration']:.2f}초")
|
||||
logger.info("=" * 60)
|
||||
|
||||
|
||||
async def test_retry():
|
||||
"""재시도 테스트: 잘못된 종목 코드로 테스트"""
|
||||
logger.info("=" * 60)
|
||||
logger.info("재시도 테스트 시작: 오류 케이스")
|
||||
logger.info("=" * 60)
|
||||
|
||||
kis_api = KoreaInvestmentAPI()
|
||||
|
||||
# 잘못된 종목 코드
|
||||
invalid_code = "000000"
|
||||
|
||||
try:
|
||||
logger.info(f"📊 Fetching invalid stock: {invalid_code}")
|
||||
result = await kis_api.get_current_price(invalid_code)
|
||||
logger.info(f"✅ Result: {result}")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Expected error: {e}")
|
||||
|
||||
# 통계 출력
|
||||
stats = kis_api.get_monitor_stats()
|
||||
logger.info("=" * 60)
|
||||
logger.info("📡 API 통계:")
|
||||
logger.info(f" - 총 호출: {stats['total_calls']}건")
|
||||
logger.info(f" - 오류: {stats['total_errors']}건")
|
||||
logger.info(f" - 재시도: {stats['total_retries']}건")
|
||||
logger.info(f" - 오류율: {stats['error_rate']:.1%}")
|
||||
logger.info("=" * 60)
|
||||
|
||||
|
||||
async def test_concurrent():
|
||||
"""동시 요청 테스트: 세마포어 확인"""
|
||||
logger.info("=" * 60)
|
||||
logger.info(f"동시 요청 테스트 시작 (최대 동시 요청: 2건)")
|
||||
logger.info("=" * 60)
|
||||
|
||||
kis_api = KoreaInvestmentAPI()
|
||||
|
||||
test_stocks = [
|
||||
"005930", # 삼성전자
|
||||
"000660", # SK하이닉스
|
||||
"035420", # 네이버
|
||||
"051910", # LG화학
|
||||
]
|
||||
|
||||
import time
|
||||
start = time.time()
|
||||
|
||||
# 병렬 실행
|
||||
tasks = [kis_api.get_current_price(code) for code in test_stocks]
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
elapsed = time.time() - start
|
||||
|
||||
success = sum(1 for r in results if not isinstance(r, Exception))
|
||||
logger.info(f"✅ 성공: {success}/{len(test_stocks)}")
|
||||
logger.info(f"⏱️ Total time: {elapsed:.2f}초")
|
||||
|
||||
# 통계 출력
|
||||
stats = kis_api.get_monitor_stats()
|
||||
logger.info("=" * 60)
|
||||
logger.info("📡 API 통계:")
|
||||
logger.info(f" - 총 호출: {stats['total_calls']}건")
|
||||
logger.info(f" - 오류: {stats['total_errors']}건")
|
||||
logger.info(f" - 재시도: {stats['total_retries']}건")
|
||||
logger.info(f" - 오류율: {stats['error_rate']:.1%}")
|
||||
logger.info(f" - 평균 응답: {stats['avg_duration']:.2f}초")
|
||||
logger.info("=" * 60)
|
||||
|
||||
|
||||
async def main():
|
||||
"""메인 테스트 실행"""
|
||||
logger.info("🚀 KIS API 안전 운영 테스트 시작")
|
||||
logger.info("")
|
||||
|
||||
# 1. 쓰로틀링 테스트
|
||||
await test_throttling()
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# 2. 재시도 테스트
|
||||
await test_retry()
|
||||
await asyncio.sleep(2)
|
||||
|
||||
# 3. 동시 요청 테스트
|
||||
await test_concurrent()
|
||||
|
||||
logger.info("")
|
||||
logger.info("✅ 모든 테스트 완료")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
69
tests/test_db_connection.py
Normal file
69
tests/test_db_connection.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DB 연결 테스트 스크립트
|
||||
실행: python test_db_connection.py
|
||||
"""
|
||||
import asyncio
|
||||
import asyncpg
|
||||
|
||||
|
||||
async def test_connection():
|
||||
print("🔵 Testing PostgreSQL connection...")
|
||||
print("Host: shium.net")
|
||||
print("Port: 55432")
|
||||
print("Database: stock")
|
||||
print("User: stock")
|
||||
print()
|
||||
|
||||
try:
|
||||
# 직접 asyncpg로 연결 시도
|
||||
conn = await asyncpg.connect(
|
||||
host='shium.net',
|
||||
port=55432,
|
||||
database='stock',
|
||||
user='stock',
|
||||
password='Tjduddn!!12',
|
||||
timeout=10,
|
||||
ssl=False,
|
||||
)
|
||||
|
||||
print("✅ Connection successful!")
|
||||
|
||||
# 간단한 쿼리 테스트
|
||||
version = await conn.fetchval('SELECT version()')
|
||||
print(f"PostgreSQL version: {version}")
|
||||
|
||||
# 테이블 목록 조회
|
||||
tables = await conn.fetch("""
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
""")
|
||||
|
||||
print(f"\nTables in database:")
|
||||
for table in tables:
|
||||
print(f" - {table['table_name']}")
|
||||
|
||||
await conn.close()
|
||||
print("\n✅ Connection test completed successfully!")
|
||||
|
||||
except asyncpg.PostgresConnectionError as e:
|
||||
print(f"❌ PostgreSQL connection error: {e}")
|
||||
except ConnectionRefusedError as e:
|
||||
print(f"❌ Connection refused: {e}")
|
||||
print("Possible causes:")
|
||||
print(" 1. PostgreSQL server is not running")
|
||||
print(" 2. Firewall blocking port 55432")
|
||||
print(" 3. PostgreSQL not configured to accept external connections")
|
||||
print(" 4. Need SSH tunnel or VPN to access the server")
|
||||
except TimeoutError as e:
|
||||
print(f"❌ Connection timeout: {e}")
|
||||
print("The server is not responding. Check network connectivity.")
|
||||
except Exception as e:
|
||||
print(f"❌ Unexpected error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_connection())
|
||||
Reference in New Issue
Block a user