mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
Stabilize KTX seat lookup review fixes
Constraint: PR #293 review found fallback korail2 imports failed under PYTHONNOUSERSITE and seat lookup needed clearer Dynapath/power-seat contracts. Rejected: Leave research endpoints outside DYNAPATH_PATHS | would keep the auth-header behavior ambiguous for live seat lookup. Confidence: high Scope-risk: narrow Directive: Keep fallback-only tests independent of installed korail2 so CI catches missing dependency behavior. Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_ktx_booking; PYTHONPATH=.:scripts PYTHONNOUSERSITE=1 python3 -m unittest discover -s scripts -p 'test_ktx_booking.py'; python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py; PYTHONPATH=.:scripts python3 scripts/ktx_booking.py seats --help; node --test scripts/skill-docs.test.js Not-tested: live Korail seat lookup against production endpoints
This commit is contained in:
parent
8420792a82
commit
9b9f5bc7a2
4 changed files with 53 additions and 5 deletions
|
|
@ -97,7 +97,7 @@ python3 scripts/ktx_booking.py seats 서울 부산 20260328 090000 --train-id <t
|
|||
python3 scripts/ktx_booking.py seats 서울 부산 20260328 090000 --train-id <train_id> --available-only --power-only
|
||||
```
|
||||
|
||||
특실 좌석을 확인하려면 `--room special`, KTX 외 열차를 조회했다면 `search` 와 같은 `--train-type` 을 함께 넘긴다.
|
||||
`--power-only` 는 KTX/KTX-산천에서 알려진 호차 배치 힌트 기반의 best-effort 필터다. 특실 좌석을 확인하려면 `--room special`, KTX 외 열차를 조회했다면 `search` 와 같은 `--train-type` 을 함께 넘긴다.
|
||||
|
||||
```bash
|
||||
python3 scripts/ktx_booking.py seats 남춘천 용산 20260503 150000 \
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ python3 scripts/ktx_booking.py seats 서울 부산 20260328 090000 --train-id <t
|
|||
python3 scripts/ktx_booking.py seats 서울 부산 20260328 090000 --train-id <train_id> --car-no 5 --available-only
|
||||
```
|
||||
|
||||
콘센트 꿀팁 자리부터 확인하려면 `--power-only` 를 붙인다. 응답의 `power_outlet` 은 `direct`, `adjacent`, `none` 중 하나다.
|
||||
콘센트 꿀팁 자리부터 확인하려면 `--power-only` 를 붙인다. 응답의 `power_outlet` 은 `direct`, `adjacent`, `none` 중 하나다. 이 필터는 KTX/KTX-산천에서 알려진 호차 배치 힌트 기반 best-effort 이다.
|
||||
|
||||
```bash
|
||||
python3 scripts/ktx_booking.py seats 서울 부산 20260328 090000 --train-id <train_id> --available-only --power-only
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ except ModuleNotFoundError as exc:
|
|||
|
||||
class _FallbackKorailModule:
|
||||
EMAIL_REGEX = re.compile(r".+@.+")
|
||||
PHONE_NUMBER_REGEX = re.compile(r"^\d+$")
|
||||
PHONE_NUMBER_REGEX = re.compile(r"^01\d-?\d{3,4}-?\d{4}$")
|
||||
|
||||
korail_mod = _FallbackKorailModule()
|
||||
else:
|
||||
|
|
@ -127,6 +127,8 @@ DEFAULT_USER_AGENT = "Dalvik/2.1.0 (Linux; U; Android 13; SM-S928N Build/UP1A.23
|
|||
DYNAPATH_PATHS = [
|
||||
"/classes/com.korail.mobile.certification.TicketReservation",
|
||||
"/classes/com.korail.mobile.nonMember.NonMemTicket",
|
||||
"/classes/com.korail.mobile.research.ResidualSeatsResearch.do",
|
||||
"/classes/com.korail.mobile.research.TrainResearch",
|
||||
"/classes/com.korail.mobile.seatMovie.ScheduleView",
|
||||
"/classes/com.korail.mobile.seatMovie.ScheduleViewSpecial",
|
||||
"/classes/com.korail.mobile.trn.prcFare.do",
|
||||
|
|
@ -899,10 +901,15 @@ def command_seats(args: argparse.Namespace) -> None:
|
|||
car_payloads: list[dict[str, object]] = []
|
||||
for car in cars:
|
||||
raw = client.car_seats(raw_train, str(car["car_no_raw"]), passenger_count, room_class)
|
||||
raw_seats = raw.get("seat_infos", {}).get("seat_info", [])
|
||||
seat_infos = raw.get("seat_infos", {}) if isinstance(raw, dict) else {}
|
||||
raw_seats = seat_infos.get("seat_info", []) if isinstance(seat_infos, dict) else []
|
||||
if isinstance(raw_seats, dict):
|
||||
raw_seats = [raw_seats]
|
||||
all_seats = [normalize_seat(seat) for seat in raw_seats if seat.get("h_con_seat_no") != "0A"]
|
||||
all_seats = [
|
||||
normalize_seat(seat)
|
||||
for seat in raw_seats
|
||||
if isinstance(seat, dict) and seat.get("h_con_seat_no") != "0A"
|
||||
]
|
||||
available_seats = [seat for seat in all_seats if seat["available"]]
|
||||
seats = all_seats
|
||||
if args.available_only:
|
||||
|
|
|
|||
|
|
@ -327,6 +327,10 @@ class KtxBookingTests(unittest.TestCase):
|
|||
self.assertFalse(ktx_booking.is_phone_login_id("1234567890"))
|
||||
self.assertFalse(ktx_booking.is_phone_login_id("user@example.com"))
|
||||
|
||||
def test_seat_lookup_urls_receive_dynapath_headers(self):
|
||||
self.assertTrue(any(path in ktx_booking.KORAIL_CARS_INFO for path in ktx_booking.DYNAPATH_PATHS))
|
||||
self.assertTrue(any(path in ktx_booking.KORAIL_CAR_DETAIL for path in ktx_booking.DYNAPATH_PATHS))
|
||||
|
||||
def test_command_search_replays_selected_train_type(self):
|
||||
selected = FakeTrain(
|
||||
train_no="2080",
|
||||
|
|
@ -598,6 +602,43 @@ class KtxBookingTests(unittest.TestCase):
|
|||
|
||||
self.assertIn("car_no 5", str(exc.exception))
|
||||
|
||||
def test_command_seats_treats_malformed_seat_infos_as_empty(self):
|
||||
selected = FakeTrain(train_no="009", dep_time="090000", arr_time="113000", label="selected")
|
||||
raw_train = {"h_trn_no": "009", "h_dpt_dt": "20260328"}
|
||||
train_id = ktx_booking.normalize_train(selected, index=1)["train_id"]
|
||||
client = FakeClient(
|
||||
[],
|
||||
train_details=[(selected, raw_train)],
|
||||
cars=[{"h_srcar_no": "05", "h_psrm_cl_cd": "1", "h_seat_cnt": "48", "h_rest_seat_cnt": "9"}],
|
||||
)
|
||||
client.car_seats = lambda *args, **kwargs: {"seat_infos": []}
|
||||
args = argparse.Namespace(
|
||||
dep="서울",
|
||||
arr="부산",
|
||||
date="20260328",
|
||||
time="090000",
|
||||
adults=1,
|
||||
children=0,
|
||||
toddlers=0,
|
||||
seniors=0,
|
||||
train_id=train_id,
|
||||
room="general",
|
||||
train_type="ktx",
|
||||
car_no=None,
|
||||
available_only=True,
|
||||
power_only=False,
|
||||
limit=10,
|
||||
)
|
||||
output = io.StringIO()
|
||||
|
||||
with patch.object(ktx_booking, "build_client", return_value=client):
|
||||
with redirect_stdout(output):
|
||||
ktx_booking.command_seats(args)
|
||||
|
||||
result = json.loads(output.getvalue())
|
||||
self.assertEqual(result["cars"][0]["available_seat_count"], 0)
|
||||
self.assertEqual(result["cars"][0]["seats"], [])
|
||||
|
||||
def test_build_parser_has_ncard_commands(self):
|
||||
parser = ktx_booking.build_parser()
|
||||
help_text = parser.format_help()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue