mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
test(srt): split seat helper regression coverage
This commit is contained in:
parent
d1d2eee71f
commit
82f11a12bc
5 changed files with 214 additions and 191 deletions
|
|
@ -11,9 +11,9 @@
|
|||
"build": "npm run build --workspaces --if-present",
|
||||
"build:manus-bundle": "node scripts/build-manus-bundle.js",
|
||||
"generate:plugin-manifest": "node scripts/generate-plugin-manifest.js",
|
||||
"lint": "node --check scripts/skill-docs.test.js scripts/korean_character_count.js scripts/test_korean_character_count.js scripts/korean_middle_korean.js scripts/test_korean_middle_korean.js scripts/build-manus-bundle.js scripts/test_build_manus_bundle.js scripts/workflow-actions.test.js scripts/generate-plugin-manifest.js scripts/test_generate_plugin_manifest.js && python3 -m py_compile scripts/k_skill_cleaner.py scripts/test_k_skill_cleaner.py corporate-registration-consulting/scripts/fill_official_hwp.py k-skill-cleaner/scripts/k_skill_cleaner.py scripts/fine_dust.py scripts/test_fine_dust.py scripts/ktx_booking.py scripts/test_ktx_booking.py scripts/srt_booking.py scripts/srt_seats.py scripts/test_srt_booking.py scripts/sillok_search.py scripts/test_sillok_search.py scripts/korean_spell_check.py scripts/test_korean_spell_check.py scripts/patent_search.py scripts/test_patent_search.py scripts/mfds_drug_safety.py scripts/test_mfds_drug_safety.py scripts/nts_business_registration.py scripts/test_nts_business_registration.py scripts/mfds_food_safety.py scripts/test_mfds_food_safety.py scripts/zipcode_search.py scripts/test_zipcode_search.py scripts/subway_lost_property.py scripts/test_subway_lost_property.py scripts/geeknews_search.py scripts/test_geeknews_search.py nts-business-registration/scripts/nts_business_registration.py scripts/test_naver_blog_search.py scripts/test_korean_slang_writing.py scripts/kakaotalk_mac.py scripts/test_kakaotalk_mac.py scripts/test_coupang_partners_mcp_wrapper.py scripts/test_ohou_today_deal.py scripts/ticket_availability.py scripts/test_ticket_availability.py scripts/test_danawa_price_search.py ticket-availability/scripts/ticket_availability.py coupang-product-search/scripts/coupang_partners_mcp.py ohou-today-deal/scripts/ohou_today_deal.py kakaotalk-mac/scripts/kakaotalk_mac.py naver-blog-research/scripts/_naver_http.py naver-blog-research/scripts/naver_search.py naver-blog-research/scripts/naver_read.py naver-blog-research/scripts/naver_download_images.py korean-slang-writing/scripts/_slang_http.py korean-slang-writing/scripts/slang_search.py korean-slang-writing/scripts/slang_lookup.py korean-scholarship-search/scripts/scholarship_filter.py korean-scholarship-search/scripts/test_scholarship_filter.py korean-scholarship-search/scripts/university_search_plan.py seoul-bike/scripts/seoul_bike.py scripts/test_seoul_bike.py danawa-price-search/scripts/danawa_search.py kosis-stats/scripts/run_kosis_stats.py kosis-stats/tests/test_run_kosis_stats.py kstartup-search/scripts/run_kstartup.py kstartup-search/tests/test_run_kstartup.py intercity-bus-booking/scripts/intercity_bus_search.py daangn-used-goods-search/scripts/daangn_used_goods.py daangn-realty-search/scripts/daangn_realty.py daangn-jobs-search/scripts/daangn_jobs.py daangn-cars-search/scripts/daangn_cars.py foresttrip-vacancy/scripts/run_foresttrip_vacancy.py foresttrip-vacancy/tests/test_run_foresttrip_vacancy.py && npm run lint --workspaces --if-present && ./scripts/validate-skills.sh && node scripts/generate-plugin-manifest.js --check",
|
||||
"lint": "node --check scripts/skill-docs.test.js scripts/korean_character_count.js scripts/test_korean_character_count.js scripts/korean_middle_korean.js scripts/test_korean_middle_korean.js scripts/build-manus-bundle.js scripts/test_build_manus_bundle.js scripts/workflow-actions.test.js scripts/generate-plugin-manifest.js scripts/test_generate_plugin_manifest.js && python3 -m py_compile scripts/k_skill_cleaner.py scripts/test_k_skill_cleaner.py corporate-registration-consulting/scripts/fill_official_hwp.py k-skill-cleaner/scripts/k_skill_cleaner.py scripts/fine_dust.py scripts/test_fine_dust.py scripts/ktx_booking.py scripts/test_ktx_booking.py scripts/srt_booking.py scripts/srt_seats.py scripts/srt_booking_test_support.py scripts/test_srt_booking.py scripts/test_srt_seats.py scripts/sillok_search.py scripts/test_sillok_search.py scripts/korean_spell_check.py scripts/test_korean_spell_check.py scripts/patent_search.py scripts/test_patent_search.py scripts/mfds_drug_safety.py scripts/test_mfds_drug_safety.py scripts/nts_business_registration.py scripts/test_nts_business_registration.py scripts/mfds_food_safety.py scripts/test_mfds_food_safety.py scripts/zipcode_search.py scripts/test_zipcode_search.py scripts/subway_lost_property.py scripts/test_subway_lost_property.py scripts/geeknews_search.py scripts/test_geeknews_search.py nts-business-registration/scripts/nts_business_registration.py scripts/test_naver_blog_search.py scripts/test_korean_slang_writing.py scripts/kakaotalk_mac.py scripts/test_kakaotalk_mac.py scripts/test_coupang_partners_mcp_wrapper.py scripts/test_ohou_today_deal.py scripts/ticket_availability.py scripts/test_ticket_availability.py scripts/test_danawa_price_search.py ticket-availability/scripts/ticket_availability.py coupang-product-search/scripts/coupang_partners_mcp.py ohou-today-deal/scripts/ohou_today_deal.py kakaotalk-mac/scripts/kakaotalk_mac.py naver-blog-research/scripts/_naver_http.py naver-blog-research/scripts/naver_search.py naver-blog-research/scripts/naver_read.py naver-blog-research/scripts/naver_download_images.py korean-slang-writing/scripts/_slang_http.py korean-slang-writing/scripts/slang_search.py korean-slang-writing/scripts/slang_lookup.py korean-scholarship-search/scripts/scholarship_filter.py korean-scholarship-search/scripts/test_scholarship_filter.py korean-scholarship-search/scripts/university_search_plan.py seoul-bike/scripts/seoul_bike.py scripts/test_seoul_bike.py danawa-price-search/scripts/danawa_search.py kosis-stats/scripts/run_kosis_stats.py kosis-stats/tests/test_run_kosis_stats.py kstartup-search/scripts/run_kstartup.py kstartup-search/tests/test_run_kstartup.py intercity-bus-booking/scripts/intercity_bus_search.py daangn-used-goods-search/scripts/daangn_used_goods.py daangn-realty-search/scripts/daangn_realty.py daangn-jobs-search/scripts/daangn_jobs.py daangn-cars-search/scripts/daangn_cars.py foresttrip-vacancy/scripts/run_foresttrip_vacancy.py foresttrip-vacancy/tests/test_run_foresttrip_vacancy.py && npm run lint --workspaces --if-present && ./scripts/validate-skills.sh && node scripts/generate-plugin-manifest.js --check",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "python3 -m pip install --user --quiet beautifulsoup4 && node --test scripts/skill-docs.test.js scripts/test_korean_character_count.js scripts/test_korean_middle_korean.js scripts/test_build_manus_bundle.js scripts/workflow-actions.test.js scripts/test_generate_plugin_manifest.js && PYTHONPATH=.:scripts python3 -m unittest scripts.test_k_skill_cleaner scripts.test_fine_dust scripts.test_ktx_booking scripts.test_srt_booking scripts.test_sillok_search scripts.test_korean_spell_check scripts.test_patent_search scripts.test_mfds_drug_safety scripts.test_nts_business_registration scripts.test_mfds_food_safety scripts.test_zipcode_search scripts.test_subway_lost_property scripts.test_geeknews_search scripts.test_naver_blog_search scripts.test_korean_slang_writing scripts.test_kakaotalk_mac scripts.test_coupang_partners_mcp_wrapper scripts.test_ohou_today_deal scripts.test_ticket_availability scripts.test_seoul_bike scripts.test_danawa_price_search && PYTHONPATH=.:scripts:korean-scholarship-search/scripts python3 -m unittest discover -s korean-scholarship-search/scripts -p 'test_scholarship_filter.py' && PYTHONPATH=.:scripts:kosis-stats/scripts python3 -m unittest discover -s kosis-stats/tests -p 'test_run_kosis_stats.py' && PYTHONPATH=.:scripts:kstartup-search/scripts python3 -m unittest discover -s kstartup-search/tests -p 'test_run_kstartup.py' && PYTHONPATH=.:foresttrip-vacancy/scripts python3 -m unittest discover -s foresttrip-vacancy/tests -p 'test_run_foresttrip_vacancy.py' && npm run test --workspaces --if-present && ./scripts/validate-skills.sh",
|
||||
"test": "python3 -m pip install --user --quiet beautifulsoup4 && node --test scripts/skill-docs.test.js scripts/test_korean_character_count.js scripts/test_korean_middle_korean.js scripts/test_build_manus_bundle.js scripts/workflow-actions.test.js scripts/test_generate_plugin_manifest.js && PYTHONPATH=.:scripts python3 -m unittest scripts.test_k_skill_cleaner scripts.test_fine_dust scripts.test_ktx_booking scripts.test_srt_booking scripts.test_srt_seats scripts.test_sillok_search scripts.test_korean_spell_check scripts.test_patent_search scripts.test_mfds_drug_safety scripts.test_nts_business_registration scripts.test_mfds_food_safety scripts.test_zipcode_search scripts.test_subway_lost_property scripts.test_geeknews_search scripts.test_naver_blog_search scripts.test_korean_slang_writing scripts.test_kakaotalk_mac scripts.test_coupang_partners_mcp_wrapper scripts.test_ohou_today_deal scripts.test_ticket_availability scripts.test_seoul_bike scripts.test_danawa_price_search && PYTHONPATH=.:scripts:korean-scholarship-search/scripts python3 -m unittest discover -s korean-scholarship-search/scripts -p 'test_scholarship_filter.py' && PYTHONPATH=.:scripts:kosis-stats/scripts python3 -m unittest discover -s kosis-stats/tests -p 'test_run_kosis_stats.py' && PYTHONPATH=.:scripts:kstartup-search/scripts python3 -m unittest discover -s kstartup-search/tests -p 'test_run_kstartup.py' && PYTHONPATH=.:foresttrip-vacancy/scripts python3 -m unittest discover -s foresttrip-vacancy/tests -p 'test_run_foresttrip_vacancy.py' && npm run test --workspaces --if-present && ./scripts/validate-skills.sh",
|
||||
"pack:dry-run": "npm pack --workspace k-lotto --dry-run && npm pack --workspace daiso-product-search --dry-run && npm pack --workspace market-kurly-search --dry-run && npm pack --workspace blue-ribbon-nearby --dry-run && npm pack --workspace kakao-bar-nearby --dry-run && npm pack --workspace cheap-gas-nearby --dry-run && npm pack --workspace public-restroom-nearby --dry-run && npm pack --workspace parking-lot-search --dry-run && npm pack --workspace court-auction-notice-search --dry-run && npm pack --workspace donation-place-search --dry-run && npm pack --workspace gongsijiga-search --dry-run && npm pack --workspace kbl-results --dry-run && npm pack --workspace kleague-results --dry-run && npm pack --workspace lck-analytics --dry-run && npm pack --workspace toss-securities --dry-run && npm pack --workspace hipass-receipt --dry-run && npm pack --workspace used-car-price-search --dry-run && npm pack --workspace k-skill-rhwp --dry-run && npm pack --workspace korean-marathon-schedule --dry-run && npm pack --workspace gangnamunni-clinic-search --dry-run && npm pack --workspace daishin-report-search --dry-run && npm pack --workspace sh-notice-search --dry-run && npm pack --workspace emergency-room-beds --dry-run && npm pack --workspace local-election-candidate-search --dry-run",
|
||||
"ci": "npm run lint && npm run typecheck && npm run test && npm run pack:dry-run",
|
||||
"version-packages": "changeset version",
|
||||
|
|
|
|||
128
scripts/srt_booking_test_support.py
Normal file
128
scripts/srt_booking_test_support.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
from __future__ import annotations
|
||||
|
||||
|
||||
SEAT_HTML = "\n".join([
|
||||
'<li class="scar-01 off"><strong>일반실<br />1호차</strong></li>',
|
||||
'<li class="scar-04 on"><a href="#none" onclick="selectScarInfo(\'0004\'); return false;"><strong>일반실<br />4호차</strong></a></li>',
|
||||
'<li class="scar-05"><a href="#none" onclick="selectScarInfo(\'0005\'); return false;"><strong>일반실<br />5호차</strong></a></li>',
|
||||
'<li class="scar-03 off"><strong>특실<br />3호차</strong></li>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'23\', \'6C\'); return false;">6C<strong><em>(정방향, 내측)</em></strong></a>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'11\', \'3A\'); return false;">3A<strong><em>(역방향, 창측)</em></strong></a>',
|
||||
"<span>5C<strong><em>(정방향, 내측, 선택불가)</em></strong></span>",
|
||||
])
|
||||
|
||||
SPECIAL_SEAT_HTML = "\n".join([
|
||||
'<li class="scar-03 on"><a href="#none" onclick="selectScarInfo(\'0003\'); return false;"><strong>특실<br />3호차</strong></a></li>',
|
||||
'<li class="scar-05 off"><strong>일반실<br />5호차</strong></li>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'31\', \'1A\'); return false;">1A<strong><em>(정방향, 1인석)</em></strong></a>',
|
||||
"<span>2C<strong><em>(역방향, 내측, 선택불가)</em></strong></span>",
|
||||
])
|
||||
|
||||
|
||||
class FakeTrain:
|
||||
train_number = "313"
|
||||
dep_date = "20260610"
|
||||
dep_time = "080000"
|
||||
arr_date = "20260610"
|
||||
arr_time = "103400"
|
||||
train_code = "17"
|
||||
train_name = "SRT"
|
||||
dep_station_code = "0551"
|
||||
dep_station_name = "수서"
|
||||
arr_station_code = "0020"
|
||||
arr_station_name = "부산"
|
||||
dep_station_run_order = "000001"
|
||||
arr_station_run_order = "000007"
|
||||
general_seat_state = "예약가능"
|
||||
special_seat_state = "매진"
|
||||
reserve_wait_possible_code = "-2"
|
||||
|
||||
def general_seat_available(self) -> bool:
|
||||
return True
|
||||
|
||||
def special_seat_available(self) -> bool:
|
||||
return False
|
||||
|
||||
def reserve_standby_available(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, text: str) -> None:
|
||||
self.text = text
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[dict[str, str]] = []
|
||||
|
||||
def get(self, _url: str, params: dict[str, str]) -> FakeResponse:
|
||||
self.calls.append(params)
|
||||
car = params["scarNo1"] or "0004"
|
||||
return FakeResponse(SEAT_HTML.replace("scar-04 on", f"scar-{car[-2:]} on"))
|
||||
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, train: FakeTrain) -> None:
|
||||
self.train = train
|
||||
self._session = FakeSession()
|
||||
|
||||
def search_train(
|
||||
self,
|
||||
_dep: str,
|
||||
_arr: str,
|
||||
_date: str,
|
||||
_time: str,
|
||||
_time_limit: str | None = None,
|
||||
available_only: bool = True,
|
||||
) -> list[FakeTrain]:
|
||||
return [self.train]
|
||||
|
||||
|
||||
class NoisySession(FakeSession):
|
||||
def get(self, _url: str, params: dict[str, str]) -> FakeResponse:
|
||||
print("접속자가 많아 대기열에 들어갑니다.")
|
||||
return super().get(_url, params)
|
||||
|
||||
|
||||
class NoisyClient(FakeClient):
|
||||
def __init__(self, train: FakeTrain) -> None:
|
||||
self.train = train
|
||||
self._session = NoisySession()
|
||||
|
||||
def search_train(
|
||||
self,
|
||||
_dep: str,
|
||||
_arr: str,
|
||||
_date: str,
|
||||
_time: str,
|
||||
_time_limit: str | None = None,
|
||||
available_only: bool = True,
|
||||
) -> list[FakeTrain]:
|
||||
print("대기인원: 6명")
|
||||
return [self.train]
|
||||
|
||||
|
||||
class EmptyClient(FakeClient):
|
||||
def search_train(
|
||||
self,
|
||||
_dep: str,
|
||||
_arr: str,
|
||||
_date: str,
|
||||
_time: str,
|
||||
_time_limit: str | None = None,
|
||||
available_only: bool = True,
|
||||
) -> list[FakeTrain]:
|
||||
return []
|
||||
|
||||
|
||||
class SpecialSession(FakeSession):
|
||||
def get(self, _url: str, params: dict[str, str]) -> FakeResponse:
|
||||
self.calls.append(params)
|
||||
return FakeResponse(SPECIAL_SEAT_HTML)
|
||||
|
||||
|
||||
class SpecialClient(FakeClient):
|
||||
def __init__(self, train: FakeTrain) -> None:
|
||||
self.train = train
|
||||
self._session = SpecialSession()
|
||||
|
|
@ -8,197 +8,10 @@ from contextlib import redirect_stdout
|
|||
from unittest.mock import patch
|
||||
|
||||
import srt_booking
|
||||
import srt_seats
|
||||
|
||||
|
||||
SEAT_HTML = "\n".join([
|
||||
'<li class="scar-01 off"><strong>일반실<br />1호차</strong></li>',
|
||||
'<li class="scar-04 on"><a href="#none" onclick="selectScarInfo(\'0004\'); return false;"><strong>일반실<br />4호차</strong></a></li>',
|
||||
'<li class="scar-05"><a href="#none" onclick="selectScarInfo(\'0005\'); return false;"><strong>일반실<br />5호차</strong></a></li>',
|
||||
'<li class="scar-03 off"><strong>특실<br />3호차</strong></li>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'23\', \'6C\'); return false;">6C<strong><em>(정방향, 내측)</em></strong></a>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'11\', \'3A\'); return false;">3A<strong><em>(역방향, 창측)</em></strong></a>',
|
||||
"<span>5C<strong><em>(정방향, 내측, 선택불가)</em></strong></span>",
|
||||
])
|
||||
|
||||
SPECIAL_SEAT_HTML = "\n".join([
|
||||
'<li class="scar-03 on"><a href="#none" onclick="selectScarInfo(\'0003\'); return false;"><strong>특실<br />3호차</strong></a></li>',
|
||||
'<li class="scar-05 off"><strong>일반실<br />5호차</strong></li>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'31\', \'1A\'); return false;">1A<strong><em>(정방향, 1인석)</em></strong></a>',
|
||||
"<span>2C<strong><em>(역방향, 내측, 선택불가)</em></strong></span>",
|
||||
])
|
||||
|
||||
MISSING_DETAIL_HTML = "\n".join([
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'41\', \'9A\'); return false;">9A<strong><em>()</em></strong></a>',
|
||||
])
|
||||
|
||||
|
||||
class FakeTrain:
|
||||
train_number = "313"
|
||||
dep_date = "20260610"
|
||||
dep_time = "080000"
|
||||
arr_date = "20260610"
|
||||
arr_time = "103400"
|
||||
train_code = "17"
|
||||
train_name = "SRT"
|
||||
dep_station_code = "0551"
|
||||
dep_station_name = "수서"
|
||||
arr_station_code = "0020"
|
||||
arr_station_name = "부산"
|
||||
dep_station_run_order = "000001"
|
||||
arr_station_run_order = "000007"
|
||||
general_seat_state = "예약가능"
|
||||
special_seat_state = "매진"
|
||||
reserve_wait_possible_code = "-2"
|
||||
|
||||
def general_seat_available(self) -> bool:
|
||||
return True
|
||||
|
||||
def special_seat_available(self) -> bool:
|
||||
return False
|
||||
|
||||
def reserve_standby_available(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, text: str) -> None:
|
||||
self.text = text
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[dict[str, str]] = []
|
||||
|
||||
def get(self, _url: str, params: dict[str, str]) -> FakeResponse:
|
||||
self.calls.append(params)
|
||||
car = params["scarNo1"] or "0004"
|
||||
return FakeResponse(SEAT_HTML.replace("scar-04 on", f"scar-{car[-2:]} on"))
|
||||
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, train: FakeTrain) -> None:
|
||||
self.train = train
|
||||
self._session = FakeSession()
|
||||
|
||||
def search_train(
|
||||
self,
|
||||
_dep: str,
|
||||
_arr: str,
|
||||
_date: str,
|
||||
_time: str,
|
||||
_time_limit: str | None = None,
|
||||
available_only: bool = True,
|
||||
) -> list[FakeTrain]:
|
||||
return [self.train]
|
||||
|
||||
|
||||
class NoisySession(FakeSession):
|
||||
def get(self, _url: str, params: dict[str, str]) -> FakeResponse:
|
||||
print("접속자가 많아 대기열에 들어갑니다.")
|
||||
return super().get(_url, params)
|
||||
|
||||
|
||||
class NoisyClient(FakeClient):
|
||||
def __init__(self, train: FakeTrain) -> None:
|
||||
self.train = train
|
||||
self._session = NoisySession()
|
||||
|
||||
def search_train(
|
||||
self,
|
||||
_dep: str,
|
||||
_arr: str,
|
||||
_date: str,
|
||||
_time: str,
|
||||
_time_limit: str | None = None,
|
||||
available_only: bool = True,
|
||||
) -> list[FakeTrain]:
|
||||
print("대기인원: 6명")
|
||||
return [self.train]
|
||||
|
||||
|
||||
class EmptyClient(FakeClient):
|
||||
def search_train(
|
||||
self,
|
||||
_dep: str,
|
||||
_arr: str,
|
||||
_date: str,
|
||||
_time: str,
|
||||
_time_limit: str | None = None,
|
||||
available_only: bool = True,
|
||||
) -> list[FakeTrain]:
|
||||
return []
|
||||
|
||||
|
||||
class SpecialSession(FakeSession):
|
||||
def get(self, _url: str, params: dict[str, str]) -> FakeResponse:
|
||||
self.calls.append(params)
|
||||
return FakeResponse(SPECIAL_SEAT_HTML)
|
||||
|
||||
|
||||
class SpecialClient(FakeClient):
|
||||
def __init__(self, train: FakeTrain) -> None:
|
||||
self.train = train
|
||||
self._session = SpecialSession()
|
||||
from srt_booking_test_support import EmptyClient, FakeClient, FakeTrain, NoisyClient, SpecialClient
|
||||
|
||||
|
||||
class SrtSeatTests(unittest.TestCase):
|
||||
def test_normalize_car_and_seat_maps_srt_html(self) -> None:
|
||||
cars = srt_seats.parse_cars(SEAT_HTML)
|
||||
seats = srt_seats.parse_seats(SEAT_HTML)
|
||||
|
||||
self.assertEqual([car["car_no"] for car in cars if car["available"]], [4, 5])
|
||||
self.assertEqual(cars[1]["room_class"], "일반실")
|
||||
self.assertTrue(cars[1]["current"])
|
||||
self.assertEqual([seat["seat"] for seat in seats if seat["available"]], ["6C", "3A"])
|
||||
self.assertEqual([seat["seat"] for seat in seats if not seat["available"]], ["5C"])
|
||||
self.assertEqual(seats[0]["direction"], "정방향")
|
||||
self.assertEqual(seats[0]["position"], "내측")
|
||||
self.assertEqual(seats[2]["notes"], ["선택불가"])
|
||||
|
||||
def test_booking_priority_sorts_middle_cars_before_end_cars(self) -> None:
|
||||
cars: list[srt_seats.SrtCar] = [
|
||||
{"car_no": 1, "car_no_raw": "0001", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 8, "car_no_raw": "0008", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 2, "car_no_raw": "0002", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 7, "car_no_raw": "0007", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 3, "car_no_raw": "0003", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 6, "car_no_raw": "0006", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 4, "car_no_raw": "0004", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 5, "car_no_raw": "0005", "room_class": "일반실", "available": True, "current": False},
|
||||
]
|
||||
|
||||
sorted_cars = srt_seats.sort_cars_for_booking(cars)
|
||||
|
||||
self.assertEqual([car["car_no"] for car in sorted_cars], [4, 5, 3, 6, 2, 7, 1, 8])
|
||||
|
||||
def test_booking_priority_sorts_forward_window_before_other_seats(self) -> None:
|
||||
seats: list[srt_seats.SrtSeat] = [
|
||||
{"seat": "3A", "seat_no": "11", "available": True, "direction": "역방향", "position": "창측", "notes": []},
|
||||
{"seat": "6C", "seat_no": "23", "available": True, "direction": "정방향", "position": "내측", "notes": []},
|
||||
{"seat": "2A", "seat_no": "7", "available": True, "direction": "정방향", "position": "창측", "notes": []},
|
||||
]
|
||||
|
||||
sorted_seats = srt_seats.sort_seats_for_booking(seats, "forward-window")
|
||||
|
||||
self.assertEqual([seat["seat"] for seat in sorted_seats], ["2A", "6C", "3A"])
|
||||
|
||||
def test_booking_priority_treats_single_seat_as_window_preference(self) -> None:
|
||||
seats: list[srt_seats.SrtSeat] = [
|
||||
{"seat": "1C", "seat_no": "3", "available": True, "direction": "정방향", "position": "내측", "notes": []},
|
||||
{"seat": "2A", "seat_no": "5", "available": True, "direction": "정방향", "position": "1인석", "notes": []},
|
||||
]
|
||||
|
||||
sorted_seats = srt_seats.sort_seats_for_booking(seats, "window-forward")
|
||||
|
||||
self.assertEqual([seat["seat"] for seat in sorted_seats], ["2A", "1C"])
|
||||
|
||||
def test_parse_seat_page_marks_missing_detail_attributes_unknown(self) -> None:
|
||||
seats = srt_seats.parse_seats(MISSING_DETAIL_HTML)
|
||||
|
||||
self.assertEqual(seats[0]["direction"], "unknown")
|
||||
self.assertEqual(seats[0]["position"], "unknown")
|
||||
|
||||
def test_command_seats_outputs_available_seats_by_booking_preference(self) -> None:
|
||||
train = FakeTrain()
|
||||
train_id = srt_booking.build_train_id(train)
|
||||
|
|
|
|||
82
scripts/test_srt_seats.py
Normal file
82
scripts/test_srt_seats.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
import srt_seats
|
||||
|
||||
|
||||
SEAT_HTML = "\n".join([
|
||||
'<li class="scar-01 off"><strong>일반실<br />1호차</strong></li>',
|
||||
'<li class="scar-04 on"><a href="#none" onclick="selectScarInfo(\'0004\'); return false;"><strong>일반실<br />4호차</strong></a></li>',
|
||||
'<li class="scar-05"><a href="#none" onclick="selectScarInfo(\'0005\'); return false;"><strong>일반실<br />5호차</strong></a></li>',
|
||||
'<li class="scar-03 off"><strong>특실<br />3호차</strong></li>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'23\', \'6C\'); return false;">6C<strong><em>(정방향, 내측)</em></strong></a>',
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'11\', \'3A\'); return false;">3A<strong><em>(역방향, 창측)</em></strong></a>',
|
||||
"<span>5C<strong><em>(정방향, 내측, 선택불가)</em></strong></span>",
|
||||
])
|
||||
|
||||
MISSING_DETAIL_HTML = "\n".join([
|
||||
'<a href="#none" onclick="selectSeatInfo(this, \'41\', \'9A\'); return false;">9A<strong><em>()</em></strong></a>',
|
||||
])
|
||||
|
||||
|
||||
class SrtSeatParserTests(unittest.TestCase):
|
||||
def test_normalize_car_and_seat_maps_srt_html(self) -> None:
|
||||
cars = srt_seats.parse_cars(SEAT_HTML)
|
||||
seats = srt_seats.parse_seats(SEAT_HTML)
|
||||
|
||||
self.assertEqual([car["car_no"] for car in cars if car["available"]], [4, 5])
|
||||
self.assertEqual(cars[1]["room_class"], "일반실")
|
||||
self.assertTrue(cars[1]["current"])
|
||||
self.assertEqual([seat["seat"] for seat in seats if seat["available"]], ["6C", "3A"])
|
||||
self.assertEqual([seat["seat"] for seat in seats if not seat["available"]], ["5C"])
|
||||
self.assertEqual(seats[0]["direction"], "정방향")
|
||||
self.assertEqual(seats[0]["position"], "내측")
|
||||
self.assertEqual(seats[2]["notes"], ["선택불가"])
|
||||
|
||||
def test_booking_priority_sorts_middle_cars_before_end_cars(self) -> None:
|
||||
cars: list[srt_seats.SrtCar] = [
|
||||
{"car_no": 1, "car_no_raw": "0001", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 8, "car_no_raw": "0008", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 2, "car_no_raw": "0002", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 7, "car_no_raw": "0007", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 3, "car_no_raw": "0003", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 6, "car_no_raw": "0006", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 4, "car_no_raw": "0004", "room_class": "일반실", "available": True, "current": False},
|
||||
{"car_no": 5, "car_no_raw": "0005", "room_class": "일반실", "available": True, "current": False},
|
||||
]
|
||||
|
||||
sorted_cars = srt_seats.sort_cars_for_booking(cars)
|
||||
|
||||
self.assertEqual([car["car_no"] for car in sorted_cars], [4, 5, 3, 6, 2, 7, 1, 8])
|
||||
|
||||
def test_booking_priority_sorts_forward_window_before_other_seats(self) -> None:
|
||||
seats: list[srt_seats.SrtSeat] = [
|
||||
{"seat": "3A", "seat_no": "11", "available": True, "direction": "역방향", "position": "창측", "notes": []},
|
||||
{"seat": "6C", "seat_no": "23", "available": True, "direction": "정방향", "position": "내측", "notes": []},
|
||||
{"seat": "2A", "seat_no": "7", "available": True, "direction": "정방향", "position": "창측", "notes": []},
|
||||
]
|
||||
|
||||
sorted_seats = srt_seats.sort_seats_for_booking(seats, "forward-window")
|
||||
|
||||
self.assertEqual([seat["seat"] for seat in sorted_seats], ["2A", "6C", "3A"])
|
||||
|
||||
def test_booking_priority_treats_single_seat_as_window_preference(self) -> None:
|
||||
seats: list[srt_seats.SrtSeat] = [
|
||||
{"seat": "1C", "seat_no": "3", "available": True, "direction": "정방향", "position": "내측", "notes": []},
|
||||
{"seat": "2A", "seat_no": "5", "available": True, "direction": "정방향", "position": "1인석", "notes": []},
|
||||
]
|
||||
|
||||
sorted_seats = srt_seats.sort_seats_for_booking(seats, "window-forward")
|
||||
|
||||
self.assertEqual([seat["seat"] for seat in sorted_seats], ["2A", "1C"])
|
||||
|
||||
def test_parse_seat_page_marks_missing_detail_attributes_unknown(self) -> None:
|
||||
seats = srt_seats.parse_seats(MISSING_DETAIL_HTML)
|
||||
|
||||
self.assertEqual(seats[0]["direction"], "unknown")
|
||||
self.assertEqual(seats[0]["position"], "unknown")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -12,7 +12,7 @@ metadata:
|
|||
|
||||
## What this skill does
|
||||
|
||||
`SRTrain` 위에 `scripts/srt_booking.py` helper 를 얹어 SRT 조회, 호차별 좌석번호 확인, 예약과 취소를 처리한다.
|
||||
`SRTrain` 위에 `scripts/srt_booking.py` helper 를 얹어 SRT 조회와 호차별 좌석번호 확인을 처리하고, 예약과 취소는 고정된 열차/예약을 다시 식별한 뒤 `SRTrain`으로 진행한다.
|
||||
|
||||
## When to use
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue