$ uv run tools/flow.py test cases/lab01-edges.json -v POST http://localhost:5678/webhook/my-faq-check · 6 case(s) × 1 ✔ 제목 40자는 통과, 41자는 너무 김 · 0.1s → request POST /webhook/my-faq-check { "batch": "E-1", "items": [ { "id": "FAQ-401", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가", "keywords": [ "메신저", "알림" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-402", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가요", "keywords": [ "메신저" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-403", "title": " ", "keywords": [ "사원증" ], "answer": "사원증 사진은 인사 포털에서 바꿉니다." }, { "id": "FAQ-404", "title": "장비 신청", "keywords": [ "장비" ], "answer": "IT 포털에서 신청" }, { "id": "FAQ-405", "title": "장비 반납", "keywords": [ "반납" ], "answer": "IT포털에서 신청" }, { "id": "FAQ-406", "title": "야간 장애 연락", "keywords": [ "야간", "장애" ], "answer": "야간 장애는 당직자 01012345678 로 전화합니다." }, { "id": "FAQ-407", "title": "IT 문의 창구", "keywords": [ "문의", "창구" ], "answer": "IT 문의 창구 번호는 02-123-4567 (평일 9~18시) 입니다." }, { "id": "FAQ-408", "title": "도움말 모음", "keywords": [ "도움말" ], "answer": "도움말은 https://intra.example.com/help 에 모여 있습니다." }, { "id": "FAQ-409", "title": "보안 교육 자료", "keywords": [ "보안 교육" ], "answer": "보안 교육 자료는 http://intra.example.com.evil.io/edu 에서 받습니다." }, { "id": "faq-410", "title": "출장 노트북", "keywords": [ "출장" ], "answer": "출장용 노트북은 IT팀에 사흘 전까지 신청합니다." } ] } ← response HTTP 200 { "batch": "E-1", "total": 10, "valid": 4, "invalid": 6, "with_warnings": 0, "rows": [ { "id": "FAQ-401", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-402", "ok": false, "errors": [ "title_too_long" ], "warnings": [] }, { "id": "FAQ-403", "ok": false, "errors": [ "title_missing" ], "warnings": [] }, { "id": "FAQ-404", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-405", "ok": false, "errors": [ "answer_too_short" ], "warnings": [] }, { "id": "FAQ-406", "ok": false, "errors": [ "pii" ], "warnings": [] }, { "id": "FAQ-407", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-408", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-409", "ok": false, "errors": [ "external_link" ], "warnings": [] }, { "id": "faq-410", "ok": false, "errors": [ "id_format" ], "warnings": [] } ], "codes": { "title_too_long": 1, "title_missing": 1, "answer_too_short": 1, "pii": 1, "external_link": 1, "id_format": 1 } } ✔ 공백만 있는 제목은 빈 값 · 0.1s → request POST /webhook/my-faq-check { "batch": "E-1", "items": [ { "id": "FAQ-401", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가", "keywords": [ "메신저", "알림" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-402", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가요", "keywords": [ "메신저" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-403", "title": " ", "keywords": [ "사원증" ], "answer": "사원증 사진은 인사 포털에서 바꿉니다." }, { "id": "FAQ-404", "title": "장비 신청", "keywords": [ "장비" ], "answer": "IT 포털에서 신청" }, { "id": "FAQ-405", "title": "장비 반납", "keywords": [ "반납" ], "answer": "IT포털에서 신청" }, { "id": "FAQ-406", "title": "야간 장애 연락", "keywords": [ "야간", "장애" ], "answer": "야간 장애는 당직자 01012345678 로 전화합니다." }, { "id": "FAQ-407", "title": "IT 문의 창구", "keywords": [ "문의", "창구" ], "answer": "IT 문의 창구 번호는 02-123-4567 (평일 9~18시) 입니다." }, { "id": "FAQ-408", "title": "도움말 모음", "keywords": [ "도움말" ], "answer": "도움말은 https://intra.example.com/help 에 모여 있습니다." }, { "id": "FAQ-409", "title": "보안 교육 자료", "keywords": [ "보안 교육" ], "answer": "보안 교육 자료는 http://intra.example.com.evil.io/edu 에서 받습니다." }, { "id": "faq-410", "title": "출장 노트북", "keywords": [ "출장" ], "answer": "출장용 노트북은 IT팀에 사흘 전까지 신청합니다." } ] } ← response HTTP 200 { "batch": "E-1", "total": 10, "valid": 4, "invalid": 6, "with_warnings": 0, "rows": [ { "id": "FAQ-401", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-402", "ok": false, "errors": [ "title_too_long" ], "warnings": [] }, { "id": "FAQ-403", "ok": false, "errors": [ "title_missing" ], "warnings": [] }, { "id": "FAQ-404", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-405", "ok": false, "errors": [ "answer_too_short" ], "warnings": [] }, { "id": "FAQ-406", "ok": false, "errors": [ "pii" ], "warnings": [] }, { "id": "FAQ-407", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-408", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-409", "ok": false, "errors": [ "external_link" ], "warnings": [] }, { "id": "faq-410", "ok": false, "errors": [ "id_format" ], "warnings": [] } ], "codes": { "title_too_long": 1, "title_missing": 1, "answer_too_short": 1, "pii": 1, "external_link": 1, "id_format": 1 } } ✔ 답 10자는 통과, 9자는 너무 짧음 · 0.0s → request POST /webhook/my-faq-check { "batch": "E-1", "items": [ { "id": "FAQ-401", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가", "keywords": [ "메신저", "알림" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-402", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가요", "keywords": [ "메신저" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-403", "title": " ", "keywords": [ "사원증" ], "answer": "사원증 사진은 인사 포털에서 바꿉니다." }, { "id": "FAQ-404", "title": "장비 신청", "keywords": [ "장비" ], "answer": "IT 포털에서 신청" }, { "id": "FAQ-405", "title": "장비 반납", "keywords": [ "반납" ], "answer": "IT포털에서 신청" }, { "id": "FAQ-406", "title": "야간 장애 연락", "keywords": [ "야간", "장애" ], "answer": "야간 장애는 당직자 01012345678 로 전화합니다." }, { "id": "FAQ-407", "title": "IT 문의 창구", "keywords": [ "문의", "창구" ], "answer": "IT 문의 창구 번호는 02-123-4567 (평일 9~18시) 입니다." }, { "id": "FAQ-408", "title": "도움말 모음", "keywords": [ "도움말" ], "answer": "도움말은 https://intra.example.com/help 에 모여 있습니다." }, { "id": "FAQ-409", "title": "보안 교육 자료", "keywords": [ "보안 교육" ], "answer": "보안 교육 자료는 http://intra.example.com.evil.io/edu 에서 받습니다." }, { "id": "faq-410", "title": "출장 노트북", "keywords": [ "출장" ], "answer": "출장용 노트북은 IT팀에 사흘 전까지 신청합니다." } ] } ← response HTTP 200 { "batch": "E-1", "total": 10, "valid": 4, "invalid": 6, "with_warnings": 0, "rows": [ { "id": "FAQ-401", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-402", "ok": false, "errors": [ "title_too_long" ], "warnings": [] }, { "id": "FAQ-403", "ok": false, "errors": [ "title_missing" ], "warnings": [] }, { "id": "FAQ-404", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-405", "ok": false, "errors": [ "answer_too_short" ], "warnings": [] }, { "id": "FAQ-406", "ok": false, "errors": [ "pii" ], "warnings": [] }, { "id": "FAQ-407", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-408", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-409", "ok": false, "errors": [ "external_link" ], "warnings": [] }, { "id": "faq-410", "ok": false, "errors": [ "id_format" ], "warnings": [] } ], "codes": { "title_too_long": 1, "title_missing": 1, "answer_too_short": 1, "pii": 1, "external_link": 1, "id_format": 1 } } ✔ 하이픈 없는 휴대전화도 개인정보, 사무실 번호는 통과 · 0.1s → request POST /webhook/my-faq-check { "batch": "E-1", "items": [ { "id": "FAQ-401", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가", "keywords": [ "메신저", "알림" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-402", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가요", "keywords": [ "메신저" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-403", "title": " ", "keywords": [ "사원증" ], "answer": "사원증 사진은 인사 포털에서 바꿉니다." }, { "id": "FAQ-404", "title": "장비 신청", "keywords": [ "장비" ], "answer": "IT 포털에서 신청" }, { "id": "FAQ-405", "title": "장비 반납", "keywords": [ "반납" ], "answer": "IT포털에서 신청" }, { "id": "FAQ-406", "title": "야간 장애 연락", "keywords": [ "야간", "장애" ], "answer": "야간 장애는 당직자 01012345678 로 전화합니다." }, { "id": "FAQ-407", "title": "IT 문의 창구", "keywords": [ "문의", "창구" ], "answer": "IT 문의 창구 번호는 02-123-4567 (평일 9~18시) 입니다." }, { "id": "FAQ-408", "title": "도움말 모음", "keywords": [ "도움말" ], "answer": "도움말은 https://intra.example.com/help 에 모여 있습니다." }, { "id": "FAQ-409", "title": "보안 교육 자료", "keywords": [ "보안 교육" ], "answer": "보안 교육 자료는 http://intra.example.com.evil.io/edu 에서 받습니다." }, { "id": "faq-410", "title": "출장 노트북", "keywords": [ "출장" ], "answer": "출장용 노트북은 IT팀에 사흘 전까지 신청합니다." } ] } ← response HTTP 200 { "batch": "E-1", "total": 10, "valid": 4, "invalid": 6, "with_warnings": 0, "rows": [ { "id": "FAQ-401", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-402", "ok": false, "errors": [ "title_too_long" ], "warnings": [] }, { "id": "FAQ-403", "ok": false, "errors": [ "title_missing" ], "warnings": [] }, { "id": "FAQ-404", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-405", "ok": false, "errors": [ "answer_too_short" ], "warnings": [] }, { "id": "FAQ-406", "ok": false, "errors": [ "pii" ], "warnings": [] }, { "id": "FAQ-407", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-408", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-409", "ok": false, "errors": [ "external_link" ], "warnings": [] }, { "id": "faq-410", "ok": false, "errors": [ "id_format" ], "warnings": [] } ], "codes": { "title_too_long": 1, "title_missing": 1, "answer_too_short": 1, "pii": 1, "external_link": 1, "id_format": 1 } } ✔ 사내 주소는 통과, 사내 주소처럼 보이는 바깥 링크는 막음 · 0.0s → request POST /webhook/my-faq-check { "batch": "E-1", "items": [ { "id": "FAQ-401", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가", "keywords": [ "메신저", "알림" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-402", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가요", "keywords": [ "메신저" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-403", "title": " ", "keywords": [ "사원증" ], "answer": "사원증 사진은 인사 포털에서 바꿉니다." }, { "id": "FAQ-404", "title": "장비 신청", "keywords": [ "장비" ], "answer": "IT 포털에서 신청" }, { "id": "FAQ-405", "title": "장비 반납", "keywords": [ "반납" ], "answer": "IT포털에서 신청" }, { "id": "FAQ-406", "title": "야간 장애 연락", "keywords": [ "야간", "장애" ], "answer": "야간 장애는 당직자 01012345678 로 전화합니다." }, { "id": "FAQ-407", "title": "IT 문의 창구", "keywords": [ "문의", "창구" ], "answer": "IT 문의 창구 번호는 02-123-4567 (평일 9~18시) 입니다." }, { "id": "FAQ-408", "title": "도움말 모음", "keywords": [ "도움말" ], "answer": "도움말은 https://intra.example.com/help 에 모여 있습니다." }, { "id": "FAQ-409", "title": "보안 교육 자료", "keywords": [ "보안 교육" ], "answer": "보안 교육 자료는 http://intra.example.com.evil.io/edu 에서 받습니다." }, { "id": "faq-410", "title": "출장 노트북", "keywords": [ "출장" ], "answer": "출장용 노트북은 IT팀에 사흘 전까지 신청합니다." } ] } ← response HTTP 200 { "batch": "E-1", "total": 10, "valid": 4, "invalid": 6, "with_warnings": 0, "rows": [ { "id": "FAQ-401", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-402", "ok": false, "errors": [ "title_too_long" ], "warnings": [] }, { "id": "FAQ-403", "ok": false, "errors": [ "title_missing" ], "warnings": [] }, { "id": "FAQ-404", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-405", "ok": false, "errors": [ "answer_too_short" ], "warnings": [] }, { "id": "FAQ-406", "ok": false, "errors": [ "pii" ], "warnings": [] }, { "id": "FAQ-407", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-408", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-409", "ok": false, "errors": [ "external_link" ], "warnings": [] }, { "id": "faq-410", "ok": false, "errors": [ "id_format" ], "warnings": [] } ], "codes": { "title_too_long": 1, "title_missing": 1, "answer_too_short": 1, "pii": 1, "external_link": 1, "id_format": 1 } } ✔ 소문자 id · 합계 10줄 중 4줄 통과 · 0.1s → request POST /webhook/my-faq-check { "batch": "E-1", "items": [ { "id": "FAQ-401", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가", "keywords": [ "메신저", "알림" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-402", "title": "사내 메신저 알림이 오지 않을 때 확인할 것과 다시 설정하는 법가가가가가요", "keywords": [ "메신저" ], "answer": "메신저 설정의 '알림' 탭에서 알림을 다시 켭니다." }, { "id": "FAQ-403", "title": " ", "keywords": [ "사원증" ], "answer": "사원증 사진은 인사 포털에서 바꿉니다." }, { "id": "FAQ-404", "title": "장비 신청", "keywords": [ "장비" ], "answer": "IT 포털에서 신청" }, { "id": "FAQ-405", "title": "장비 반납", "keywords": [ "반납" ], "answer": "IT포털에서 신청" }, { "id": "FAQ-406", "title": "야간 장애 연락", "keywords": [ "야간", "장애" ], "answer": "야간 장애는 당직자 01012345678 로 전화합니다." }, { "id": "FAQ-407", "title": "IT 문의 창구", "keywords": [ "문의", "창구" ], "answer": "IT 문의 창구 번호는 02-123-4567 (평일 9~18시) 입니다." }, { "id": "FAQ-408", "title": "도움말 모음", "keywords": [ "도움말" ], "answer": "도움말은 https://intra.example.com/help 에 모여 있습니다." }, { "id": "FAQ-409", "title": "보안 교육 자료", "keywords": [ "보안 교육" ], "answer": "보안 교육 자료는 http://intra.example.com.evil.io/edu 에서 받습니다." }, { "id": "faq-410", "title": "출장 노트북", "keywords": [ "출장" ], "answer": "출장용 노트북은 IT팀에 사흘 전까지 신청합니다." } ] } ← response HTTP 200 { "batch": "E-1", "total": 10, "valid": 4, "invalid": 6, "with_warnings": 0, "rows": [ { "id": "FAQ-401", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-402", "ok": false, "errors": [ "title_too_long" ], "warnings": [] }, { "id": "FAQ-403", "ok": false, "errors": [ "title_missing" ], "warnings": [] }, { "id": "FAQ-404", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-405", "ok": false, "errors": [ "answer_too_short" ], "warnings": [] }, { "id": "FAQ-406", "ok": false, "errors": [ "pii" ], "warnings": [] }, { "id": "FAQ-407", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-408", "ok": true, "errors": [], "warnings": [] }, { "id": "FAQ-409", "ok": false, "errors": [ "external_link" ], "warnings": [] }, { "id": "faq-410", "ok": false, "errors": [ "id_format" ], "warnings": [] } ], "codes": { "title_too_long": 1, "title_missing": 1, "answer_too_short": 1, "pii": 1, "external_link": 1, "id_format": 1 } } Passed 6/6 report: runs/lab01-edges-20261101-180518.json $ uv run tools/flow.py test cases/lab01-edges.json --webhook base-faq-check POST http://localhost:5678/webhook/base-faq-check · 6 case(s) × 1 ✘ 제목 40자는 통과, 41자는 너무 김 · 0.0s · rows.1.ok = true (expected false); rows.1.errors has none of ['title_too_long'] ✘ 공백만 있는 제목은 빈 값 · 0.1s · rows.2.errors has none of ['title_missing'] ✘ 답 10자는 통과, 9자는 너무 짧음 · 0.0s · rows.4.errors has none of ['answer_too_short'] ✘ 하이픈 없는 휴대전화도 개인정보, 사무실 번호는 통과 · 0.1s · rows.5.errors has none of ['pii'] ✘ 사내 주소는 통과, 사내 주소처럼 보이는 바깥 링크는 막음 · 0.0s · rows.8.errors has none of ['external_link'] ✘ 소문자 id · 합계 10줄 중 4줄 통과 · 0.1s · valid = 10 (expected 4); rows.9.errors has none of ['id_format'] Passed 0/6 report: runs/lab01-edges-20261101-180519.json