테스트 도구 실전 · sleep 없이 시간 경계를 재현한다 · 实验
sleep 없이 시간 경계를 재현한다
목표
가짜 시계로 제한 창·재시도 시각·사용자별 격리를 검증합니다.
왜 중요한가
요청 제한 테스트에 sleep을 넣었더니 개발 PC에서는 통과하고 CI에서는 실패했다. 느린 실행기가 시간 경계를 바꾸고 테스트 자체도 오래 걸렸다. 시간은 프로그램의 입력이므로 호출자가 제어할 수 있게 만들고 경계를 정확히 밟아야 한다.
단계
1. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: validate_limit(limit, window)는 bool 제외 양의 int limit와 양의 유한 int/float window만 허용해 (limit, float(window))를 반환합니다. 나머지는 ValueError입니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/test-clock-window-labtest -e /root/work/test-clock-window-lab/service.py || cp /opt/fixtures/ten_labs/test-clock-window-lab/service.py /root/work/test-clock-window-lab/service.pytest -e /root/work/test-clock-window-lab/test_service.py || cp /opt/fixtures/ten_labs/test-clock-window-lab/test_service.py /root/work/test-clock-window-lab/test_service.pycd /root/work/test-clock-window-lab2. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: active(history, now, window)는 now-window보다 큰 시각만 원래 순서의 새 리스트로 반환합니다. history는 정렬된 비감소 시각입니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
3. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: retry_after(history, now, window)는 이미 정리된 비어 있지 않은 history의 첫 시각+window-now를 ceil한 값과 0 중 큰 정수입니다. 빈 리스트는 0입니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
4. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: history_for(state, key)는 없는 키면 빈 리스트, 있으면 그 기록의 사본을 반환합니다. 조회만으로 state를 수정하지 않습니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
5. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: admit(state, key, now, limit, window)는 설정 검증 후 해당 키의 만료 기록을 정리합니다. 여유가 있으면 now를 추가하고 (True,0), 꽉 찼으면 추가하지 않고 (False,retry_after)를 반환합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
6. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: client_key(value)는 1~40자 ASCII 영문·숫자·하이픈 문자열을 그대로 반환하고 나머지는 ValueError입니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
7. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: limited_response(wait)는 상태 429, 본문 {error:'rate_limited'}, Retry-After 헤더는 wait를 문자열로 만든 JSONResponse입니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
8. /root/work/test-clock-window-lab/test_service.py에서 제공된 service.py의 다음 공개 계약을 테스트하세요: create_app(clock, limit=2, window=10)는 GET /work에서 X-Client-ID를 검사해 잘못된 키는 400 {error:'invalid_client'}, 허용은 200 {ok:True}, 초과는 limited_response입니다. state는 앱별로 분리합니다. 정상 구현에서는 통과하고 이 계약을 어긴 구현에서는 실제 테스트 본문의 실패로 검출해야 합니다. 앞 단계 테스트를 유지하며 test_ 함수를 추가하세요.
참고
- 인터넷과 패키지 설치 없이 기존 lab-dev 환경에서 수행합니다.
- 각 단계는 45초 채점 예산 안에서 실행됩니다. 실제 sleep이나 네트워크 호출을 추가하지 마세요.
- 제출 테스트는 별도 임시 폴더에서 정상·결함 구현에 실행합니다. 정상에서는 실제 실행한 테스트가 모두 통과하고 결함에서는 테스트 본문이 실패해야 합니다. 수집 오류, 실행 0개, 전부 건너뜀, 강제 종료는 통과가 아닙니다. pytest 기본 기능과 제공 라이브러리만 사용하세요.
- [FastAPI 공식 문서](https://fastapi.tiangolo.com/) · [pytest 공식 문서](https://docs.pytest.org/en/stable/) · [Python sqlite3](https://docs.python.org/3/library/sqlite3.html)
- 한계: 프로세스 메모리에 있는 단일 워커용 예제다. 여러 파드가 공유하는 전역 한도나 악의적인 클라이언트의 신원을 보장하지 않는다. X-Client-ID는 테스트용 키이므로 운영에서는 인증된 주체에서 키를 얻어야 한다. 지속적인 시계 역행은 단조 시계 사용으로 피해야 하며 이 실습의 clock은 비감소한다. 제공 구현은 읽어도 되지만 채점은 별도 사본을 사용한다. 소스 문구 검사나 파일 수정으로 결함을 우회하지 말고 공개 인터페이스의 실행 결과를 검사한다.
8个步骤
- 설정을 검증한다 — 테스트
- 창의 왼쪽 경계를 제외한다 — 테스트
- 대기 시간을 올림한다 — 테스트
- 키마다 기록을 나눈다 — 테스트
- 허용 요청만 기록한다 — 테스트
- 클라이언트 키를 검증한다 — 테스트
- 거절 응답을 만든다 — 테스트
- 가상 시간으로 요청 흐름을 끝낸다 — 테스트