mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
Document the verified public tracking outputs explicitly
Add aligned CJ and 우체국 sample public-output blocks to both delivery-tracking docs and extend the docs regression so those examples stay synchronized with the normalized non-PII contract. The new examples are dated live smoke-test captures, giving reviewers and users a concrete expected result without relying on raw carrier fields. Constraint: feature/#4 already matched dev, so the follow-up needed a small reviewable change to reopen PR work on the same branch Constraint: Keep verification offline in CI and avoid new runtime dependencies Rejected: Add live carrier calls to CI | external endpoints are flaky and would make the docs regression nondeterministic Confidence: high Scope-risk: narrow Reversibility: clean Directive: Refresh both dated sample-output blocks together if the smoke-test invoices ever change their public tracking history Tested: node --test scripts/skill-docs.test.js Tested: npm run ci Tested: python3 /tmp/cj_verify.py Tested: python3 /tmp/epost_verify.py Tested: npx --yes skills add . --list Tested: git diff --check Not-tested: Automated live carrier verification in CI
This commit is contained in:
parent
fed0973979
commit
21f036e942
3 changed files with 198 additions and 0 deletions
|
|
@ -154,6 +154,42 @@ PY
|
|||
rm -f "$tmp_body" "$tmp_cookie" "$tmp_json"
|
||||
```
|
||||
|
||||
#### CJ 공개 출력 예시
|
||||
|
||||
아래 값은 2026-03-27 기준 live smoke test(`1234567890`)에서 확인한 정규화 결과다.
|
||||
|
||||
```json
|
||||
{
|
||||
"carrier": "cj",
|
||||
"invoice": "1234567890",
|
||||
"status_code": "91",
|
||||
"status": "배달완료",
|
||||
"timestamp": "2026-03-21 12:22:13",
|
||||
"location": "경기광주오포",
|
||||
"event_count": 3,
|
||||
"recent_events": [
|
||||
{
|
||||
"timestamp": "2026-03-10 03:01:45",
|
||||
"location": "청원HUB",
|
||||
"status_code": "44",
|
||||
"status": "상품이동중"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-03-21 10:53:19",
|
||||
"location": "경기광주오포",
|
||||
"status_code": "82",
|
||||
"status": "배송출발"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-03-21 12:22:13",
|
||||
"location": "경기광주오포",
|
||||
"status_code": "91",
|
||||
"status": "배달완료"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
추가 smoke test 로는 `000000000000` 도 사용할 수 있다.
|
||||
|
||||
CJ 응답은 `parcelResultMap.resultList` 가 비어 있어도 `parcelDetailResultMap.resultList` 쪽에 이벤트가 들어올 수 있으므로, 상세 이벤트 배열을 우선 본다. published 예시는 공통 결과 스키마(`carrier`, `invoice`, `status`, `timestamp`, `location`, `event_count`, `recent_events`, 선택적 `status_code`)에 맞춰 비식별 필드만 남기고, 담당자 이름·연락처가 섞일 수 있는 `crgNm` 원문은 그대로 보여주지 않는다.
|
||||
|
|
@ -259,6 +295,33 @@ PY
|
|||
rm -f "$tmp_html"
|
||||
```
|
||||
|
||||
#### 우체국 공개 출력 예시
|
||||
|
||||
아래 값은 2026-03-27 기준 live smoke test(`1234567890123`)에서 확인한 정규화 결과다.
|
||||
|
||||
```json
|
||||
{
|
||||
"carrier": "epost",
|
||||
"invoice": "1234567890123",
|
||||
"status": "배달완료",
|
||||
"timestamp": "2025.12.04 15:13",
|
||||
"location": "제주우편집중국",
|
||||
"event_count": 2,
|
||||
"recent_events": [
|
||||
{
|
||||
"timestamp": "2025.12.04 15:13",
|
||||
"location": "제주우편집중국",
|
||||
"status": "배달준비"
|
||||
},
|
||||
{
|
||||
"timestamp": "2025.12.04 15:13",
|
||||
"location": "제주우편집중국",
|
||||
"status": "배달완료"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
우체국 기본정보 테이블은 `등기번호`, `보내는 분/접수일자`, `받는 분`, `수령인/배달일자`, `취급구분`, `배달결과` 순서를 사용하고, 상세 이벤트는 `processTable` 아래 `날짜 / 시간 / 발생국 / 처리현황` 행을 읽으면 된다. published 예시는 CJ와 같은 공통 결과 스키마(`carrier`, `invoice`, `status`, `timestamp`, `location`, `event_count`, `recent_events`)에 맞춰 배송 상태에 필요한 값만 남기고, 이벤트 location에 섞일 수 있는 `TEL` 번호 조각도 제거한 뒤 수령인/상세 메모 원문은 그대로 노출하지 않는다.
|
||||
|
||||
### 3. Normalize for humans
|
||||
|
|
|
|||
|
|
@ -105,6 +105,42 @@ PY
|
|||
rm -f "$tmp_body" "$tmp_cookie" "$tmp_json"
|
||||
```
|
||||
|
||||
### CJ 공개 출력 예시
|
||||
|
||||
아래 값은 2026-03-27 기준 live smoke test(`1234567890`)에서 확인한 정규화 결과다.
|
||||
|
||||
```json
|
||||
{
|
||||
"carrier": "cj",
|
||||
"invoice": "1234567890",
|
||||
"status_code": "91",
|
||||
"status": "배달완료",
|
||||
"timestamp": "2026-03-21 12:22:13",
|
||||
"location": "경기광주오포",
|
||||
"event_count": 3,
|
||||
"recent_events": [
|
||||
{
|
||||
"timestamp": "2026-03-10 03:01:45",
|
||||
"location": "청원HUB",
|
||||
"status_code": "44",
|
||||
"status": "상품이동중"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-03-21 10:53:19",
|
||||
"location": "경기광주오포",
|
||||
"status_code": "82",
|
||||
"status": "배송출발"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-03-21 12:22:13",
|
||||
"location": "경기광주오포",
|
||||
"status_code": "91",
|
||||
"status": "배달완료"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
CJ는 JSON 응답이므로 `parcelDetailResultMap.resultList` 를 기준으로 상태를 읽는 편이 가장 안정적이다. 문서 예시는 공통 결과 스키마(`carrier`, `invoice`, `status`, `timestamp`, `location`, `event_count`, `recent_events`, 선택적 `status_code`)만 남기고, 담당자 이름이나 휴대폰 번호가 포함될 수 있는 `crgNm` 원문은 그대로 출력하지 않는다.
|
||||
|
||||
## 우체국 예시
|
||||
|
|
@ -204,6 +240,33 @@ PY
|
|||
rm -f "$tmp_html"
|
||||
```
|
||||
|
||||
### 우체국 공개 출력 예시
|
||||
|
||||
아래 값은 2026-03-27 기준 live smoke test(`1234567890123`)에서 확인한 정규화 결과다.
|
||||
|
||||
```json
|
||||
{
|
||||
"carrier": "epost",
|
||||
"invoice": "1234567890123",
|
||||
"status": "배달완료",
|
||||
"timestamp": "2025.12.04 15:13",
|
||||
"location": "제주우편집중국",
|
||||
"event_count": 2,
|
||||
"recent_events": [
|
||||
{
|
||||
"timestamp": "2025.12.04 15:13",
|
||||
"location": "제주우편집중국",
|
||||
"status": "배달준비"
|
||||
},
|
||||
{
|
||||
"timestamp": "2025.12.04 15:13",
|
||||
"location": "제주우편집중국",
|
||||
"status": "배달완료"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
우체국은 HTML 응답이라 기본정보 `table_col` 과 상세 `processTable` 을 파싱해야 한다. 문서 예시는 CJ와 같은 공통 결과 스키마(`carrier`, `invoice`, `status`, `timestamp`, `location`, `event_count`, `recent_events`)만 남기고, 이벤트 location에 섞일 수 있는 `TEL` 번호 조각도 제거한 뒤 수령인/상세 메모 원문은 그대로 출력하지 않는다.
|
||||
|
||||
## 결과 정리 기준
|
||||
|
|
|
|||
|
|
@ -35,6 +35,14 @@ function findRecentEventsBlock(doc, carrier) {
|
|||
return block;
|
||||
}
|
||||
|
||||
function findJsonFenceAfterLabel(doc, label) {
|
||||
const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const match = doc.match(new RegExp(`${escaped}[\\s\\S]*?\\\`\\\`\\\`json\\n([\\s\\S]*?)\\n\\\`\\\`\\\``));
|
||||
|
||||
assert.ok(match, `expected JSON example after "${label}"`);
|
||||
return JSON.parse(match[1]);
|
||||
}
|
||||
|
||||
test("root npm test script includes the skill docs regression suite", () => {
|
||||
const packageJson = JSON.parse(read("package.json"));
|
||||
|
||||
|
|
@ -329,3 +337,67 @@ test("delivery-tracking published examples lock a shared normalized non-PII sche
|
|||
assert.doesNotMatch(skill, /"message":\s*latest\.get\("crgNm"\)/);
|
||||
assert.doesNotMatch(featureDoc, /print\(\{\s*"tracking_no"/);
|
||||
});
|
||||
|
||||
test("delivery-tracking docs publish aligned sample normalized outputs for both carriers", () => {
|
||||
const skill = read(path.join("delivery-tracking", "SKILL.md"));
|
||||
const featureDoc = read(path.join("docs", "features", "delivery-tracking.md"));
|
||||
const cjSkillOutput = findJsonFenceAfterLabel(skill, "CJ 공개 출력 예시");
|
||||
const cjFeatureOutput = findJsonFenceAfterLabel(featureDoc, "CJ 공개 출력 예시");
|
||||
const epostSkillOutput = findJsonFenceAfterLabel(skill, "우체국 공개 출력 예시");
|
||||
const epostFeatureOutput = findJsonFenceAfterLabel(featureDoc, "우체국 공개 출력 예시");
|
||||
|
||||
assert.deepEqual(cjSkillOutput, cjFeatureOutput, "CJ sample output must stay aligned across docs");
|
||||
assert.deepEqual(epostSkillOutput, epostFeatureOutput, "ePost sample output must stay aligned across docs");
|
||||
|
||||
assert.deepEqual(Object.keys(cjSkillOutput), [
|
||||
"carrier",
|
||||
"invoice",
|
||||
"status_code",
|
||||
"status",
|
||||
"timestamp",
|
||||
"location",
|
||||
"event_count",
|
||||
"recent_events",
|
||||
]);
|
||||
assert.equal(cjSkillOutput.carrier, "cj");
|
||||
assert.equal(cjSkillOutput.invoice, "1234567890");
|
||||
assert.equal(typeof cjSkillOutput.status, "string");
|
||||
assert.equal(typeof cjSkillOutput.timestamp, "string");
|
||||
assert.equal(typeof cjSkillOutput.location, "string");
|
||||
assert.equal(typeof cjSkillOutput.event_count, "number");
|
||||
assert.ok(Array.isArray(cjSkillOutput.recent_events));
|
||||
assert.ok(cjSkillOutput.recent_events.length > 0 && cjSkillOutput.recent_events.length <= 3);
|
||||
for (const event of cjSkillOutput.recent_events) {
|
||||
assert.deepEqual(Object.keys(event), ["timestamp", "location", "status_code", "status"]);
|
||||
}
|
||||
|
||||
assert.deepEqual(Object.keys(epostSkillOutput), [
|
||||
"carrier",
|
||||
"invoice",
|
||||
"status",
|
||||
"timestamp",
|
||||
"location",
|
||||
"event_count",
|
||||
"recent_events",
|
||||
]);
|
||||
assert.equal(epostSkillOutput.carrier, "epost");
|
||||
assert.equal(epostSkillOutput.invoice, "1234567890123");
|
||||
assert.equal(typeof epostSkillOutput.status, "string");
|
||||
assert.equal(typeof epostSkillOutput.timestamp, "string");
|
||||
assert.equal(typeof epostSkillOutput.location, "string");
|
||||
assert.equal(typeof epostSkillOutput.event_count, "number");
|
||||
assert.ok(Array.isArray(epostSkillOutput.recent_events));
|
||||
assert.ok(epostSkillOutput.recent_events.length > 0 && epostSkillOutput.recent_events.length <= 3);
|
||||
for (const event of epostSkillOutput.recent_events) {
|
||||
assert.deepEqual(Object.keys(event), ["timestamp", "location", "status"]);
|
||||
assert.ok(!JSON.stringify(event).includes("TEL"));
|
||||
}
|
||||
|
||||
for (const output of [cjSkillOutput, epostSkillOutput]) {
|
||||
const serialized = JSON.stringify(output);
|
||||
assert.ok(!serialized.includes("crgNm"));
|
||||
assert.ok(!serialized.includes("sender"));
|
||||
assert.ok(!serialized.includes("receiver"));
|
||||
assert.ok(!serialized.includes("delivered_to"));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue