Skip to content

Commit

Permalink
Add basic mocktest for login / logout methods (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanking13 authored Sep 23, 2023
1 parent 84933c4 commit bca6fb2
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 28 deletions.
1 change: 0 additions & 1 deletion .github/workflows/test_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Test PR

on:
pull_request_target:
pull_request:

jobs:
authorize:
Expand Down
13 changes: 13 additions & 0 deletions SRT/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,16 @@
}

WINDOW_SEAT = {None: "000", True: "012", False: "013"}

SRT_MOBILE = "https://app.srail.or.kr:443"
API_ENDPOINTS = {
"main": f"{SRT_MOBILE}/main/main.do",
"login": f"{SRT_MOBILE}/apb/selectListApb01080_n.do",
"logout": f"{SRT_MOBILE}/login/loginOut.do",
"search_schedule": f"{SRT_MOBILE}/ara/selectListAra10007_n.do",
"reserve": f"{SRT_MOBILE}/arc/selectListArc05013_n.do",
"tickets": f"{SRT_MOBILE}/atc/selectListAtc14016_n.do",
"ticket_info": f"{SRT_MOBILE}/ard/selectListArd02017_n.do?",
"cancel": f"{SRT_MOBILE}/ard/selectListArd02045_n.do",
"standby_option": f"{SRT_MOBILE}/ata/selectListAta01135_n.do",
}
39 changes: 12 additions & 27 deletions SRT/srt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import requests # type: ignore[import]

from . import constants
from .constants import STATION_CODE
from .errors import SRTError, SRTLoginError, SRTNotLoggedInError, SRTResponseError
from .passenger import Adult, Passenger
Expand All @@ -14,24 +15,6 @@
EMAIL_REGEX = re.compile(r"[^@]+@[^@]+\.[^@]+")
PHONE_NUMBER_REGEX = re.compile(r"(\d{3})-(\d{3,4})-(\d{4})")

SCHEME = "https"
SRT_HOST = "app.srail.or.kr"
SRT_PORT = "443"

SRT_MOBILE = "{scheme}://{host}:{port}".format(
scheme=SCHEME, host=SRT_HOST, port=SRT_PORT
)

SRT_MAIN = f"{SRT_MOBILE}/main/main.do"
SRT_LOGIN = f"{SRT_MOBILE}/apb/selectListApb01080_n.do"
SRT_LOGOUT = f"{SRT_MOBILE}/login/loginOut.do"
SRT_SEARCH_SCHEDULE = f"{SRT_MOBILE}/ara/selectListAra10007_n.do"
SRT_RESERVE = f"{SRT_MOBILE}/arc/selectListArc05013_n.do"
SRT_TICKETS = f"{SRT_MOBILE}/atc/selectListAtc14016_n.do"
SRT_TICKET_INFO = f"{SRT_MOBILE}/ard/selectListArd02017_n.do?"
SRT_CANCEL = f"{SRT_MOBILE}/ard/selectListArd02045_n.do"
SRT_STANDBY_OPTION = f"{SRT_MOBILE}/ata/selectListAta01135_n.do"

DEFAULT_HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Linux; Android 5.1.1; LGM-V300K Build/N2G47H) AppleWebKit/537.36 "
Expand Down Expand Up @@ -119,14 +102,14 @@ def login(self, srt_id: str | None = None, srt_pw: str | None = None):
else:
login_type = LOGIN_TYPES["MEMBERSHIP_ID"]

url = SRT_LOGIN
url = constants.API_ENDPOINTS["login"]
data: dict[str, str] = {
"auto": "Y",
"check": "Y",
"page": "menu",
"deviceKey": "-",
"customerYn": "",
"login_referer": SRT_MAIN,
"login_referer": constants.API_ENDPOINTS["main"],
"srchDvCd": login_type,
"srchDvNm": srt_id,
"hmpgPwdCphd": srt_pw,
Expand All @@ -152,13 +135,15 @@ def logout(self) -> bool:
if not self.is_login:
return True

url = SRT_LOGOUT
url = constants.API_ENDPOINTS["logout"]

r = self._session.post(url=url)
self._log(r.text)

if not r.ok:
raise SRTResponseError(r.text)

self.is_login = False
return True

def search_train(
Expand Down Expand Up @@ -197,7 +182,7 @@ def search_train(
if time is None:
time = "000000"

url = SRT_SEARCH_SCHEDULE
url = constants.API_ENDPOINTS["search_schedule"]
data = {
# course (1: 직통, 2: 환승, 3: 왕복)
# TODO: support 환승, 왕복
Expand Down Expand Up @@ -368,7 +353,7 @@ def _reserve(
else:
is_special_seat = False

url = SRT_RESERVE
url = constants.API_ENDPOINTS["reserve"]
data = {
"jobId": jobid,
"jrnyCnt": "1",
Expand Down Expand Up @@ -470,7 +455,7 @@ def reserve_standby_option_settings(
if isinstance(reservation, SRTReservation):
reservation = reservation.reservation_number

url = SRT_STANDBY_OPTION
url = constants.API_ENDPOINTS["standby_option"]

data = {
"pnrNo": reservation,
Expand All @@ -495,7 +480,7 @@ def get_reservations(self, paid_only: bool = False) -> list[SRTReservation]:
if not self.is_login:
raise SRTNotLoggedInError()

url = SRT_TICKETS
url = constants.API_ENDPOINTS["tickets"]
data = {"pageNo": "0"}

r = self._session.post(url=url, data=data)
Expand Down Expand Up @@ -541,7 +526,7 @@ def ticket_info(self, reservation: SRTReservation | int) -> list[SRTTicket]:
if isinstance(reservation, SRTReservation):
reservation = reservation.reservation_number

url = SRT_TICKET_INFO
url = constants.API_ENDPOINTS["ticket_info"]
data = {"pnrNo": reservation, "jrnySqno": "1"}

r = self._session.post(url=url, data=data)
Expand Down Expand Up @@ -574,7 +559,7 @@ def cancel(self, reservation: SRTReservation | int) -> bool:
if isinstance(reservation, SRTReservation):
reservation = reservation.reservation_number

url = SRT_CANCEL
url = constants.API_ENDPOINTS["cancel"]
data = {"pnrNo": reservation, "jrnyCnt": "1", "rsvChgTno": "0"}

r = self._session.post(url=url, data=data)
Expand Down
File renamed without changes.
26 changes: 26 additions & 0 deletions tests/mock_responses/login_fail_no_user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"strResult": "FAIL",
"MSG": "존재하지않는 회원입니다.",
"page": "menu",
"userMap": {
"USER_DV": null,
"wctNo": "12345",
"strDeviceInfo": ""
},
"commandMap": {
"auto": "Y",
"check": "Y",
"page": "menu",
"deviceKey": "-",
"customerYn": "",
"login_referer": "https://app.srail.or.kr:443/main/main.do",
"srchDvCd": "3",
"srchDvNm": "01012341234",
"hmpgPwdCphd": "password",
"login_idIdx": "3",
"login_idVal": "01012341234",
"login_check": "Y",
"login_auto": "Y"
},
"RTNCD": "N"
}
26 changes: 26 additions & 0 deletions tests/mock_responses/login_fail_password.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"strResult": "FAIL",
"MSG": "비밀번호 오류입니다.",
"page": "menu",
"userMap": {
"USER_DV": null,
"wctNo": "12345",
"strDeviceInfo": ""
},
"commandMap": {
"auto": "Y",
"check": "Y",
"page": "menu",
"deviceKey": "-",
"customerYn": "",
"login_referer": "https://app.srail.or.kr:443/main/main.do",
"srchDvCd": "3",
"srchDvNm": "01012341234",
"hmpgPwdCphd": "password",
"login_idIdx": "3",
"login_idVal": "01012341234",
"login_check": "Y",
"login_auto": "Y"
},
"RTNCD": "N"
}
68 changes: 68 additions & 0 deletions tests/mock_responses/login_success.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"userMap": {
"CPHD_CHG_SKP_TNO": "1.0",
"CPHD_CHG_FLG": "true",
"RET_QNTY": "",
"deviceKey": "-",
"SALE_QNTY": "",
"BTDT": "2000-01-01",
"CPHD_CHG_YMD": "20200123",
"requestJSessionTime": 1695435161352,
"UUID": "",
"CPHD_CHG_PRD": "263.0",
"KR_JSESSIONID": "JSESSIONID=1234Jpg2dZ9Ulvn81tc3sEyOTSzana6XHLMtcpVudJAz0nV7nsQqoysEeNoRtEiR;Path=/",
"RTNCD": "Y",
"USER_KEY": "-",
"USER_DV": "1",
"MSG": "정상적으로 로그인되었습니다.",
"wctNo": "12345",
"CUST_DTL_SRT_CD": "210",
"URT_DAY_NOTI": "1514",
"HME_PHONE": "null-null-null",
"JOIN_DT": "2014-01-15",
"strDeviceInfo": "",
"DLY_WRK_SCDL_TXT": "",
"CUST_SRT_CD": "P",
"CUST_NM": "홍길동",
"CUST_MG_SRT_NM": "일반회원",
"PV_NO": "",
"MB_CRD_NO": "1234123412",
"POSI_NM": "",
"IND_INFO_OFR_FLG": "N",
"CUST_DTL_SRT_NM": "일반회원",
"DPT_NM": "",
"MBL_PHONE": "010-1234-1234",
"GOFF_RS_STN_CD": "0508",
"CPHD_CHG_SKP_YN": "Y",
"WCTNO": "81301",
"CUST_CL_CD": "N",
"GRD_NM": "",
"GRCP_JONN_STN_NM": "N",
"SEX_DV_CD": "M",
"SR_JSESSIONID": "JSESSIONID=1234Jpg2dZ9Ulvn81tc3sEyOTSzana6XHLMtcpVudJAz0nV7nsQqoysEeNoRtEiR;Path=/",
"DSCP_YN": "N",
"USR_PWD_CPHD": "1234BA56E9F8B3DC64F77C87318C4F37BC12CFBF1A37573CDF3E4FA683F20155",
"ABRD_RS_STN_CD": "0123",
"CUST_MG_NO": "MP0001234567",
"uuid": "",
"SEX_DV_NM": "",
"NTCE_DV_CD": "",
"CPHD_CHG_270": "true"
},
"commandMap": {
"auto": "Y",
"check": "Y",
"page": "menu",
"deviceKey": "-",
"customerYn": "",
"login_referer": "https://app.srail.or.kr:443/main/main.do",
"srchDvCd": "3",
"srchDvNm": "01012341234",
"hmpgPwdCphd": "password",
"login_check": "Y",
"login_auto": "Y",
"login_idIdx": "3",
"login_idVal": "01012341234",
"login_pwVal": "password"
}
}
13 changes: 13 additions & 0 deletions tests/mock_responses/logout_success.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"commandMap": {
"isEnableUpdate": "N",
"isForceUpdate": "N",
"AOS_VERSION": "1.0.5",
"iOS_VERSION": "1.0.5"
},
"userMap": {
"USER_DV": "",
"wctNo": "",
"uuid": ""
}
}
77 changes: 77 additions & 0 deletions tests/test_srt_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from pathlib import Path

import pytest

mock_response_dir = Path(__file__).parent / "mock_responses"


@pytest.fixture
def mock_server(httpserver, monkeypatch):
from SRT import constants

endpoints = {
"main": httpserver.url_for("/main"),
"login": httpserver.url_for("/login"),
"logout": httpserver.url_for("/logout"),
"search_schedule": httpserver.url_for("/search_schedule"),
"reserve": httpserver.url_for("/reserve"),
"tickets": httpserver.url_for("/tickets"),
"ticket_info": httpserver.url_for("/ticket_info"),
"cancel": httpserver.url_for("/cancel"),
"standby_option": httpserver.url_for("/standby_option"),
}

monkeypatch.setattr(
constants,
"API_ENDPOINTS",
endpoints,
)

yield


def register_mock_response(httpserver, endpoint, filename, status=200):
response = (mock_response_dir / filename).read_text(encoding="utf-8")
httpserver.expect_oneshot_request(endpoint).respond_with_data(
response,
status=status,
headers={"Content-Type": "application/json"},
)


def test_login_success(mock_server, httpserver):
from SRT import SRT

register_mock_response(httpserver, "/login", "login_success.json")

srt = SRT("010-1234-1234", "password")
assert srt.is_login


def test_login_fail_wrong_password(mock_server, httpserver):
from SRT import SRT, SRTLoginError

register_mock_response(httpserver, "/login", "login_fail_password.json")

with pytest.raises(SRTLoginError):
SRT("010-1234-1234", "password")


def test_login_fail_wrong_username(mock_server, httpserver):
from SRT import SRT, SRTLoginError

register_mock_response(httpserver, "/login", "login_fail_no_user.json")

with pytest.raises(SRTLoginError):
SRT("010-1234-1234", "password")


def test_logout(mock_server, httpserver):
from SRT import SRT

register_mock_response(httpserver, "/login", "login_success.json")
register_mock_response(httpserver, "/logout", "logout_success.json")

srt = SRT("010-1234-1234", "password")
srt.logout()
assert not srt.is_login

0 comments on commit bca6fb2

Please sign in to comment.